首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何手动处理WPF DataGrid的更新?

如何手动处理WPF DataGrid的更新?
EN

Stack Overflow用户
提问于 2017-02-16 22:46:23
回答 2查看 965关注 0票数 1

我的DataGrid的项目是IList。更新IList的唯一方法是通过它是属性的类上的方法。

代码语言:javascript
复制
class SomeObject
{
    public ReadOnlyCollection<SomeType> Items { get; }

    public void AddItem(SomeType someType);

    public event Action<SomeType> ItemAdded;
}

列表本身是一个只读集合,不能直接更新-噢,它肯定不是ObservableCollection。是否有一种方法可以将DataGrid绑定到显示目的,但使用某种类型的绑定/数据挂钩来处理创建、更新和删除项?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-02-20 19:06:31

为您的集合创建实现IBindingList和ICancelAddNew的代理。有了这两个,您可以“拦截”调用添加新的项目,并删除现有的项目。添加新项时调用方法AddNew(),在删除项时调用Remove/RemoveAt。在这种情况下,您可以调用为此目的设计的API方法,而不是直接修改集合。以下是所需的最低实现。请注意以下几点:

  1. 新项不会立即添加到集合中--这些项暂时存储在_newItem字段中。
  2. 如果在编辑新项时命中转义,则首先调用CancelNew,然后调用EndNew
  3. 假定‘Item’是一个可观察的集合,从这些事件触发相应的ListChanged事件。
  4. 此技术没有提供一种方法来拦截对现有项的属性所做的编辑。

..。

代码语言:javascript
复制
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;
    }
}
票数 1
EN

Stack Overflow用户

发布于 2017-02-16 23:01:17

可以将ItemsSource属性DataGrid绑定到返回IEnumerable (包括IList<T>属性)的任何公共属性。但是,不能绑定到字段,因此如果要绑定到Items,则应将其设置为属性:

代码语言:javascript
复制
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事件,并使用BindingExpressionUpdateTarget()方法。

代码语言:javascript
复制
someObject.ItemAdded += (se, ee) => 
{
    var be = BindingOperations.GetBindingExpression(theDataGrid, DataGrid.ItemsSourceProperty);
    if (be != null)
        be.UpdateTarget();

};

或者您可以重置ItemsSource属性:

代码语言:javascript
复制
someObject.ItemAdded += (se, ee) => 
{
    theDataGrid.ItemsSource = someObject.Items;

};

编辑2:

我的问题是,我需要一种可靠的方法来拦截网格的出站绑定机制,以便在添加新行时调用AddItem()。我已经试验过IBindingList,看看是否可以使用它,但到目前为止,它还没有完成。

如果将DataGridDataGrid属性绑定到ObservableCollection<SomeType>,则可以处理此集合的CollectionChanged事件:

代码语言:javascript
复制
observableCollection.CollectionChanged += (ss, ee) =>
{
    if(ee.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
    {
        SomeType newItem = ee.NewItems[0] as SomeType;
        someObject.AddItem(newItem);
    }
};

DataGrid向源集合添加新项时,将引发此事件。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42286131

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档