locked
How to add some control into existing ItemTemplate (ListBox) RRS feed

  • Question

  • Hello!

    This is my first post here :) and I really hope that you can help me with an answer...

    So I would like to ask you to help me in one issue - my problem is: I have custom UserControl with ListBox on it (e.g. named MyListBox) and I want to reuse it on some other UserControls. I would like to assign ItemTemplate of internal ListBox from outer UserControl (when MyListBox resides on some other UserControl) - so I have dependency property ItemTemplate (type of property is DataTemplate) in my MyListBox control.

    As I want to process ListBox item creation I have some predefined ItemTemplate in the internal ListBox:

    <ListBox.ItemTemplate>
    <DataTemplate >
    <Canvas Loaded="PanelLoaded" MinHeight="10">
    </Canvas>
    </DataTemplate>
    </ListBox.ItemTemplate>
    
     As you can see here - there is (in MyListBox class) PanelLoaded method handles Loaded event of Canvas - this event/method fires on every ListBox's row creation (it seems like some kind of hack but ListBox doesn't provide an easy way to process row creation). That's why I can't change the ListBox Item template property according with assigned in outer UserControl ItemTemplate data - it will replace 'Loaded="PanelLoaded"' and I'll lose row creation event - I somehow need to copy controls from outer ItemTemplate into internal Canvas that resides in the ListBox ItemTemplate property... I know that I can create UIElement based on ItemTemplate - lb.ItemTemplate.LoadContent() as Panel but I don't know how to copy children into current ItemTemplate...

    Another questions that can help me in this issue:

    1. May be there is a way exists how to create DataTemplate class from FrameworkElement/UIElement?
    2. How can I get XAML from existing FrameworkElement/UIElement? It's allows my to user this: (DataTemplate)XamlReader.Load(xaml)

    Thank you in advance!!

    Thursday, August 13, 2009 12:05 PM

Answers

  • Hi, Maxim. Welcome to the forum!

    You can set ItemTemplate in the class inherited from ListBox by overriding PrepareContainerForItemOverride method. I've prepared a sample class for you. Try this:

    public class ListBoxEx : ListBox
    {
        public DataTemplate ContentTemplate
        {
            get { return (DataTemplate)GetValue(ContainerProperty); }
            set { SetValue(ContainerProperty, value); }
        }
    
        public static readonly DependencyProperty ContainerProperty = DependencyProperty.Register("ContentTemplate", typeof(DataTemplate), typeof(ListBoxEx), null);
    
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
            var listBoxItem = element as ListBoxItem;
            if (ContentTemplate != null) listBoxItem.ContentTemplate = ContentTemplate;
            ((FrameworkElement) element).Loaded += (sender, e) =>
                                                       {
                                                           // add your code here
                                                       };
        }
    }

     Usage

    Xaml:

    <Grid x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded">
        <Grid.Resources>
            <DataTemplate x:Key="ListBoxExDataTemplate">
                <Border Background="Red" Height="50">
                    <TextBlock Text="{Binding}"/>
                </Border>
            </DataTemplate>
        </Grid.Resources>
        
        <DemoApp:ListBoxEx x:Name="listBox" ContentTemplate="{StaticResource ListBoxExDataTemplate}"/>
    </Grid>

    Code-behind:

    public partial class Page
    {
        public Page()
        {
            InitializeComponent();
        }
    
        private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
        {
            var items = "Lorem ipsum dolor sit amet, consectetur adipiscing elit".Split();
            listBox.ItemsSource = items;
        }
    }
     
    Friday, August 14, 2009 8:16 AM

All replies

  • Hi, Maxim. Welcome to the forum!

    You can set ItemTemplate in the class inherited from ListBox by overriding PrepareContainerForItemOverride method. I've prepared a sample class for you. Try this:

    public class ListBoxEx : ListBox
    {
        public DataTemplate ContentTemplate
        {
            get { return (DataTemplate)GetValue(ContainerProperty); }
            set { SetValue(ContainerProperty, value); }
        }
    
        public static readonly DependencyProperty ContainerProperty = DependencyProperty.Register("ContentTemplate", typeof(DataTemplate), typeof(ListBoxEx), null);
    
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
            var listBoxItem = element as ListBoxItem;
            if (ContentTemplate != null) listBoxItem.ContentTemplate = ContentTemplate;
            ((FrameworkElement) element).Loaded += (sender, e) =>
                                                       {
                                                           // add your code here
                                                       };
        }
    }

     Usage

    Xaml:

    <Grid x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded">
        <Grid.Resources>
            <DataTemplate x:Key="ListBoxExDataTemplate">
                <Border Background="Red" Height="50">
                    <TextBlock Text="{Binding}"/>
                </Border>
            </DataTemplate>
        </Grid.Resources>
        
        <DemoApp:ListBoxEx x:Name="listBox" ContentTemplate="{StaticResource ListBoxExDataTemplate}"/>
    </Grid>

    Code-behind:

    public partial class Page
    {
        public Page()
        {
            InitializeComponent();
        }
    
        private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
        {
            var items = "Lorem ipsum dolor sit amet, consectetur adipiscing elit".Split();
            listBox.ItemsSource = items;
        }
    }
     
    Friday, August 14, 2009 8:16 AM
  • Thanks a lot!!
    Monday, August 17, 2009 5:12 AM
  • I was so excited when I saw this post because I have been fighting something like this for days. But, my joy quickly turned to dispair when I saw

    Loaded="LayoutRoot_Loaded"

     in the Grid definition. This is not allowed in a custom control (Generic.xaml) definition. SL crashes with a markup exception. Any other thoughts on doing this in a custom control?

    Tuesday, December 7, 2010 11:52 AM