Search code examples
c#wpfmvvmdatagridcomboboxcolumndatagridtextcolumn

Binding a DataGridTextComun To DataGridComboBoxColumn.SelectedItem


Let me first explain what I want, I would like to have a datagrid that will have a ComboBox with lets say a list of companies. When I choose a company, I would like the cell(DataGridTextColumn) next to it to populate the company phone number.

I was able to do this with a regular ComboBox and TextBox, but when I get to a Datagrid it doesn't seem to work correctly. Below is the project that I created for this expample.

enter image description here

        <DataGrid Grid.Row="3" 
              Grid.Column="0"
              Grid.ColumnSpan="2" 
              ItemsSource="{Binding People}"
              AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridComboBoxColumn Header="Comapny"
                                    x:Name="ComboBoxColumn"
                                    SelectedValuePath="{Binding CompanyId}"
                                    DisplayMemberPath="Name">
                <DataGridComboBoxColumn.ElementStyle>
                    <Style TargetType="{x:Type ComboBox}">
                        <Setter Property="ItemsSource"
                                Value="{Binding Path=DataContext.Companies,
                            RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
                    </Style>
                </DataGridComboBoxColumn.ElementStyle>
                <DataGridComboBoxColumn.EditingElementStyle>
                    <Style TargetType="{x:Type ComboBox}">
                        <Setter Property="ItemsSource"
                                Value="{Binding Path=DataContext.Companies, 
                            RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
                        <Setter Property="IsEditable"
                                Value="True" />
                        <Setter Property="SelectedItem"
                                Value="{Binding Path=DataContext.Company}"/>
                    </Style>
                </DataGridComboBoxColumn.EditingElementStyle>
            </DataGridComboBoxColumn>
            <DataGridTextColumn Header="Company Phone"
                                Binding="{Binding ElementName=ComboBox,
                Path=SelectedItem.Phone,
                UpdateSourceTrigger=PropertyChanged}"/>
            <DataGridTextColumn Header="Person Name"
                                Binding="{Binding Name}" />
        </DataGrid.Columns>
     </DataGrid>

Company:

    {
    private string _name;
    private string _phone;
    public int Id { get; set; }

    public string Name
    {
        get { return _name; }
        set
        {
            if(_name!=value)
            {
                _name = value;
                OnPropertyChanged();
            }
        }
    }
    public string Phone
    {
        get { return _phone; }
        set
        {
            if (_phone != value)
            {
                _phone = value;
                OnPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

Person:

    public class Person:INotifyPropertyChanged
{
    private Company _company;
    private int _companyId;
    private string _name;
    public int Id { get; set; }

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
            }
        }
    }

    public Company Company
    {
        get { return _company; }
        set
        {
            if (_company != value)
            {
                _company = value;
                OnPropertyChanged();
            }
        }
    }

    public int CompanyId
    {
        get { return _companyId; }
        set
        {
            if (_companyId != value)
            {
                _companyId = value;
                OnPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

I am not sure what I am doing wrong.

thanks for your help


Solution

  • Why does Person have Company and CompanyId? Company itself has an Id property, no need to replicate it on Person. I'm going to remove CompanyId from Person for this answer.

    DataGridComboBoxColumn - get rid of SelectedValuePath="{Binding CompanyId}" and add SelectedItemBinding="{Binding Company, UpdateSourceTrigger=PropertyChanged}". The UpdateSourceTrigger is so the phone number updates as soon as you pick a company.

    <DataGridComboBoxColumn Header="Company"
                        x:Name="ComboBoxColumn"
                        SelectedItemBinding="{Binding Company, UpdateSourceTrigger=PropertyChanged}"
                        DisplayMemberPath="Name">
    

    The other issue is with the Company Phone DataGridTextColumn. You're binding to element "ComboBox" (even though, I think you meant "ComboBoxColumn"?). But that isn't what you think it is. It's the DataGridComboBoxColumn itself not an instance of what it defines - not the actual combobox.

    You don't need to reference the combobox, the person object that is the row's DataContext has a reference to the Company so just use that. Change the Company Phone DataGridTextColumn to this:

    <DataGridTextColumn Header="Company Phone" Binding="{Binding Path=Company.Phone, UpdateSourceTrigger=PropertyChanged}"/>
    

    I hope that helps.