数据虚拟化:通知 GridView 更新时“不支持指定的方法”

问题描述 投票:0回答:1

我想构建一个随机访问虚拟数据源,它从异步源获取数据。现在,我只是试图进行最基本的原理验证:我正在尝试从文件中读取数据并将其显示在 DataGrid 中(最终,这是我想要的 DataGrid 行为),并且我'我正在尝试为其构建一个虚拟化集合。即使是这个最小的工作示例,代码也太多,无法重现(并且该代码远未达到生产标准 - 我一直在破解它以试图暴露问题),但主要功能如下。

在xaml中,我指定一个像这样的DataGrid:

<controls:DataGrid controls:DockPanel.Dock="Right" x:Name="HexDump" ItemsSource="{Binding DataRows, Mode=OneWay}" AutoGenerateColumns="false" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserSortColumns="False" DataFetchSize="32">
    <controls:DataGrid.Columns>
        <controls:DataGridTextColumn Header="Addr" Binding="{Binding startAdr, Converter={StaticResource x4Converter}}"/>
        <controls:DataGridTextColumn Header="Bytes" Binding="{Binding data, Converter={StaticResource x2Converter}}"/>
        <controls:DataGridTextColumn Header="Chars" Binding="{Binding data, Converter={StaticResource cConverter}}"/>
    </controls:DataGrid.Columns>
</controls:DataGrid>

DataRows
BufferRandomAccessVirtualiser
的一个示例,其定义(部分)如下:

public class BufferRandomAccessVirtualiser : IList, INotifyCollectionChanged 
{

    //INotifyCollectionChanged impl

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    // IList impl

    Dictionary <int, BufferedBlock> tempBlocks = new Dictionary<int, BufferedBlock> ();

    public BufferedBlock this[int index] {
        get {
            if (index >= Count) throw new IndexOutOfRangeException();

            if (tempBlocks.ContainsKey(index)) {
                Debug.Write(index); Debug.Write("& ");
                return tempBlocks[index]; 
            }

            Debug.Write(index); Debug.Write("? ");
            var oldItem = tempBlocks[index] = new BufferedBlock(); // placeholder
            loadRowsTask.ContinueWith(rowsResult => { 
                var row = index % rowsPerPage;
                var newItem = new BufferedBlock (rowsResult [row]);
                
                Debug.Write(index); Debug.Write("<");
                tempBlocks[index] = newItem;
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, index));
                Debug.Write("> ");
            });
            return tempBlocks[index];
        } 
        set => throw new NotImplementedException();
    }

    // other parts of IList impl - just boilerplate really
}

这个想法是:当 DataGrid 第一次请求一个值时,行中没有缓存任何内容,因此我提供一个空的

BufferedBlock
,并开始加载正确值的过程。当值准备好时,我调用
CollectionChanged
让 DataGrid 知道。在此期间,如果对该值有任何进一步的请求到达,我会提供原始值的副本 - 之后,我会提供“正确”值。

总的来说,这是有效的。我发现我需要对

NotifyCollectionChangedAction.Replace
调用中的值绝对精确 -
oldItem
必须与 DataGrid 已知的值完全匹配,并且
newIem
必须与下一个调用返回的值精确匹配到索引器(稍后会详细介绍)。但我发现了两件非常奇怪的事情。首先,当进程启动时,某些东西会重复调用第零个值。我在调试输出中看到了这一点(例如当集合中有五个值时):

0? 0& 0& 0& 0& 0& 1? 2? 3? 4? 0& 0& 0& 0& 0& 0& 0&  ...

其中至少有一个来自调用 IList 枚举器的 DataGrid。

让我难住的另一件事是,在

NotifyCollectionChangedAction.Replace
调用期间,DataGrid 调用索引器,检查结果是否匹配
newItem
,然后抛出
System.NotSupportedException
'Specified method is not supported'
。我不知道不支持哪种方法(此时堆栈已深入本机代码),但我知道当我从 XAML 中删除所有列时,问题仍然会发生,因此它不是我的列绑定。当只有一行数据可用时,情况如下:

0? 0& 0& 0& 0& 0<0& Exception thrown: 'System.NotSupportedException' in Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.dll Specified method is not supported.

顺便说一句,如果 tempBlocks 已经填充,并且不需要通知更新,则转换器可以正常工作并且 Gridview 可以正确显示内容。我很满意这不是问题所在。

有人知道 Gridview 试图达到的方法是什么吗?我是否在 Xaml 中遗漏了某些内容?以前有人见过这样的事情吗? (另外,还有一点:初始化是做什么的?为什么 DataGrid 需要调用索引器,即使我已经告诉它新值是什么?)

wpf gridview uwp inotifycollectionchanged
1个回答
0
投票

经过一些额外的工作(以及来自 BionicCode 的提示 - 谢谢!)我已经找到了问题。异常是在 Microsoft.Toolkit.Uwp.UI.Controls.DataGrid/DataGrid/DataGridDataConnection.cs 中生成的,位于 NotifyingDataSource_CollectionChanged 的第 926 行。请参阅它的位置:

case NotifyCollectionChangedAction.Replace:
    throw new NotSupportedException();

所以,它不起作用,因为编写该控件的人明确阻止了它! (我认为这仅限于 UWP - 我知道其他人已经在“传统”WPF 中很好地工作了。)我尝试了

Remove
ing 然后
Add
ing 该项目,但是(除了创建各种种族之外)重叠更新的问题)它不起作用,因为每次添加和删除都会重新编号并刷新现有项目,并且它枚举了整个集合。这完全破坏了数据虚拟化。

我真的希望我的

BufferedBlock
是不可变的(而且,既然如此,我无法理解为什么 BionicCode 推荐使用
INotifyPropertyChanged
)。现在我明白了:我不再尝试更新集合,而是只更新了
BufferedBlock
中的数据,并且成功了!

所以,我详细地发布了这个答案,因为在浪费了一周的时间来追查我只能认为是微软库内严重闲置而产生的故障之后,再加上无用的文档,我希望这对其他人有帮助同样的路径。

© www.soinside.com 2019 - 2024. All rights reserved.