locked
How to best solve the following binding scenario? RRS feed

  • Question

  • Hello!

    Lets say we have 5 different Classes (Class A, B, C, D, E) and we have some kind of ViewClass which holds a collection of all those classes items per type (CollA, CollB, CollC...). Then we have a combobox containing a dropdown item for each Class Type. If we select an item in the combo box we populate a listbox with the corresponding collection of the item type - so if we select Type A in the combobox, the listbox shows the CollA items... now depending on the item type (class type) we have different fields/attribute values we want to show in another window. So each type has different fields and thus different elements to show in this window. Is there any easy or common way to handle this situation with the usual xaml bindings?

    What would be the best way to achive this?

    Thanks alot,
    Oliver

    Wednesday, November 3, 2010 8:14 PM

Answers

  • OK, well, I'll show you the code, as you'll still need to adapt this to your specific scenario, but basically, we'll create a List of all the Type's that you want to show in your ComboBox, and bind the selection. When that selection changes, we'll filter the list of all ClassView's accordingly, and the result is shown in the ListBox. The ListBox's selection is bound to the selected ClassView, which is shown via a ContentPresent via DataTemplate, which contains an ItemsControl (just a bit handier than a stackpanel in this case), which shows the properties of each class. 

     

    XAML

     

    <Window.Resources>

        <DataTemplate DataType="{x:Type local:ClassViewA}">

            <ItemsControl ItemsSource="{Binding PropertyNames}"></ItemsControl>

        </DataTemplate>

     

        <DataTemplate DataType="{x:Type local:ClassViewB}">

            <ItemsControl ItemsSource="{Binding PropertyNames}"></ItemsControl>

        </DataTemplate>

     

        <DataTemplate DataType="{x:Type local:ClassViewC}">

            <ItemsControl ItemsSource="{Binding PropertyNames}"></ItemsControl>

        </DataTemplate>

    </Window.Resources>

     

    <Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto"></RowDefinition>

            <RowDefinition Height="50"></RowDefinition>

            <RowDefinition Height="*"></RowDefinition>

     

        </Grid.RowDefinitions>

        <ComboBox Grid.Row="0" ItemsSource="{Binding AllClassViewTypes}" SelectedValue="{Binding SelectedClassViewType}" DisplayMemberPath="Name"></ComboBox>

        <ListBox Grid.Row="1" ItemsSource="{Binding FilteredClassViews}" SelectedValue="{Binding SelectedClassView}" DisplayMemberPath="FriendlyName"></ListBox>

     

        <ContentPresenter Grid.Row="2" Content="{Binding SelectedClassView}"></ContentPresenter>

    </Grid>


    C#

        public partial class Window1 : Window, INotifyPropertyChanged
        {
            ClassView _selectedClassView;
            Type _selectedClassViewType;
            List<ClassView> _allClassViews;
            List<Type> _allClassViewTypes;
            List<ClassView> _filteredClassViews;

            public List<Type> AllClassViewTypes
            {
                get { return _allClassViewTypes; }
                set { _allClassViewTypes = value; }
            }

            public Type SelectedClassViewType
            {
                get { return _selectedClassViewType; }
                set { 
                    _selectedClassViewType = value;

                    FilteredClassViews = _allClassViews.Where(x => x.GetType() == _selectedClassViewType).ToList();
                }
            }

            public ClassView SelectedClassView
            {
                get { return _selectedClassView; }
                set
                {
                    _selectedClassView = value;

                    OnPropertyChanged("SelectedClassView");
                }
            }        

            public List<ClassView> FilteredClassViews
            {
                get { return _filteredClassViews; }
                set
                {
                    _filteredClassViews = value;

                    OnPropertyChanged("FilteredClassViews");
                }
            }        

            public Window1()
            {
                InitializeComponent();
                
                _allClassViewTypes = new List<Type>()
                {
                    typeof(ClassViewA),
                    typeof(ClassViewB),
                    typeof(ClassViewC),
                };            

                _allClassViews = new List<ClassView>() 
                  { 
                    new ClassViewA("Class View A 1"), 
                    new ClassViewB("Class View B 1"), 
                    new ClassViewC("Class View C 1"), 
                    new ClassViewB("Class View B 2"), 
                    new ClassViewC("Class View C 2"), 
                    new ClassViewA("Class View A 2"), 
                    new ClassViewC("Class View C 3"), 
                  };

                this.DataContext = this;
            }

            #region INotifyPropertyChanged Members

            public event PropertyChangedEventHandler PropertyChanged;

            public void OnPropertyChanged(string name)
            {
                if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name));
            }

            #endregion
        }

        public abstract class ClassView
        {
            public string FriendlyName { get; set; }
            public List<string> PropertyNames { get; set; }

            public ClassView(string friendlyName)
            {
                FriendlyName = friendlyName;
            }
        }

        public class ClassViewA : ClassView
        {
            public ClassViewA(string friendlyName)
                : base(friendlyName)
            {
                PropertyNames = new List<string>()
                {
                    "ClassViewA Property 1",
                    "ClassViewA Property 2",
                    "ClassViewA Property 3",
                };
            }
        }

        public class ClassViewB : ClassView
        {
            public ClassViewB(string friendlyName)
                : base(friendlyName)
            {
                PropertyNames = new List<string>()
                {
                    "ClassViewB Property 1",
                    "ClassViewB Property 2",                
                };

            }
        }

        public class ClassViewC : ClassView
        {
            public ClassViewC(string friendlyName)
                : base(friendlyName)
            {
                PropertyNames = new List<string>()
                {
                    "ClassViewC Property 1",
                    "ClassViewC Property 2",                
                    "ClassViewC Property 3",                
                    "ClassViewC Property 4",                
                };

            }
        }


    Warm regards,
    Matt

    • Marked as answer by Oliver Greil Monday, November 8, 2010 7:31 AM
    Monday, November 8, 2010 1:45 AM

