簡體   English   中英

的ObservableCollection

[英]ObservableCollection

我有一個綁定到ObservableCollection<MyEntity>類型列表的WPF對話框。 在對話框中,我希望只有在對ObservableCollection<MyEntity>列表進行更改時才啟用“確定”按鈕 - 包括從列表中添加/刪除項目以及修改列表中的各個項目。

為了從列表中添加/刪除項目,很容易 - 我為CollectionChanged事件實現了一個處理程序。

我不知道怎么辦是修改單個項目。 比方說,MyEntity.Name =“New Value”,MyEntity類需要實現哪些接口才能使其“可觀察”?

MyEntity需要實現INotifyPropertyChanged,然后在發生屬性更改時觸發PropertyChanged事件。 像這樣:

public class MyEntity : INotifyPropertyChanged
{
    public bool MyFlag 
    {
        get { return _myFlag; }
        set 
        {
            _myFlag = value;
            OnPropertyChanged("MyFlag");
        }
    }

    protected void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

解決這個問題的兩種方法是:

  • 在對象內部有一個事件監聽器,然后在屬性發生變化時設置一個IsDirty標志。 然后將OK按鈕綁定到命令(檢查ICommand接口的用法),並在命令的CanExecute方法中檢查ObservableCollection中的任何對象是否已設置為dirty。 可以使用簡單的LINQ語句完成此檢查: myCollection.Any(x => x.IsDirty == true)

  • 這個方法更笨重和臭...有一個外部對象偵聽更改(通過在每個對象上訂閱PropertyChanged事件),然后外部偵聽器可以啟用OK按鈕(通過數據綁定或直接設置)。

我喜歡slugster提供的答案,這里是一個關於slugster答案的替代建築。

如果使用DelegateCommnd綁定到OK按鈕,則可以為CollectionChanged和PropertyChanged添加事件處理程序,以更改一個簡單的布爾標志來控制OK按鈕的狀態。

public class MainViewModel : ViewModelBase
{
  public DelegateCommand<object> RunCommand { get; set; }
  public DelegateCommand<object> OkCommand { get; set; }
  private bool enableOk = false;
  private bool setOK = false;
  private ObservableCollection<MyEntity> _entites = new ObservableCollection<MyEntity>();

  public MainViewModel()
  {
     _entites.CollectionChanged += (s, e) =>
     {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
           // handle property changing
           foreach (MyEntity item in e.NewItems)
           {
              ((INotifyPropertyChanged)item).PropertyChanged += (s1, e1) => { if (setOK) enableOk = true; };
           }
        }
        // handle collection changing
        if (setOK) enableOk = false;
     };

     MyEntity me1 = new MyEntity { Name = "Name", Information = "Information", Details = "Detials" };
     MyEntity me2 = new MyEntity { Name = "Name", Information = "Information", Details = "Detials" };
     MyEntity me3 = new MyEntity { Name = "Name", Information = "Information", Details = "Detials" };
     _entites.Add(me1);
     _entites.Add(me2);
     _entites.Add(me3);

     // allow collection changes now to start enabling the ok button...
     setOK = true;

     RunCommand = new DelegateCommand<object>(OnRunCommnad, CanRunCommand);
     OkCommand = new DelegateCommand<object>(OnOkCommnad, CanOkCommand);
  }

  private void OnRunCommnad(object obj)
  {
     MyEntity me = new MyEntity { Name = "Name", Information = "Information", Details = "Detials" };

     // causes ok to become enabled
     _entites.Add(me);

     MyEntity first = _entites[0];

     // causes ok to become enabled
     first.Name = "Zamboni";
  }

  private bool CanRunCommand(object obj)
  {
     return true;
  }

  private void OnOkCommnad(object obj)
  {
  }

  private bool CanOkCommand(object obj)
  {
     return enableOk;
  } 
}

這是MyEntity版本(類似於slugster提供的版本):
在此示例中,只有Name屬性觸發事件...

