清除项目并将其添加到 ObservableCollection 时,不会执行 DataTemplateSelector

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

在我的 .Net Maui 应用程序中,我想使用

ObservableCollection
显示存储在
DataTemplateSelector
中的数据。因此我使用微软的文档创建了一个示例。 https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/datatemplate?view=net-maui-7.0

我在 ViewModel 中有一个

ObservableCollection<DataBaseItem> DataBaseItemsFiltered;
。在视图中,我有一个 CollectionView 使用
DataTemplateSelector
显示数据。

<CollectionView ItemsSource="{Binding DataBaseItemsFiltered}"
                        ItemTemplate="{StaticResource DBISelector}">

一切都按预期进行,直到我更新

ObservableCollection
。这是启动应用程序后的样子。 “BestCompany”拥有的每个项目都会突出显示(如预期)。

但是如果我更新/过滤“ObservableCollection”(出于测试目的,只需清除并添加原始项目)

[RelayCommand]
void Filter()
{
    ...
    DataBaseItemsFiltered.Clear();
    foreach (DataBaseItem item in dataBaseItems)
    {
        DataBaseItemsFiltered.Add(item);
    }                
}

发生这种情况。

每次调用

Filter()
函数时,这种行为都会交替出现。 我发现只有在启动时才会为集合中的每个项目调用自定义 DataTemplateSelector。然后就再也不会调用它了。 但是,如果我更改
Filter()
函数来实例化
new ObservableCollection

DataBaseItemsFiltered = new(dataBaseItems);

它可以工作,在调试时我可以看到每次调用过滤器函数时调用 DataTemplateSelector (如预期)。

我的问题:有人如何解决这个问题吗?我是否以不符合预期的方式使用

ObservableCollection
?是否有解决方法来告诉视图必须为集合视图中的每个项目调用 DataTemplateSelector?

如果您需要更多代码,请告诉我。提前谢谢你。


编辑 2022 年 12 月 19 日 回答 ToolMakerSteve 的问题。

  1. 是的 DataBaseItemsFiltered 是公共属性(由 MVVMToolkit 生成)
[ObservableProperty]
ObservableCollection<DataBaseItem> dataBaseItemsFiltered;
  1. 是的。刚刚测试过
  2. 过滤器函数绑定到SearchBar的SearchCommand

                <SearchBar Grid.Column="1" 
                           SearchCommand="{Binding FilterCommand}" 
                           Text="{Binding CompanyFilterText}"/>

编辑 2 2022 年 12 月 19 日 解答沉立群-MSFT的问题。

显然

ObservableCollection
(OC) 的行为如下:

  1. 如果添加了项目并且计数大于 OC 实例化以来的计数。 DataTemplateSelector 将被触发。

示例:删除 1 个项目,然后向 OC 添加 2 次,会将删除的项目的模板分配给第一个添加的项目(不触发 DataTemplateSelector)。对于第二项,触发了 DataTemplateSelector(正在分配正确的模板)

  1. 删除 n 个项目并添加 n 个项目将像 LIFO 那样分配模板。

示例: 使用 DataTemplate1 删除一项。 使用 DataTemplate2 删除另一个项目。 添加新项目 => 正在分配 DataTemplate2(无论如何)。 添加新项目 => DataTemplate1 正在被分配(无论如何)。

c# xaml maui
2个回答
1
投票

我找到了一种方法来解决这个问题。 顺便说一句:这个问题在android上不会发生。无法检查 iOS 和 MAC。

所以问题似乎是

DataTemplate
中使用的
DataTemplateSelector
(或更准确地说是它的子元素)。


工作版本

如果孩子继承了

Layout
Grid
VerticalStackLayout
等),一切都会按预期进行。无论如何,当将项目添加到 ObservableCollection 时,会触发
DataTemplateSelector

示例:(使用

Grid
作为
DataTemplate
的子元素)

<DataTemplate x:Key="DataTemplateDefault_Good">
    <Grid>
        <Frame>
            <HorizontalStackLayout x:DataType="local:SomeItem"
                                    Spacing="30">
                <Label Text="{Binding Id}" />
                <Label Text="{Binding Name}" />
            </HorizontalStackLayout>
        </Frame>
    </Grid>
</DataTemplate>

版本无法使用

否则(

DataTemplate
的子代继承于
View
ContentView
)将显示我在问题中描述的行为。仅当将项目添加到 ObservableCollection 且 ObservableCollection 的大小比实例化以来的大小更大时,DataTemplateSelector 才会触发。

示例:(使用

Frame
作为
DataTemplate
的子元素)

<DataTemplate x:Key="DataTemplateDefault_Bad">
    <Frame>
        <HorizontalStackLayout x:DataType="local:SomeItem"
                                Spacing="30">
            <Label Text="{Binding Id}" />
            <Label Text="{Binding Name}" />
        </HorizontalStackLayout>
    </Frame>
</DataTemplate>

如果对此行为有任何进一步的解释,我将不胜感激。这可能是一个错误并应该报告吗?


0
投票

我终于找到了答案。

如果

DataTemplateSelector
ItemTemplate
属性无效然后重新设置,
CollectionView
将被强制重新读取(使用相同的
DataTemplateSelector
实例可以正常工作)

分配给

ObservableCollection
ItemsSource
属性的
CollectionView
将具有
CollectionChanged
event
。订阅它检查是否
NotifyCollectionChangedAction = Reset
(在调用
ObservableCollection
Clear
方法时发出)然后在
event
处理程序中,您可以为空并再次设置
DataTemplate
,如上所述:

myObservableCollection.CollectionChanged += (sender, notifyCollectionChangedEventArgs) =>
{
    if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Reset)
    {
        ItemTemplate = null;
        ItemTemplate = dataTemplate;
    }
};

现在,当重新填充列表时,项目会获得新的

DataTemplate
,就像第一次一样。

最重要的是,这是针对肯定是错误的问题的优雅解决方案。

信用这个问答给了我这个想法

别忘了点赞和订阅哦!

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