Обновите элемент ObservableCollection и связанное представление, когда элемент реализует интерфейс
Как написано везде (например , здесь , здесь , здесь , здесь ...), сообщение оObservableCollection
изменение свойства элемента на представление требует, чтобы элемент реализовал .
Используя CommunityToolkit.Mvvm , это можно сделать с помощью атрибута:
public class MyViewModel : ObservableObject
{
public ObservableCollection<MyItem> MyCollection { get; set; }
//...
}
[INotifyPropertyChanged] // yay! No boilerplate code needed
public partial class MyItem
{
public string MyProperty { get; set; }
}
Если где-то внутри происходит изменениеMyProperty
предметаMyCollection
, представление будет обновлено. Что делать, если интерфейс вступает в игру?
public class MyViewModel : ObservableObject
{
public ObservableCollection<IMyInterface> MyCollection { get; set; }
//...
}
[INotifyPropertyChanged]
public partial class MyItem : IMyInterface // MyProperty is in IMyInterface too
{
public string MyProperty { get; set; }
}
Вид больше не обновляется. Я пытался:
- Наследование
INotifyPropertyChanged
вIMyInterface
(что требует явной реализацииPropertyChanged
событие иOnPropertyMethod
метод вMyItem
, чего я не хочу, иначе я бы не использовал CommunityToolkit.Mvvm) - Добавление
[INotifyPropertyChanged]
кMyViewModel
(ничего не ожидая, но где-то был ответ, говорящий об этом)
Есть ли очевидное решение без шаблонов, которое мне здесь не хватает?
Представление обновляется, если я делаю что-то вроде предложенного здесь
var item = MyCollection[0];
item.MyProperty = "new value";
MyCollection[0] = item;
но я надеюсь, что есть лучший способ.
1 ответ
Я не уверен, что это решит вашу проблему, но я вижу, что вы неправильно используете атрибут.
См. https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/inotifypropertychanged
«Эти атрибуты предназначены для использования только в тех случаях, когда целевые типы не могут просто наследоваться от эквивалентных типов (например, от ObservableObject). Если это возможно, рекомендуемым подходом является наследование, поскольку оно уменьшит размер двоичного файла, избегая создания продублированный код в окончательную сборку».
Вы не наследуете IMyInterface, вы его реализуете.
Ваша модель представления должна наследовать ObservableObject
Вместо
[INotifyPropertyChanged]
public partial class MyItem : IMyInterface
Вы должны иметь:
public partial class MyItem : ObservableObject, IMyInterface
Свойства выглядят так:
[ObservableProperty]
private List<FlatTransactionVM> transactions = new List<FlatTransactionVM>();
Это создает общедоступное свойство Transactions.
Если у вас есть только свойства, вам не нужно, чтобы это был разделяемый класс. Это необходимо для генерации команды реле.
РЕДАКТИРОВАТЬ
Это работает для меня:
Главное окно
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding MyCollection}"
DisplayMemberPath="MyProperty"/>
</Grid>
MainWindowViewModel
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<IMyInterface> myCollection = new ObservableCollection<IMyInterface>();
public MainWindowViewModel()
{
MyCollection = new ObservableCollection<IMyInterface>(
new List<MyItem>{
new MyItem{ MyProperty = "A" },
new MyItem { MyProperty = "B" },
new MyItem { MyProperty = "C" }
});
}
}
МойЭлемент
public partial class MyItem : ObservableObject, IMyInterface
{
[ObservableProperty]
private string myProperty;
}
Интерфейс
public interface IMyInterface
{
string MyProperty { get; set; }
}
Быстрый и грязный код в представлении. Это делается исключительно для того, чтобы увидеть, что происходит, когда установлен один из MyItems.MyProperty.
private async void Window_ContentRendered(object sender, EventArgs e)
{
await Task.Delay(5000);
var mw = this.DataContext as MainWindowViewModel;
mw.MyCollection[1].MyProperty = "XXXXX";
}
После небольшого ожидания я вижу, как второй элемент меняется на XXXXX, как и ожидалось.