1
votes

I have a problem understanding how to use a combobox embedded in a datagrid. For example, I have a datagrid which is bound to the class GridModel:

public partial class MainWindow : Window
{
    public GridModel gridModel { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        gridModel = new GridModel();
        dgCustomers.DataContext = gridModel;
    }
}

where the GridModel class is:

public class GridModel : ViewModelBase
{
    public ObservableCollection<Record> customers { get; set; }
    public List<Country> countries { get; set; }
    public GridModel()
    {
        customers = new ObservableCollection<Record>();
        customers.Add(new Record { name = "Alan", phone = "123" });
        customers.Add(new Record { name = "Bert", phone = "234" });
        countries = new List<Country> { new Country { id = 1, name = "England", code = 44 }, new Country { id = 2, name = "Germany", code = 49 } };
    }
}

The XAML is:

<Window x:Class="Customer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
<Grid>
    <DataGrid x:Name="dgCustomers" AutoGenerateColumns="False" ItemsSource="{Binding customers}">
        <DataGrid.Resources>
            <DataTemplate x:Key="dgCombobox">
                <ComboBox   x:Name="cmbCountry"
                            ItemsSource="{Binding countries}"
                            DisplayMemberPath="{Binding name}"
                            SelectedValue="{Binding customers.countryCode, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                            SelectedValuePath="{Binding code}">
                </ComboBox>
            </DataTemplate>
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding name}"/>
            <DataGridTemplateColumn Header="Country" CellTemplate="{StaticResource dgCombobox}" />
            <DataGridTextColumn Header="Phone" Binding="{Binding phone}" ></DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>

</Grid>

The combobox in the second column is supposed to be filled from the countries property of the model, which is a list of class Country:

public class Country : ViewModelBase
{
    private string _name;
    public string name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("name");
        }
    }

    private int _id;
    public int id
    {
        get { return _id; }
        set
        {
            _id = value;
            OnPropertyChanged("id");
        }
    }

    private int _code;
    public int code
    {
        get { return _code; }
        set
        {
            _code = value;
            OnPropertyChanged("code");
        }
    }
}

The combobox should display the country name. The datagrid is populated from the customers ObservableCollection of class record:

public class Record : ViewModelBase
{
    private string _name;
    public string name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("name");
        }
    }

    private string _phone;
    public string phone
    {
        get { return _phone; }
        set
        {
            _phone = value;
            OnPropertyChanged("phone");
        }
    }

    private int _countryCode;
    public int countryCode
    {
        get { return _countryCode; }
        set
        {
            _countryCode = value;
            OnPropertyChanged("countryCode");
        }
    }
}

For the sake of completeness, here is the ViewModelBase class:

public class ViewModelBase : INotifyPropertyChanged
{
    public ViewModelBase()
    {

    }

    public event PropertyChangedEventHandler PropertyChanged;

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

What I want is for the user to be able to select a country for a customer from the combobox in each row, and then the record for that row have its countryCode property set to the code property of the selected country. I've used a datagridtemplatecolumn so that the combobox is displayed on the grid. Any help appreciated, I've been banging my head against this for more than a month and am starting to feel pretty stupid.

1

1 Answers

1
votes

The combobox in the second column is supposed to be filled from the countries property of the model,

I rearranged your datagrid xaml for that

     <Grid>
        <DataGrid ItemsSource="{Binding customers}" 
      AutoGenerateColumns="False" SelectedItem="{Binding SelectedRow,Mode=TwoWay}"
      CanUserAddRows="False" Grid.Row="1">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding name}" Header="Name"
                                Width="*"/>

                <DataGridTemplateColumn Width="Auto" Header="Country">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox ItemsSource="{Binding DataContext.countries,
                                RelativeSource={RelativeSource AncestorType={x:Type Window}}}" 
                                      DisplayMemberPath="name" SelectedValuePath="name" Margin="5" 
                                      SelectedItem="{Binding DataContext.SelectedCountry,RelativeSource={RelativeSource AncestorType={x:Type Window}},Mode=TwoWay}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTextColumn Binding="{Binding phone}" Header="Phone"
                                Width="*"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>

What I want is for the user to be able to select a country for a customer from the combobox in each row, and then the record for that row have its countryCode property set to the code property of the selected country.

In your viewmodel remove dgCustomers.DataContext = gridModel; In MVVM you should never access controls name in code behind instead replace it with this this.DataContext = gridModel; Also add

   private Country _selectedCountry;
    public Country SelectedCountry
    {
        get
        {
            return _selectedCountry;
        }
        set
        {
            _selectedCountry = value;
            _selectedRow.countryCode = _selectedCountry.code;
            OnPropertyChanged("SelectedRow");

        }

    }

    private Record _selectedRow;
    public Record SelectedRow
    {
        get {
            return _selectedRow;

        }
        set
        {
            _selectedRow = value;
            OnPropertyChanged("SelectedRow");
        }


    }

When the item in the listbox is changed the selected row value gets updated.You can put a debugger and check for the values.