0
votes

I'm a beginner to WPF and MVVM pattern. I still have a issue with databinding in case of backward binding. I would like to bind selectedValues of the Comboboxes into my Oberservable Collection. I learned is possible to bind the value of a combobox to a property, but in this case I would like to bind to a property of a collection and the collection is the parent.

Could somebody explain me how to bind the combobox selectedvalues to my observablecollection?

I have a workaround in my mind to make for each combobox a new property in my viewmodel and collect all values and store by button press this values into my collection, but this seems for me wrong, because is not the behavouir of databinding.

EDIT The comboboxes items are corrected populated from each model, but how i can bind the selectedvalue of the combobox to my collection property?

<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, 
                                         AncestorType={x:Type Window}}, 
                                         Path=DataContext.Cities}" 
                                         DisplayMemberPath="Name"
                                         SelectedValue="{Binding Path=RowEntries }"/>

The SelectedValue="{Binding Path=RowEntries}" is wrong or is this correct?

EDIT 2

I added a Listview binded to my collection to see, if the properties are binded to the selectedvalue of my combobox, but is keeps empty.

<ListView ItemsSource="{Binding RowEntries}" BorderBrush="Black" BorderThickness="1">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Path=CityName}"></TextBlock>
                        <TextBlock Text="{Binding Path=CountryName}"></TextBlock>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

I have this solution:

Solution

My Models:

// INPC implemented by Propertychanged.Fody
    public class RowEntry : BaseModel
    {
        public string CityName { get; set; }
        public string CountryName { get; set; }
    }

    // INPC implemented by Propertychanged.Fody
    public class City : BaseModel
    {
        public string Name { get; set; }
    }
    // INPC implemented by Propertychanged.Fody
    public class Country : BaseModel
    {
        public string Name { get; set; }
    }

My ViewModel:

public class TestViewModel : ViewModelBase
{
    #region properties
    // INPC implemented by Propertychanged.Fody
    public ObservableCollection<City> Cities { get; set; } = new ObservableCollection<City>();
    public ObservableCollection<Country> Countries { get; set; } = new ObservableCollection<Country>();
    public ObservableCollection<RowEntry> RowEntries { get; set; } = new ObservableCollection<RowEntry>();
    #endregion

    #region constructors and destructors

    /// <summary>
    /// Initializes a new instance of the MainViewModel class.
    /// </summary>
    public TestViewModel()
    {
        // Sample Data
        var temp = new City { Name = "Rom" };
        Cities.Add(temp);
        var temp2 = new City { Name = "Sydney" };
        Cities.Add(temp2);

        var temp3 = new Country { Name = "Italy" };
        Countries.Add(temp3);
        var temp4 = new Country { Name = "Australia" };
        Countries.Add(temp4);

        RowEntries.Add(new RowEntry());
    }
    #endregion
}

My Ui:

<StackPanel>
        <DataGrid ItemsSource="{Binding RowEntries}" AlternationCount="{Binding Items.Count, RelativeSource={RelativeSource Self}}" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding AlternationIndex, RelativeSource={RelativeSource AncestorType=DataGridRow}}" Header="#"/>
                <DataGridTemplateColumn Header="City">
                    <DataGridTemplateColumn.CellTemplate>
                        <HierarchicalDataTemplate>
                            <ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, 
                                         AncestorType={x:Type Window}}, 
                                         Path=DataContext.Cities}" 
                                         DisplayMemberPath="Name"
                                         SelectedValue="{Binding Path=RowEntries }"/>
                        </HierarchicalDataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="Countries">
                    <DataGridTemplateColumn.CellTemplate>
                        <HierarchicalDataTemplate>
                            <ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, 
                                         AncestorType={x:Type Window}}, 
                                         Path=DataContext.Countries}" 
                                         DisplayMemberPath="Name"/>
                        </HierarchicalDataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
        <Button Content="Add Row" Margin="0,0,1797,0"></Button>
    </StackPanel>
1
What is your issue? Also show the XAML markup.Kim Hoang
Sorry Connection dropped during input. Please check my full postShazter
Your RelativeSource seems not correct, can you try this RelativeSource={RelativeSource AncestorType={x:Type Window}}Kim Hoang
To avoid misunderstanding. Add start the Rowentries collection is empty and the collection shall be filled by the combobox selected values. @Kim I tried, but this give me syntax error.Shazter

1 Answers

1
votes

You should bind the SelectedValue property of the ComboBoxes to the CityName and CountryName properties of the RowEntry object and set the SelectedValuePath property of the ComboBoxes to "Name". Also set the UpdateSourcePropertyTrigger of the bindings to PropertyChanged:

<DataGrid.Columns>
    <DataGridTextColumn Binding="{Binding AlternationIndex, RelativeSource={RelativeSource AncestorType=DataGridRow}}" Header="#"/>
    <DataGridTemplateColumn Header="City">
        <DataGridTemplateColumn.CellTemplate>
            <HierarchicalDataTemplate>
                <ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Cities}" 
                                         DisplayMemberPath="Name"
                                         SelectedValuePath="Name"
                                         SelectedValue="{Binding Path=CityName, UpdateSourceTrigger=PropertyChanged}"/>
            </HierarchicalDataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>
    <DataGridTemplateColumn Header="Countries">
        <DataGridTemplateColumn.CellTemplate>
            <HierarchicalDataTemplate>
                <ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Countries}" 
                                         DisplayMemberPath="Name"
                                         SelectedValuePath="Name"
                                         SelectedValue="{Binding Path=CountryName, UpdateSourceTrigger=PropertyChanged}"/>
            </HierarchicalDataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>
</DataGrid.Columns>

Then the setters of the source properties (CityName and CountryName) will be called when you select an item in the ComboBoxes.

If you want to select some values initially, you simply set these properties to some values that are present in the ComboBoxes:

public TestViewModel()
{
    ...
    RowEntries.Add(new RowEntry() { CityName = "Sydney", CountryName = "Australia" });
}