none
What is "best practise" for databinding custom controls with xmlFile from xaml ? RRS feed

  • Question

  • Hello..
    I´m having a hard time figuring out how to databind a custom control from xaml with an XmlDocument. My problem is how to generate the ItemCollection.

    Example, myListBox control containing a List of <myListBoxItem>. In codebehind it is very easy to create a ...
    List<myListBoxItem> myItems = new List<myListBoxItem>();
    ...and then use...
    myItems.add(..); 

    What I´m looking for is a very simple example where this is done so after the databinding has been made in xaml (with xml)  I have an customized ItemCollection (in this case myListBoxItem) that I can control, add events, animate or whatever I wanna do from my parentContainer (myListBox)

    Hope I made myself clear, otherwise I´ll try to clarify myself..
    /Erik
    Monday, September 25, 2006 5:15 PM

Answers

  • Hi Erik,

    Yes, I think I understand what you want to do. You want MyListBoxItems (and NOT ListBoxItems) to be generated when you bind your MyItemsControl to a collection, right? That can be done very easily by simply overriding the GetContainerForItemOverride method from ItemsControl from within the custom control, and returning a MyListBoxItem:

        public class MyItemsControl : ItemsControl
        {
            protected override DependencyObject GetContainerForItemOverride()
            {
                return new MyListBoxItem();
            }
        }

        public class MyListBoxItem : ListBoxItem
        {
            public MyListBoxItem()
            {
                this.Background = Brushes.PapayaWhip;
            }
        }

    If you bind MyItemsControl's ItemsSource to a collection, MyListBoxItems will be generated and will wrap each of the data items. You can verify this with the following simple xaml:

      <Window.Resources>
        <XmlDataProvider XPath="Items/Item" x:Key="xdp">
          <x:XData>
            <Items xmlns="">
              <Item>A</Item>
              <Item>B</Item>
              <Item>C</Item>
              <Item>D</Item>
            </Items>
          </x:XData>
        </XmlDataProvider>
      </Window.Resources>
      <StackPanel Margin="20">
        <local:MyItemsControl ItemsSource="{Binding Source={StaticResource xdp}}" x:Name="ic"/>
      </StackPanel>

    If you run this application, the items will have orange (sorry, "PapayaWhip") background. I uploaded a project with this code/markup here: http://www.beacosta.com/Forum/CustomLBI.zip

    Let me know if this is what you wanted.

    Thanks,

    Bea

    Tuesday, September 26, 2006 6:03 AM
    Moderator
  • Hi Erik,

    Unlike WinForms, when you get ItemsIdea, you will always get the data item, and not the UIElement that wraps it. Even though you get an XmlElement back, you can trust that we wrap it with a MyListBoxItem before we add it to the ListBox - otherwise, the background of the items would not be orange.

    We decided to return the data item because we've seen that this is what users typically want. However, if in your particular case you really need a handle to the MyListBoxItem, you can get it by using the ItemContainerGenerator. I blogged about this here: http://www.beacosta.com/Archive/2005_09_01_bcosta_archive.html, on September 21 and 27.

    About your solution of making a collection of MyListBoxItem, the platform definetely supports it but I don't recommend it. We should always strive for the most separation of UI and data, and you are mixing these two concepts in the same file. For example, imagine in the future some other UI technology is invented, and you want to port your Avalon application to that technology. It will save you a lot of time if you design your data sources to be completely independent from Avalon today. I'm sure that people that are now porting their apps from WinForms to Avalon feel this problem first hand.

    Thanks,

    Bea

     

    Tuesday, September 26, 2006 5:23 PM
    Moderator