All replies

  • You could use DataTemplates and the DataType property.

     

    Something like this...

     

    <DataTemplate DataType="{x:Type local:TypeA}">

        <TextBlock Text="{Binding Path=TypeA.PropertyA}" Foreground="Red"/>

    </DataTemplate>

     

    <DataTemplate DataType="{x:Type local:TypeB}">

        <TextBlock Text="{Binding Path=TypeB.PropertyB}" Foreground="Green"/>

    </DataTemplate>

     

     

    Here is a msdn page that may help.


    Matt Hohn
    • Proposed as answer by Min Zhu Friday, November 5, 2010 7:36 AM
    Wednesday, November 3, 2010 8:37 PM
  • Hmm that seems like a nice solution!

    However at the moment I have handled that situation completly different, and I guess I handled it more winforms like.

    I am using 5 different listboxes, which I bind to the collections. The listboxes are also bound to their corresponding property windows. Depending on the combobox item selected I am switching between them (an their corresponding property windows) via the collapsed/visibility attribute. This also gives me the possibility to easily design different property windows for each class type in blend (especially since they all have pretty different layouts).

    Is this an approach that is absolutely depreciated in WPF or is it something I can do without having to worry about ?

    Thanks alot for your time,
    Oliver

    Wednesday, November 3, 2010 9:23 PM
  • Mm.. I don't know about deprecated, but DataTemplating like Matt Hohn suggested is a very clean way of doing it. Take this for example...

     

    XAML

    <Window.Resources>
        <DataTemplate DataType="{x:Type local:ClassViewA}">
          <TextBlock Text="What Class A looks like..."></TextBlock>
          <!-- Note : This could just as easily be a UserControl called ClassViewAView.-->
          <!-- <local:ClassViewAView></local:ClassViewAView> -->
        </DataTemplate>
        
        <DataTemplate DataType="{x:Type local:ClassViewB}">
          <TextBlock Text="What Class B looks like..."></TextBlock>
        </DataTemplate>
        
        <DataTemplate DataType="{x:Type local:ClassViewC}">
          <TextBlock Text="What Class C looks like..."></TextBlock>
        </DataTemplate>
      </Window.Resources>
          
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto"></RowDefinition>
          <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <ComboBox Grid.Row="0" ItemsSource="{Binding AllClassViews}" SelectedValue="{Binding SelectedClassView}" DisplayMemberPath="FriendlyName"></ComboBox>
        <ContentPresenter Grid.Row="1" Content="{Binding SelectedClassView}"></ContentPresenter>
      </Grid>
    
    

    C#

     public partial class Window1 : Window, INotifyPropertyChanged
      {
        ClassView _selectedClassView;
    
        public ClassView SelectedClassView
        {
          get { return _selectedClassView; }
          set { _selectedClassView = value;
    
          OnPropertyChanged("SelectedClassView");
          }
        }
    
        public List<ClassView> AllClassViews { get; set; }    
    
        public Window1()
        {
          InitializeComponent();
    
          AllClassViews = new List<ClassView>() 
          { 
            new ClassViewA("Class View A"), 
            new ClassViewB("Class View B"), 
            new ClassViewC("Class View C"), 
          };
    
          this.DataContext = this;
        }
    
        #region INotifyPropertyChanged Members
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        public void OnPropertyChanged(string name)
        {
          if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    
        #endregion
      }
    
      public abstract class ClassView
      {
        public string FriendlyName { get; set; }
    
        public ClassView(string friendlyName)
        {
          FriendlyName = friendlyName;
        }
      }
    
      public class ClassViewA : ClassView
      {
        public ClassViewA(string friendlyName)
          : base(friendlyName)
        {
        
        }
      }
      
      public class ClassViewB : ClassView
      {
        public ClassViewB(string friendlyName)
          : base(friendlyName)
        {
        
        }
      }
    
      public class ClassViewC : ClassView
      {
         public ClassViewC(string friendlyName)
          : base(friendlyName)
        {
        
        }
      }
    

    Warm regards,

    Matt

    • Proposed as answer by Min Zhu Friday, November 5, 2010 7:36 AM
    Thursday, November 4, 2010 5:01 AM
  • Hello and thanks alot for the example. However I tried now to extend this example to my initial problem (described in my first post) but I am still not totally clear on how to solve this. It would be much appreciated if you could explain me on how to basically extend this example to get the binding scenario described in my initial post -> like this:

    -> Combobox (there we select the ClassType - ClassViewA, ClassViewB...)
    -> ListBox ( there we show the list of all items of the selected Type from the combobox)
    -> StackPanel (there we list all the properties of the selected ListBox item).

    Thanks a lot for taking your time,
    Oliver

    Friday, November 5, 2010 8:12 AM
  • OK, well, I'll show you the code, as you'll still need to adapt this to your specific scenario, but basically, we'll create a List of all the Type's that you want to show in your ComboBox, and bind the selection. When that selection changes, we'll filter the list of all ClassView's accordingly, and the result is shown in the ListBox. The ListBox's selection is bound to the selected ClassView, which is shown via a ContentPresent via DataTemplate, which contains an ItemsControl (just a bit handier than a stackpanel in this case), which shows the properties of each class. 

     

    XAML

     

    <Window.Resources>

        <DataTemplate DataType="{x:Type local:ClassViewA}">

            <ItemsControl ItemsSource="{Binding PropertyNames}"></ItemsControl>

        </DataTemplate>

     

        <DataTemplate DataType="{x:Type local:ClassViewB}">

            <ItemsControl ItemsSource="{Binding PropertyNames}"></ItemsControl>

        </DataTemplate>

     

        <DataTemplate DataType="{x:Type local:ClassViewC}">

            <ItemsControl ItemsSource="{Binding PropertyNames}"></ItemsControl>

        </DataTemplate>

    </Window.Resources>

     

    <Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto"></RowDefinition>

            <RowDefinition Height="50"></RowDefinition>

            <RowDefinition Height="*"></RowDefinition>

     

        </Grid.RowDefinitions>

        <ComboBox Grid.Row="0" ItemsSource="{Binding AllClassViewTypes}" SelectedValue="{Binding SelectedClassViewType}" DisplayMemberPath="Name"></ComboBox>

        <ListBox Grid.Row="1" ItemsSource="{Binding FilteredClassViews}" SelectedValue="{Binding SelectedClassView}" DisplayMemberPath="FriendlyName"></ListBox>

     

        <ContentPresenter Grid.Row="2" Content="{Binding SelectedClassView}"></ContentPresenter>

    </Grid>


    C#

        public partial class Window1 : Window, INotifyPropertyChanged
        {
            ClassView _selectedClassView;
            Type _selectedClassViewType;
            List<ClassView> _allClassViews;
            List<Type> _allClassViewTypes;
            List<ClassView> _filteredClassViews;

            public List<Type> AllClassViewTypes
            {
                get { return _allClassViewTypes; }
                set { _allClassViewTypes = value; }
            }

            public Type SelectedClassViewType
            {
                get { return _selectedClassViewType; }
                set { 
                    _selectedClassViewType = value;

                    FilteredClassViews = _allClassViews.Where(x => x.GetType() == _selectedClassViewType).ToList();
                }
            }

            public ClassView SelectedClassView
            {
                get { return _selectedClassView; }
                set
                {
                    _selectedClassView = value;

                    OnPropertyChanged("SelectedClassView");
                }
            }        

            public List<ClassView> FilteredClassViews
            {
                get { return _filteredClassViews; }
                set
                {
                    _filteredClassViews = value;

                    OnPropertyChanged("FilteredClassViews");
                }
            }        

            public Window1()
            {
                InitializeComponent();
                
                _allClassViewTypes = new List<Type>()
                {
                    typeof(ClassViewA),
                    typeof(ClassViewB),
                    typeof(ClassViewC),
                };            

                _allClassViews = new List<ClassView>() 
                  { 
                    new ClassViewA("Class View A 1"), 
                    new ClassViewB("Class View B 1"), 
                    new ClassViewC("Class View C 1"), 
                    new ClassViewB("Class View B 2"), 
                    new ClassViewC("Class View C 2"), 
                    new ClassViewA("Class View A 2"), 
                    new ClassViewC("Class View C 3"), 
                  };

                this.DataContext = this;
            }

            #region INotifyPropertyChanged Members

            public event PropertyChangedEventHandler PropertyChanged;

            public void OnPropertyChanged(string name)
            {
                if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name));
            }

            #endregion
        }

        public abstract class ClassView
        {
            public string FriendlyName { get; set; }
            public List<string> PropertyNames { get; set; }

            public ClassView(string friendlyName)
            {
                FriendlyName = friendlyName;
            }
        }

        public class ClassViewA : ClassView
        {
            public ClassViewA(string friendlyName)
                : base(friendlyName)
            {
                PropertyNames = new List<string>()
                {
                    "ClassViewA Property 1",
                    "ClassViewA Property 2",
                    "ClassViewA Property 3",
                };
            }
        }

        public class ClassViewB : ClassView
        {
            public ClassViewB(string friendlyName)
                : base(friendlyName)
            {
                PropertyNames = new List<string>()
                {
                    "ClassViewB Property 1",
                    "ClassViewB Property 2",                
                };

            }
        }

        public class ClassViewC : ClassView
        {
            public ClassViewC(string friendlyName)
                : base(friendlyName)
            {
                PropertyNames = new List<string>()
                {
                    "ClassViewC Property 1",
                    "ClassViewC Property 2",                
                    "ClassViewC Property 3",                
                    "ClassViewC Property 4",                
                };

            }
        }


    Warm regards,
    Matt

    • Marked as answer by Oliver Greil Monday, November 8, 2010 7:31 AM
    Monday, November 8, 2010 1:45 AM
  • Thanks alot that works perfect :)

    Kind regards,
    Oliver

    Monday, November 8, 2010 7:33 AM