WPF 列表和可观察集合



所以这可能以更一般的形式回答,但这是一个更具体的情况,我想得到如何解决这个问题的想法。我正在编写一个 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 中有两种方法可以从ViewModelView公开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>方法。例如,我有一个当前实现ContainsIndexOfAddRangeRemoveRangeObservableCollectionEx类。如果您有兴趣,这里有一个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事件。这样,您的视图模型就可以订阅该事件,并在更新发生时自行更新。

相关内容

  • 没有找到相关文章

最新更新