All replies

  • Hi Erik,

    Yes, I think I understand what you want to do. You want MyListBoxItems (and NOT ListBoxItems) to be generated when you bind your MyItemsControl to a collection, right? That can be done very easily by simply overriding the GetContainerForItemOverride method from ItemsControl from within the custom control, and returning a MyListBoxItem:

        public class MyItemsControl : ItemsControl
        {
            protected override DependencyObject GetContainerForItemOverride()
            {
                return new MyListBoxItem();
            }
        }

        public class MyListBoxItem : ListBoxItem
        {
            public MyListBoxItem()
            {
                this.Background = Brushes.PapayaWhip;
            }
        }

    If you bind MyItemsControl's ItemsSource to a collection, MyListBoxItems will be generated and will wrap each of the data items. You can verify this with the following simple xaml:

      <Window.Resources>
        <XmlDataProvider XPath="Items/Item" x:Key="xdp">
          <x:XData>
            <Items xmlns="">
              <Item>A</Item>
              <Item>B</Item>
              <Item>C</Item>
              <Item>D</Item>
            </Items>
          </x:XData>
        </XmlDataProvider>
      </Window.Resources>
      <StackPanel Margin="20">
        <local:MyItemsControl ItemsSource="{Binding Source={StaticResource xdp}}" x:Name="ic"/>
      </StackPanel>

    If you run this application, the items will have orange (sorry, "PapayaWhip") background. I uploaded a project with this code/markup here: http://www.beacosta.com/Forum/CustomLBI.zip

    Let me know if this is what you wanted.

    Thanks,

    Bea

    Tuesday, September 26, 2006 6:03 AM
    Moderator
  • Thanks Bea, that is what I want When i debug the code I see that...

    protected override DependencyObject GetContainerForItemOverride()
    {
    return new MyListBoxItem();
    }

    ...is doing it´s job to generate MyListBoxItem object for each Item in the Xml. However when I check
    MyItemsControl collection (which contains 4 Items) the Items are not of type MyListBoxItem, but XmlElement..

    foreach (MyListBoxItem mylbItem in this.Items)
    {
    // Here i want to some stuff with my List<MyListBoxItem>
    // that i want to be generatade via the xml databinding in xaml

    // The Exception that is thrown
    // Unable to cast object of type 'System.Xml.XmlElement' to type
    //local.MyListBoxItem'.
    }

    Lovely PapapWhip background by the way
    Tuesday, September 26, 2006 8:48 AM
  • Solved it by create a List of MyListBoxItem... well it ain´t the best way cause now data is stored in 2 places.

           // member objects
           private List<MyListBoxItem> _lbItems = new List<MyListBoxItem>();

            protected override DependencyObject GetContainerForItemOverride()
            {
                MyListBoxItem item = new MyListBoxItem();
                _lbItems.Add(item);

                return item;
            }


                    for (int i = 0; i < _lbItems.Count-1; i++)
                    {
                        MyListBoxItem item = _lbItemsIdea;

                       //

                   }



    Tuesday, September 26, 2006 11:34 AM
  • Hi Erik,

    Unlike WinForms, when you get ItemsIdea, you will always get the data item, and not the UIElement that wraps it. Even though you get an XmlElement back, you can trust that we wrap it with a MyListBoxItem before we add it to the ListBox - otherwise, the background of the items would not be orange.

    We decided to return the data item because we've seen that this is what users typically want. However, if in your particular case you really need a handle to the MyListBoxItem, you can get it by using the ItemContainerGenerator. I blogged about this here: http://www.beacosta.com/Archive/2005_09_01_bcosta_archive.html, on September 21 and 27.

    About your solution of making a collection of MyListBoxItem, the platform definetely supports it but I don't recommend it. We should always strive for the most separation of UI and data, and you are mixing these two concepts in the same file. For example, imagine in the future some other UI technology is invented, and you want to port your Avalon application to that technology. It will save you a lot of time if you design your data sources to be completely independent from Avalon today. I'm sure that people that are now porting their apps from WinForms to Avalon feel this problem first hand.

    Thanks,

    Bea

     

    Tuesday, September 26, 2006 5:23 PM
    Moderator
  • aaahhh.. I see. Thanks for you feedback Bea, it´s been very usefeul for me!
    Have a nice day
    /erik
    Tuesday, September 26, 2006 5:42 PM