Search code examples
wpfvalidationdata-bindingdatagriddatagridcomboboxcolumn

Populate ComboBox of a DataGrid with values from another column


I am an experienced user of Winforms and I am now playing with WPF, but I am struggling a bit. I have a datagrid with 2 columns. The first column is a DataGridTextColumn which is bound to the "Name" property of my object. The second column is bound to the "Power" property of my object.

When the user edits the Power column, I would like to display a combo box that lists all the Name of the first column in addition of "None" as first item. How can I do that?

In addition, if the user updates any Name of the first column, I would like the change to be reflected in the Power column. Is it possible?

In code behind:

public partial class MainWindow : Window
{
    ObservableCollection<MyObject> objects = new ObservableCollection<MyObject>();

    public MainWindow()
    {
        InitializeComponent();
        dgObjects.ItemsSource = objects;
    }
}

public class MyObject
{   
    public String Name { get; set; }
    public String Power { get; set; }
}

In Xaml:

<DataGrid Name="dgObjects" AutoGenerateColumns="False">
   <DataGrid.Columns>
        <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
        <DataGridComboBoxColumn Header="Power" Binding="????"/>
    </DataGrid.Columns>
</DataGrid>

Thanks


Solution

  • You can do this via binding on the DataContext. Though, first you need to set up the Window's DataContext properly. ItemsSource should not be done via code-behind (this is WPF, use binding!).

    Set up your class structure like:

      public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                DataContext = new ViewModel();
            }
        }
    
        public class ViewModel
        {
            public ObservableCollection<MyObject> Objects { get; } = new ObservableCollection<MyObject>();
    
            public ViewModel()
            {
                Objects.Add(new MyObject
                {
                    Name = "Name"
                });
                Objects.Add(new MyObject
                {
                    Name = "Name2"
                });
                Objects.Add(new MyObject
                {
                    Name = "Name3"
                });
            }
        }
    
        public class MyObject
        {
            public String Name { get; set; }
            public String Power { get; set; }
        }
    

    Now, update your xaml. You will need to use a CellTemplate and a standard Combobox. The ComboBox will then bind to the Objects collection, display the Name, but set the value in Power.

    <Window x:Class="WpfApplication8.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525"
            x:Name="MyWindow">
        <Grid>
            <DataGrid Name="dgObjects" AutoGenerateColumns="False"
                      ItemsSource="{Binding Objects}">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
                    <DataGridTemplateColumn>
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ComboBox ItemsSource="{Binding ElementName=MyWindow, Path=DataContext.Objects}" 
                                          DisplayMemberPath="Name"
                                          SelectedItem="{Binding Power}" />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Window>