wpfentity-frameworkdata-bindingprism-6

WPF Combobox Binding broken after Collection repopulation


I have following Situation. I have a ListView set up as following:

<ListView Margin="3,3,3,3" ItemsSource="{Binding Toner}" SelectedValue="{Binding CurrentToner}" IsSynchronizedWithCurrentItem="True">
        <ListView.ItemContainerStyle>
            <Style TargetType="{x:Type ListViewItem}">
                <Setter Property="BorderBrush" Value="LightGray" />
                <Setter Property="BorderThickness" Value="0,0,0,1" />
            </Style>
        </ListView.ItemContainerStyle>

        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid>

                    <Grid.RowDefinitions>
                        <RowDefinition></RowDefinition>
                        <RowDefinition></RowDefinition>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="50"></ColumnDefinition>
                        <ColumnDefinition Width="Auto"></ColumnDefinition>

                    </Grid.ColumnDefinitions>
                    <Label Grid.Row="0" Grid.Column="0" Content="{Binding Id}" FontWeight="Bold"></Label>
                    <Label Grid.Row="0" Grid.Column="1" Content="{Binding Name}" FontWeight="Bold"></Label>
                    <Label Grid.Row="1" Grid.Column="0" Content="{Binding Ratio}">
                    </Label>
                    <Label Grid.Row="1" Grid.Column="1" Content="{Binding TonerType.Name}"  ></Label>

                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

I set the Property CurrentToner in the corresponding ViewModel:

private Toner currentToner = new Toner();
public Toner CurrentToner
    {
        get => currentToner;
        set
        {
            SetProperty(ref currentToner, value);
            updateTonerCommand.RaiseCanExecuteChanged();
            deleteTonerCommand.RaiseCanExecuteChanged();
            increaseAmountCommand.RaiseCanExecuteChanged();
            RaisePropertyChanged(nameof(Changes));
        }

    }

Now in the same View as above i also have some Controls bound to Properties of the CurrentToner.

<TextBox Style="{StaticResource TextBoxStyle}" Grid.Row="0" Grid.Column="1" Text="{Binding CurrentToner.Name}"></TextBox>
        <TextBox Style="{StaticResource TextBoxStyle}" Grid.Row="1" Grid.Column="1" Text="{Binding CurrentToner.Level}" ></TextBox>
        <ComboBox  Grid.Row="2" Grid.Column="1" SelectedValue="{Binding CurrentToner.TonerType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding TonerTypes, Mode=OneWay}" DisplayMemberPath="Name"></ComboBox>
        <TextBox Style="{StaticResource TextBoxStyle}" Grid.Column="1" Grid.Row="3" Text="{Binding CurrentToner.Note}" AcceptsReturn="True" VerticalScrollBarVisibility="Visible"></TextBox>

Now i can click arround in the ListView and my TextControls will be updated as they are supposed to. However, if i reload the Data in the Observable Collection, the selected Item will no longer be updated in the Combobox (The data is still available, i checked with the Debugger and the Combobox can still set the TonerType in the CurrentToner) I reload the Data from an SQL Server using Entity Framework :

private void ReloadToner()
    {
        Toner.Clear();
        using (var uw = new UnitOfWork(new Gritly_DEVEntities1()))
        {
            Toner.AddRange(uw.Toners.GetAll());

        }

    }

How do i have to implement my Reload logic to update the selected item again? (It works before the reload)

EDIT: Screenshots of how it is supposed to be and how it's not. it should be like this not like this

EDIT2: I found the Answer, I had to override Equals() on TonerType to compare based on Id instead of Adress


Solution

  • If you reload the data then you have new objects in your collection.
    I missed what that object type is if it's there but let's call that a Toner anyhow. Make sure Toner has something uniquely identifies it, like an ID. If you look at a Toner object , you can probably work out which is the old current one but computers are very literal minded. They need you to tell them exactly how they're supposed to know a Toner is a specific Toner rather than just any old one.

    When you're going to re-read your data, save that ID in a private variable. Read the data.
    Build your observable collection. Find the appropriate entry using some linq matching on id.
    Something like:

    Toner newSelectedToner = Toners.Where(x=>x.ID = lastSelectedID).FirstOrDefault();
    

    That's just air code by the way. Set CurrentToner to that instance.
    Also, override equals on your Toner to use ID.