我有一种情况,我想处理ObservableCollection的CollectionChanged事件,但只有在UI更改集合时才会这样。该集合绑定到DataGrid,我需要在用户添加或删除某些内容后检查一些内容。但是,当我从代码填充集合时,我不需要处理该事件。
我传统上处理这种方式的方法是在进行更改时解除事件。但这看起来有点像“hackish”。另一个想法是设置/取消设置一个类Boolean,它控制何时执行事件代码。但这似乎也很难看。
这种情况是否有任何模式或最佳做法?
答案 0 :(得分:3)
这可以通过为ObservableCollection创建包装器来处理。它将实现修改集合所需的接口,它可以直接传递给包装的observable集合,但它将在调用基本方法时处理设置布尔值,并且当设置布尔值时不会触发其处理程序
这意味着你可以创建一个包装器,为事件添加一个处理程序,然后使用该包装器在代码中操作集合而不会触发事件,但仍然从UI获取事件(假设你绑定了UI)到底层集合,而不是包装器。
您可以通过添加ObservableCollection
实现的一些其他接口(例如只读版本)来添加此功能。您可能还希望公开公开基础集合,如果您希望能够恢复“真实”集合。
这里的优点是,虽然布尔值的设置仍在进行,但您不再需要在整个代码中执行所有操作;它全都搬到了一个地方。
public class ObservableWrapper<T> :
ICollection<T>, IList<T>, INotifyCollectionChanged
{
private ObservableCollection<T> other;
private bool changing = false;
public ObservableWrapper(ObservableCollection<T> wrapped)
{
other = wrapped;
other.CollectionChanged += (sender, args) =>
{
var handler = CollectionChanged;
if (handler != null && !changing)
handler(sender, args);
};
}
public void Add(T item)
{
changing = true;
other.Add(item);
changing = false;
}
public void Clear()
{
changing = true;
other.Clear();
changing = false;
}
public bool Contains(T item)
{
return other.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
other.CopyTo(array, arrayIndex);
}
public int Count
{
get { return other.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
changing = true;
bool result = other.Remove(item);
changing = false;
return result;
}
public int IndexOf(T item)
{
return IndexOf(item);
}
public void Insert(int index, T item)
{
changing = true;
other.Insert(index, item);
changing = false;
}
public void RemoveAt(int index)
{
changing = true;
other.RemoveAt(index);
changing = false;
}
public T this[int index]
{
get
{
return other[index];
}
set
{
changing = true;
other[index] = value;
changing = false;
}
}
public IEnumerator<T> GetEnumerator()
{
return other.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return other.GetEnumerator();
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
}
答案 1 :(得分:1)
由于您似乎有自定义要求,我建议您使用完全独立的列表来执行您需要的任何操作,而不是“黑客攻击”ObservableCollection。
让ObservableCollection成为ObservableCollection,并创建一个实现INotifyCollectionChanged和IList的类。
您需要覆盖添加,删除,等等方法,但最终您会拥有某种自己的智能集合,可以重复使用,它会按照您的要求进行。
您正在谈论的DataGrid需要知道您的自定义集合已实现IList接口,以便调用添加或删除方法。 DataGrid不会自动触发CollectionChanged事件。它只是调用那些add,remove,replace..etc方法,因此你需要在那些改变集合的方法中调用CollectionChanged明确。
这就是为什么CollectionChanged事件中的发送者始终是实例本身而不是某些wpf控件的原因。
答案 2 :(得分:-1)
对于许多其他活动,您可以查看sender object passed to your event handler,以查看它是DataGrid
,普通UIElement
还是您的ViewModel;例如:
yourObservableCollection.CollectionChanged +=
(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs args) =>
{
if (sender is System.Windows.Controls.DataGrid)
{
...
}
}