在这种情况下,如何模拟可观察集合的效果?

时间:2010-04-15 21:13:21

标签: c# wpf data-binding mvvm properties

我正在为另一个应用程序创建配置编辑器,并使用反射来从配置类中提取可编辑字段。以下类是我的各种“DataTypeViewModels”的基类,并显示了我如何获取和设置适当的属性。

public abstract class DataTypeViewModel<T> : ViewModelBase
{
    Func<T> getFunction;

    Action<T> setAction;

    public const string ValuePropertyName = "Value";

    public string Label { get; set; }

    public T Value
    {
        get
        {
            return getFunction.Invoke();
        }

        set
        {
            if (getFunction.Invoke().Equals(value))
            {
                return;
            }

            setAction.Invoke(value);

            // Update bindings, no broadcast
            RaisePropertyChanged(ValuePropertyName);
        }
    }

     /// <summary>
    /// Initializes a new instance of the StringViewModel class.
    /// </summary>
    public DataTypeViewModel(string sectionName, string label)
    {
        if (IsInDesignMode)
        {
            // Code runs in Blend --> create design time data.
        }
        else
        {
            Label = label;

            getFunction = new Func<T>(() =>
                {
                    return (T)Settings.Instance.GetType().GetProperty(sectionName).PropertyType.
                        GetProperty(label).GetValue(Settings.Instance.GetType().GetProperty(sectionName).GetValue(Settings.Instance, null), null);
                });

            setAction = new Action<T>(value =>
                {
                    Settings.Instance.GetType().GetProperty(sectionName).PropertyType.GetProperty(label).
                        SetValue(Settings.Instance.GetType().GetProperty(sectionName).GetValue(Settings.Instance, null), value, null);
                });
        }
    }
}

这部分按我想要的方式工作,下一部分是字符串列表上的示例DataTypeViewModel。

public class StringListViewModel : DataTypeViewModel<ICollection<string>>
{
    /// <summary>
    /// The <see cref="RemoveItemCommand" /> property's name.
    /// </summary>
    public const string RemoveItemCommandPropertyName = "RemoveItemCommand";

    private RelayCommand<string> _removeItemCommand = null;

    public ObservableCollection<string> ObservableValue { get; set; }

    /// <summary>
    /// Gets the RemoveItemCommand property.
    /// TODO Update documentation:
    /// Changes to that property's value raise the PropertyChanged event. 
    /// This property's value is broadcasted by the Messenger's default instance when it changes.
    /// </summary>
    public RelayCommand<string> RemoveItemCommand
    {
        get
        {
            return _removeItemCommand;
        }

        set
        {
            if (_removeItemCommand == value)
            {
                return;
            }

            var oldValue = _removeItemCommand;
            _removeItemCommand = value;

            // Update bindings, no broadcast
            RaisePropertyChanged(RemoveItemCommandPropertyName);
        }
    }

    /// <summary>
    /// The <see cref="AddItemCommand" /> property's name.
    /// </summary>
    public const string AddItemCommandPropertyName = "AddItemCommand";

    private RelayCommand<string> _addItemCommand = null;

    /// <summary>
    /// Gets the AddItemCommand property.
    /// TODO Update documentation:
    /// Changes to that property's value raise the PropertyChanged event. 
    /// This property's value is broadcasted by the Messenger's default instance when it changes.
    /// </summary>
    public RelayCommand<string> AddItemCommand
    {
        get
        {
            return _addItemCommand;
        }

        set
        {
            if (_addItemCommand == value)
            {
                return;
            }

            var oldValue = _addItemCommand;
            _addItemCommand = value;

            // Update bindings, no broadcast

            RaisePropertyChanged(AddItemCommandPropertyName);
        }
    }

    /// <summary>
    /// Initializes a new instance of the StringListViewModel class.
    /// </summary>
    public StringListViewModel(string sectionName, string label) : base(sectionName, label)
    {
        ObservableValue = new ObservableCollection<string>(Value);
        AddItemCommand = new RelayCommand<string>(param =>
            {
                if (param != string.Empty)
                {
                    Value.Add(param);
                    ObservableValue.Add(param);
                }
            });

        RemoveItemCommand = new RelayCommand<string>(param =>
            {
                if (param != null)
                {
                    Value.Remove(param);
                    ObservableValue.Remove(param);
                }
            });
    }
}

正如您在构造函数中看到的,我当前将“Value”镜像到一个名为“ObservableValue”的新ObservableCollection中,然后由XAML中的ListView绑定。它以这种方式运行良好,但克隆List似乎是这样一种hacky方式。虽然绑定到Value,但我尝试添加:

RaisePropertyChanged("Value");

到AddItemCommand和RemoveItemCommand,但这不起作用,ListView将不会更新。这样做的正确方法是什么?

1 个答案:

答案 0 :(得分:2)

实现INotifyCollectionChanged它就像NotifyPropertyChanged,但ObservableCollection使用它来通知插入/删除/重置......

  public class MyCustomCollection : INotifyCollectionChanged
    {
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, e);
            }
        }

        public void Add(Object o)
        {
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, o));
        }

        public void Remove(Object o)
        {
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, 0));
        }

        public void Clear()
        {
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        public void Move(Object o, Int32 newIndex)
        {
            Int32 oldIndex = 0; // can get the old index position using collection.IndexOf(o);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move,
                o, newIndex, oldIndex));
        }

        public Object this[Int32 index]
        {
            get
            {
                return null; // return collection[index];
            }
            set
            {
                Object oldValue = null;  // get old value using collection[index];
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,
                    value, oldValue));
            }
        }
    }

...我从here

复制了此内容