0

Let's say I have the following ListView in my view

<ListView x:Name="ListView1" ItemsSource="{Binding SomeCollection}">
    <ListView.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Do something" Command="{Binding SomeCommand, Mode=OneWay}" />
        </ContextMenu>
    </ListView.ContextMenu>
    <ListView.ItemTemplate>
        <DataTemplate DataType="model:Person">
            <StackLayout>
                <TextBlock Text="{Binding Name}">
                <Image Source="{Binding State, Converter={StaticResource StateToIconConverter}, Mode=OneWay}" />
            </StackLayout>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Now, this Person is a model and its properties do not have any way to inform the view of them being updated (according to MVVC). But, I need to update the view after SomeCommand gets executed, because items inside SomeCollection get edited.

I tried doing this

public void ExecuteSomeCommand() {
    // change the state of some person inside SomeCollection
    (ListView1.SelectedItems[0] as Person).State = "Some different state";

    // now inform the view of change, so it can reflect in the DataTemplate
    ListView1.GetBindingExpression(ListBox.ItemsSourceProperty).UpdateTarget();
}

And I thought this would propagate into the DataTemplate, but it doesn't. Is there any other way to do this? How should I change my approach?

Sorashi
  • 931
  • 2
  • 9
  • 32
  • The ItemsSource Binding is not the Image.Source Binding. Updating the first when the second's source property has changed is pointless. Besides that, consider making Person fire property change notifications. – Clemens Aug 30 '21 at 15:24
  • @Clemens I know, but how do I get the reference to the image control if it's inside a DataTemplate? – Sorashi Aug 30 '21 at 15:28
  • As another note, do not use the `as` operator without checking its result for `null`. Use an explicit cast instead: `((Person)ListView1.SelectedItem).State = ...`; – Clemens Aug 30 '21 at 15:28
  • 1
    Let your model implement [INotifyPropertyChanged](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged?view=net-5.0). – Olivier Jacot-Descombes Aug 30 '21 at 15:28
  • @Clemens I do not use it like this, this is just a sample code which reflects what I want to have done – Sorashi Aug 30 '21 at 15:29
  • @OlivierJacot-Descombes that is not good practice, see https://stackoverflow.com/questions/6922130/in-mvvm-model-should-the-model-implement-inotifypropertychanged-interface – Sorashi Aug 30 '21 at 15:29
  • @Sorashi Quite the opposite. When you bind to a model property that affects the UI, that property should always fire a change notification. Otherwise wrap the model class in a view model class. – Clemens Aug 30 '21 at 15:30
  • @Clemens okay, so in this case, it's ok to use INotifyPropertyChanged? – Sorashi Aug 30 '21 at 15:31
  • Sure, we told you already. – Clemens Aug 30 '21 at 15:31
  • Okay, then post it as an answer – Sorashi Aug 30 '21 at 15:32

1 Answers1

1

When the model used in data binding implements the INotifyPropertyChanged Interface the UI is automatically updated when you modify a property of the model.

public class Person : INotifyPropertyChanged
{
    private Image _state;
    public Image State
    {
        get => _state;
        set {
            if (value != _state) {
                _state = value;
                OnPropertyChanged(nameof(State));
            }
        }
    }

    // ... other properties here ...

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • State should not return an Image, but an ImageSource - or anything else that is not a UIElement and can be converted by OP's Binding Converter. E.g. a string, as the code in the questions suggests. – Clemens Aug 30 '21 at 16:11
  • Okay, my example must be adapted to the type the OP is using in its model class. – Olivier Jacot-Descombes Aug 30 '21 at 16:15