public class MyEntity : INotifyPropertyChanged
{
  private string _name = string.Empty;
  public string Name
  { 
     get
     {
        return _name;
     }
     set
     {
        _name = value;
        OnPropertyChanged("Name");
     }
  }
  public string Information { get; set; }
  public string Details { get; set; }

  public event PropertyChangedEventHandler PropertyChanged;

  protected void OnPropertyChanged(string propertyName)
  {
     PropertyChangedEventHandler handler = PropertyChanged;

     if (handler != null)
     {
        handler(this, new PropertyChangedEventArgs(propertyName));
     }
  }
}

您應該實現INotifyPropertyChanged 您可以通過以下方式執行此操作(如您所見,此實現完全是線程安全的)

private readonly object _sync = new object();

public event PropertyChangedEventHandler PropertyChanged
{
   add { lock (_sync) _propertyChanged += value; }
   remove { lock (_sync) _propertyChanged -= value; }
} private PropertyChangedEventHandler _propertyChanged;

protected void OnPropertyChanged(Expression<Func<object>> propertyExpression)
{
   OnPropertyChanged(GetPropertyName(propertyExpression));
}

protected string GetPropertyName(Expression<Func<object>> propertyExpression)
{
    MemberExpression body;

    if (propertyExpression.Body is UnaryExpression)
        body = (MemberExpression) ((UnaryExpression) propertyExpression.Body).Operand;
    else
        body = (MemberExpression) propertyExpression.Body;

    return body.Member.Name;
}

protected virtual void OnPropertyChanged(string propertyName)
{
  var handler = _propertyChanged;
  if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}

在我上面描述的實現之后,您可以通過兩種方式通知您的更改1)第一種方式

public int MyProperty
{
     get { return _myProperty; }
     set
        {
           if (value != __myProperty)
           {
               _subVersion = value;
               OnPropertyChanged(MyPropertyPropertyName);
            }
        }
} private int _myProperty; const string MyPropertyPropertyName = "MyProperty";

2)第二種方式

public int MyProperty
{
     get { return _myProperty; }
     set
        {
           if (value != _myProperty)
           {
               _subVersion = value;
               OnPropertyChanged(() => MyProperty);
            }
        }
} private int _myProperty; 

另一個解決方案可能是自定義可觀察集合,需要項目來實現INotifyPropertyChanged 用戶必須將處理程序附加到OnItemPropertyChanged事件,只要集合中項目的屬性發生更改,就會調用該事件。

public class ObservableCollectionEnhanced<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
  public ObservableCollectionEnhanced()
    : base()
  { }

  public ObservableCollectionEnhanced(IEnumerable<T> collection)
    : base(collection)
  {
    foreach (T item in Items)
      item.PropertyChanged += OnItemPropertyChanged;
  }

  public ObservableCollectionEnhanced(List<T> list)
    : base(list)
  {
    foreach (T item in Items)
      item.PropertyChanged += OnItemPropertyChanged;
  }

  public event System.ComponentModel.PropertyChangedEventHandler ItemPropertyChanged;
  public void OnItemPropertyChanged(Object sender, PropertyChangedEventArgs e)
  {
    if (null != ItemPropertyChanged)
      ItemPropertyChanged(sender, e);
  }

  protected override void InsertItem(int index, T item)
  {
    base.InsertItem(index, item);
    item.PropertyChanged += OnItemPropertyChanged;
  }

  protected override void RemoveItem(int index)
  {
    T item = this.Items[index];
    item.PropertyChanged -= OnItemPropertyChanged;
    base.RemoveItem(index);
  }

  protected override void SetItem(int index, T item)
  {
    T oldItem = Items[index];
    base.SetItem(index, item);
    oldItem.PropertyChanged -= OnItemPropertyChanged;
    item.PropertyChanged += OnItemPropertyChanged;
  }
}

按如下方式配置處理程序:

public void OnItemPropertyChanged(Object sender, PropertyChangedEventArgs e)
{
  System.Diagnostics.Debug.WriteLine("Update called on {0}", sender);
}

...

collection.ItemPropertyChanged += OnItemPropertyChanged;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM