【问题标题】:WPF Datagrid with TrulyObservableCollection loses focus on edit具有 TrulyObservableCollection 的 WPF Datagrid 失去了对编辑的关注
【发布时间】:2017-11-01 00:41:46
【问题描述】:

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

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 列中键入有效键,单元格就会失去焦点。

【问题讨论】:

  • 我的猜测是,每次你输入一个字母时,视图模型Quantity 属性都会更新,引发PropertyChanged 事件,这会导致你的TrulyObservableCollection 引发CollectionChanged 和@ 987654329@,这反过来又会导致DataGrid 重建自身,从而导致先前聚焦的编辑器失去焦点(可能它甚至不再是同一个编辑器,而是一个新创建的实例)。
  • @Grx70:好点。但是,为什么每次更改项目的属性时(OP)都会引发 Reset 事件,即使用 TrulyObservableCollection 类的目的是什么?
  • 我正在使用 TrulyObservable 集合,以便当集合中的项目也发生变化时我可以收到通知。
  • 但是为什么每次更改项目时都会引发重置事件...?这对我来说没有任何意义。

标签: c# wpf mvvm observablecollection wpfdatagrid


【解决方案1】:

诊断

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

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

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

所以这里的核心问题是NotifyCollectionChangedAction.Reset。当仅更改一件物品的一项属性时,声称该收藏品发生了巨大变化似乎完全是矫枉过正。

解决方案

更合理的选择是使用NotifyCollectionChangedAction.Replace 提高CollectionChanged,包括“替换”项目的信息。再加上一些改进,这里的集合实现应该像您期望的那样运行(在我测试它时就是这样):

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); 
    }
}

【讨论】:

    猜你喜欢
    • 2014-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-20
    • 2014-07-26
    • 2015-04-08
    • 2020-06-20
    • 1970-01-01
    相关资源
    最近更新 更多