Search code examples
c#wpfxamldatagriddatagridcomboboxcolumn

How to bind two dependant ComboBoxes in a DataGrid


I've a problem with the WPF datagrid. I have two ComboBox columns and the second one should set it's datacontext dependant of what is selected in the first one.

ViewModel

public class MyCollection: AbstractViewModel
{
    private BindingList<string> _subitems;
    public BindingList<string> Subitems
    {
        get { return _subitems; }
        set { _subitems = value; Notify("Subitems");}
    }

    private string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; Notify("Name");}
    }
}

Xaml

<DataGridTemplateColumn>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding SelectedName}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox SelectedValuePath="Name"
                      x:Name="CollectionBox"
                      SelectedValue="{Binding SelectedName}"
                      DisplayMemberPath="Name"
                      ItemsSource="{Binding SomeBinding}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn >
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding SelectedSubitem}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox SelectedValue="{Binding SelectedSubitem}"
                      ItemsSource="{Binding ElementName=CollectionBox, Path=SelectedItem.Subitems}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

The binding for my first ComboBox is ok but I did not see any items in my second ComboBox (the viewmodel has entries for booth of the ComboBoxes); only the TextBox binding works as expected. I tryed it with a ComboBoxColumn as well but with the same result.


Solution

  • here an simple working example how you could use your ViewModel to achieve your needs

    XAML

       <DataGrid ItemsSource="{Binding Source}" Width="500">
            <DataGrid.Columns>
            <DataGridTemplateColumn Header="main" Width="100">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding SelectedName}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
                    <DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <ComboBox SelectedValuePath="Name"                      
                                      SelectedValue="{Binding SelectedName}"                                  
                                      SelectedItem="{Binding SelectedMainItem, UpdateSourceTrigger=PropertyChanged}"                      
                                      DisplayMemberPath="Name"                      
                                      ItemsSource="{Binding SomeBinding}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellEditingTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="sub" Width="100">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding SelectedSubitem}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                    <DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <ComboBox SelectedValue="{Binding SelectedSubitem}"                      
                                      ItemsSource="{Binding  Path=SelectedMainItem.Subitems}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellEditingTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    

    code behind

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new VMColl();
        }
    }
    
    public class VMColl
    {
        List<VM> source;
    
        public List<VM> Source
        {
            get { return source; }
            set { source = value; }
        }
    
        public VMColl()
        {
            source  = new List<VM>(){new VM(),new VM(),new VM(),new VM()};
        }
    }
    
    /// <summary>
    /// your Row
    /// </summary>
    public class VM : Notify
    {
        private List<mainObj> _items;
        public List<mainObj> SomeBinding
        {
            get { return _items; }
            set
            {
                _items = value;
                OnPropertyChanged("Items");
            }
        }
    
        private string _selectedName;
        public string SelectedName
        {
            get { return _selectedName; }
            set
            {
                _selectedName = value;
                OnPropertyChanged("SelectedName");
            }
        }
    
        private mainObj _selectedMainItem;
        public mainObj SelectedMainItem
        {
            get { return _selectedMainItem; }
            set
            {
                _selectedMainItem = value;
                OnPropertyChanged("SelectedMainItem");
                OnPropertyChanged("SelectedMainItem.Subitems");
            }
        }
    
        private string _selectedSubitem;
        public string SelectedSubitem
        {
            get { return _selectedSubitem; }
            set
            {
                _selectedSubitem = value;
                OnPropertyChanged("SelectedSubitem");
            }
        }
    
        public VM()
        {
            SomeBinding = new List<mainObj>() {new mainObj("first"),new mainObj("second"),new mainObj("someother") };
        }
    }
    
    public class mainObj : Notify
    {
        private BindingList<string> _subitems;
        public BindingList<string> Subitems
        {
            get { return _subitems; }
            set
            {
                _subitems = value;
                OnPropertyChanged("Subitems");
            }
        }
    
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }
    
        public mainObj(string name)
        {
            _name = name;
            _subitems = new BindingList<string>(){"1","2","3"};
        }
    }
    
    public class Notify : INotifyPropertyChanged
    {
        // Declare the event 
        public event PropertyChangedEventHandler PropertyChanged;
    
        // Create the OnPropertyChanged method to raise the event 
        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }