我的DataGrid的项目是IList。更新IList的唯一方法是通过它是属性的类上的方法。
class SomeObject
{
public ReadOnlyCollection<SomeType> Items { get; }
public void AddItem(SomeType someType);
public event Action<SomeType> ItemAdded;
}列表本身是一个只读集合,不能直接更新-噢,它肯定不是ObservableCollection。是否有一种方法可以将DataGrid绑定到显示目的,但使用某种类型的绑定/数据挂钩来处理创建、更新和删除项?
发布于 2017-02-20 19:06:31
为您的集合创建实现IBindingList和ICancelAddNew的代理。有了这两个,您可以“拦截”调用添加新的项目,并删除现有的项目。添加新项时调用方法AddNew(),在删除项时调用Remove/RemoveAt。在这种情况下,您可以调用为此目的设计的API方法,而不是直接修改集合。以下是所需的最低实现。请注意以下几点:
..。
class SomeTypeBindingList : IBindingList, ICancelAddNew
{
public SomeTypeBindingList(SomeObject someObject)
{
_someObject = someObject;
var observableCollection = _someObject.Items as ObservableCollection<SomeType>;
if (observableCollection != null)
observableCollection.CollectionChanged += ObservableCollectionOnCollectionChanged;
}
public IEnumerator GetEnumerator()
{
return new SomeTypeEnumerator(this);
}
public int Count => _someObject.Items.Count + (_newItem == null ? 0 : 1);
public object SyncRoot { get; } = new object();
public bool IsSynchronized { get; } = false;
public bool Contains(object value)
{
return IndexOf(value) != -1;
}
public int IndexOf(object value)
{
if (ReferenceEquals(value, _newItem))
return _someObject.Items.Count;
return _someObject.Items.IndexOf((SomeType)value);
}
public void Remove(object value)
{
var someType = (SomeType)value;
_someObject.RemoveItem(someType);
}
public void RemoveAt(int index)
{
var someType = _someObject.Items[index];
_someObject.RemoveItem(someType);
}
public object this[int index]
{
get
{
if (index >= _someObject.Items.Count)
{
if(_newItem == null)
throw new IndexOutOfRangeException();
return _newItem;
}
return _someObject.Items[index];
}
set
{
throw new NotImplementedException();
}
}
public object AddNew()
{
_newItem = new SomeType();
ListChanged?.Invoke(this, new ListChangedEventArgs(ListChangedType.ItemAdded, _someObject.Items.Count));
return _newItem;
}
public void CancelNew(int itemIndex)
{
_newItem = null;
ListChanged?.Invoke(this, new ListChangedEventArgs(ListChangedType.ItemDeleted, itemIndex));
}
public void EndNew(int itemIndex)
{
if (_newItem != null)
{
var someType = _newItem;
_newItem = null;
_someObject.AddItem(someType);
}
}
private void ObservableCollectionOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove)
Enumerable.Range(e.OldStartingIndex, e.OldItems.Count).ForEach(i => ListChanged?.Invoke(this, new ListChangedEventArgs(ListChangedType.ItemDeleted, i)));
else if(e.Action == NotifyCollectionChangedAction.Add)
Enumerable.Range(e.NewStartingIndex, e.NewItems.Count).ForEach(i => ListChanged?.Invoke(this, new ListChangedEventArgs(ListChangedType.ItemAdded, i)));
}
private readonly SomeObject _someObject;
private SomeType _newItem;
class SomeTypeEnumerator : IEnumerator
{
public SomeTypeEnumerator(SomeObject someObject)
{
_someObject = someObject;
Reset();
}
public void Dispose()
{
}
public bool MoveNext()
{
_index++;
return _someObject.Items.Count < _index;
}
public void Reset()
{
_index = -1;
}
public object Current => _someObject.Items[_index];
object IEnumerator.Current
{
get { return Current; }
}
private readonly SomeObject _someObject;
private int _index;
}
}发布于 2017-02-16 23:01:17
可以将ItemsSource属性DataGrid绑定到返回IEnumerable (包括IList<T>属性)的任何公共属性。但是,不能绑定到字段,因此如果要绑定到Items,则应将其设置为属性:
public IList<SomeType> Items { get; private set; }但是,为了能够在运行时动态地向源集合添加项,并使新项自动显示在DataGrid中,源集合必须实现INotifyCollectionChanged接口。只有ObservableCollection<T>类在.NET框架中这样做。一个List<T>不需要。
而且IList<T>不是只读集合,因为它有一个Add方法:https://msdn.microsoft.com/en-us/library/system.collections.ilist.add%28v=vs.110%29.aspx。所以我想你最好使用一个ObservableCollection<T>。
编辑:
如果您真的想“手动”重新填充DataGrid,可以在视图的代码后面订阅对象的ItemAdded事件,并使用BindingExpression的UpdateTarget()方法。
someObject.ItemAdded += (se, ee) =>
{
var be = BindingOperations.GetBindingExpression(theDataGrid, DataGrid.ItemsSourceProperty);
if (be != null)
be.UpdateTarget();
};或者您可以重置ItemsSource属性:
someObject.ItemAdded += (se, ee) =>
{
theDataGrid.ItemsSource = someObject.Items;
};编辑2:
我的问题是,我需要一种可靠的方法来拦截网格的出站绑定机制,以便在添加新行时调用AddItem()。我已经试验过IBindingList,看看是否可以使用它,但到目前为止,它还没有完成。
如果将DataGrid的DataGrid属性绑定到ObservableCollection<SomeType>,则可以处理此集合的CollectionChanged事件:
observableCollection.CollectionChanged += (ss, ee) =>
{
if(ee.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
SomeType newItem = ee.NewItems[0] as SomeType;
someObject.AddItem(newItem);
}
};当DataGrid向源集合添加新项时,将引发此事件。
https://stackoverflow.com/questions/42286131
复制相似问题