我有一个绑定到ObservableCollection<MyEntity>
类型列表的WPF对话框。在对话框中,我希望只有在对ObservableCollection<MyEntity>
列表进行更改时才启用“确定”按钮 - 包括从列表中添加/删除项目以及修改列表中的各个项目。
为了从列表中添加/删除项目,很容易 - 我为CollectionChanged
事件实现了一个处理程序。
我不知道如何修改单个项目。比方说,MyEntity.Name =“New Value”,MyEntity类需要实现哪些接口才能使其“可观察”?
答案 0 :(得分:9)
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中的任何对象是否已设置为脏。可以使用简单的LINQ语句完成此检查:myCollection.Any(x => x.IsDirty == true)
这个方法更笨拙和臭...有一个外部对象侦听更改(通过在每个对象上订阅PropertyChanged事件),然后外部侦听器可以启用OK按钮(通过数据绑定或通过直接设置)。
答案 1 :(得分:0)
我喜欢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));
}
}
}
答案 2 :(得分:0)
您应该实施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;
答案 3 :(得分:0)
另一个解决方案可能是需要项目实现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;