所以这可能以更一般的形式回答,但这是一个更具体的情况,我想得到如何解决这个问题的想法。我正在编写一个 WPF 应用程序并尝试使用 MVVM 模式(第一次使用此模式)。
我的域对象 Viper 具有多个属性和集合,并在多个现有应用程序中使用。现在,我已经在要在新的 WPF 应用中绑定的所有属性上实现了INotifyPropertyChanged
。我现在正在创建一个位于域对象和 WPF 视图之间的视图模型。问题是 Viper 对象中作为集合的所有属性都是列表而不是可观察集合。我不能简单地将它们设为 ObservableCollections,因为这会影响使用此对象模型的所有其他应用程序(不支持 AddRange
等)。
这个新的 WPF 应用维护一个 Viper 对象列表,这些对象将用于(通过视图模型)来控制 GUI。为了使事情变得更加复杂,该应用程序将以List<Viper>
数据的形式接收数据。应用循环遍历此 Viper 对象列表,并将每个传入的 Viper 数据合并到现有的 Viper 中(按索引)。因此,假设我传入的 Viper 对象将一个项目添加到现有 Viper (List<Event>
) 的 Events 属性中,因为这不是 GUI 不会更新事件网格ObservableCollection
。即使我将视图模型中的List<Event>
转换为OC<Event>
,也是底层 Viper 对象对所有属性和集合进行合并,而不是视图模型,因此修改仍然不会触发任何事件,因为视图模型 OC 没有更新。Viper 对象和所有子对象实现一个自定义MergeWith()
函数,用于确定传入数据应如何合并。有些执行替换,有些执行追加,有些更新。
处理这种情况的正确方法是什么?如果有什么不清楚的地方,请告诉我。
MVVM 中有两种方法可以从ViewModel
向View
公开Model
的属性:将整个模型公开给视图,或者公开视图模型中关注的各个属性。
这两种方法同样有效,尽管您通常使用哪种方法取决于情况。
在您的情况下,您必须使用未设计为通知 UI 更改的现有 Model 对象,我将使用第二种方法在视图的 ViewModel 中创建属性。
例如
<DataGrid ItemsSource="{Binding SelectedViper.Events}" />
public class ViperViewModel : INotifyPropertyChanged
{
private Viper _selectedViper;
public Viper SelectedViper
{
get { return _selectedViper; }
set
{
if (value != _selectedViper)
{
_selectedViper= value;
RaisePropertyChanged("SelectedViper");
}
}
}
}
将变成:
<DataGrid ItemsSource="{Binding ViperEvents}" />
public class ViperViewModel : INotifyPropertyChanged;
{
private Viper _selectedViper;
private ObservableCollection<Event> _viperEvents;
public ViperViewModel()
{
this.PropertyChanged += ViperViewModel_PropertyChanged;
}
void ViperViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedViper")
{
if (SelectedViper == null)
ViperEvents = null;
else
ViperEvents = new ObservableCollection<Event>(SelectedViper.Events);
}
}
public Viper SelectedViper
{
get { return _selectedViper; }
set
{
if (value != _selectedViper)
{
_selectedViper= value;
RaisePropertyChanged("SelectedViper");
}
}
}
public ObservableCollection<Event> ViperEvents
{
get { return _viperEvents; }
set
{
if (value != _viperEvents)
{
_viperEvents = value;
RaisePropertyChanged("ViperEvents");
}
}
}
}
前期工作要多一些,但维护起来要简单得多
或者,您可以覆盖 ObservableCollection
类并实现您感兴趣的List<T>
方法。例如,我有一个当前实现Contains
、IndexOf
、AddRange
和RemoveRange
的ObservableCollectionEx
类。如果您有兴趣,这里有一个Sort
方法的示例。
当然,这也意味着您可能必须更新使用该属性的其他所有内容以使用 ObservableCollectionEx<T>
而不是List<T>
这里的想法基本上是在绑定中保留相同的集合并抑制通知,直到您需要它们为止。
public class Oc<T> : ObservableCollection<T>
{
private object _lockObject;
private bool _suppressChangeNotification;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_suppressChangeNotification)
base.OnCollectionChanged(e);
}
public void Merge(IEnumerable<T> newItems )
{
//don't know if you need a lock..that's your determination
lock (_lockObject)
{
_suppressChangeNotification = true;
foreach (var newItem in newItems)
{
//whatever you do here, insert/remove based on some condition
//i'll just put insert for now
InsertItem(0,newItem);
}
_suppressChangeNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
}
的一种方法是向MergeWith()
中调用的Viper对象添加一个Merged
事件。这样,您的视图模型就可以订阅该事件,并在更新发生时自行更新。