带有TrulyObservableCollection的WPF Datagrid失去了对编辑

时间:2017-05-31 09:28:35

标签: c# wpf mvvm observablecollection wpfdatagrid

您好我有一个与TrulyObservableCollection绑定的WPF数据网格,每当我尝试通过textinput编辑单元格时,datagrid单元格在输入每个键后都会失去焦点,

public sealed class TrulyObservableCollection<T> : ObservableCollection<T>
    where T : INotifyPropertyChanged
{
    public TrulyObservableCollection()
    {
        CollectionChanged += FullObservableCollectionCollectionChanged;
    }

    public TrulyObservableCollection(IEnumerable<T> pItems)
        : this()
    {
        foreach (var item in pItems) {
            this.Add(item);
        }
    }

    private void FullObservableCollectionCollectionChanged(object sender,
        NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null) {
            foreach (Object item in e.NewItems) {
                ((INotifyPropertyChanged)item).PropertyChanged += ItemPropertyChanged;
            }
        }
        if (e.OldItems != null) {
            foreach (Object item in e.OldItems) {
                ((INotifyPropertyChanged)item).PropertyChanged -= ItemPropertyChanged;
            }
        }
    }

    private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        try {
            var a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
            OnCollectionChanged(a);
        }
        catch {
            // ignored
        }
    }
}

下面是我的ViewModel,它绑定到Datagrid,

public TrulyObservableCollection<SubLotModel> SubLotCollection
{
    get { return _subLotCollection; }
    set
    {
        _subLotCollection = value;
        NotifyOfPropertyChange(() => SubLotCollection);
        SerialNumberAdded = _subLotCollection.Count;
    }
}

在Datagrid中,我有一个文本框列,该列绑定到SublotCollection中的Quantity属性,

<DataGridTemplateColumn Header="Quantity">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBox Text="{Binding Quantity, ValidatesOnDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>

        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

只要在texbox列中输入有效密钥,单元就会失去焦点。

1 个答案:

答案 0 :(得分:3)

诊断

正如我在评论中提到的,编辑失去焦点的原因是这一系列事件:

  • 您输入一个字母,由于UpdateSourceTrigger=PropertyChanged
  • ,新文本被推送到视图模型
  • 您的视图模型上的Quantity属性已更新,并且PropertyChanged事件已被提升
  • 您的TrulyObservableCollection处理PropertyChanged事件,结果将CollectionChanged事件设置为NotifyCollectionChangedAction.Reset
  • DataGrid旨在在这种情况下重建自己,因此每个单元格都是从头开始构建的(重新模板化),并且最终还有一个全新的TextBox。您曾经在信中输入的那个

焦点不会返回到新的TextBox,因为DataGrid并未真正意识到它的存在(因为它在DataTemplate中已定义) 。即使您使用DataGridTextColumn(但您可以查看),我也不会认为焦点会恢复。

所以这里的核心问题是NotifyCollectionChangedAction.Reset。当仅仅改变一个项目的一个属性时,声称收集被彻底改变似乎完全有点过分。

解决方案

更合理的选择是使用CollectionChanged提升NotifyCollectionChangedAction.Replace,包括哪些项目被替换为&#34;。再加上一些改进,这里的集合实现应该像你期望的那样(当我测试它的时候会这样做):

public class TrulyObservableCollection<T> : ObservableCollection<T> 
    where T : INotifyPropertyChanged
{
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        base.OnCollectionChanged(e);
        if (e.NewItems != null)
        {
            foreach (INotifyPropertyChanged inpc in e.NewItems)
                    inpc.PropertyChanged += Item_PropertyChanged;
        }
        if (e.OldItems != null)
        {
            foreach (INotifyPropertyChanged inpc in e.OldItems)
                    inpc.PropertyChanged -= Item_PropertyChanged;
        }
    }

    private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var index = IndexOf((T)sender);
        var args = new NotifyCollectionChangedEventArgs(
            action: NotifyCollectionChangedAction.Replace, 
            newItem: sender, 
            oldItem: sender, 
            index: index);
        //no need to call the override since we've already
        //subscribed to that item's PropertyChanged event
        base.OnCollectionChanged(args); 
    }
}