在我的 .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 的问题。
[ObservableProperty]
ObservableCollection<DataBaseItem> dataBaseItemsFiltered;
<SearchBar Grid.Column="1"
SearchCommand="{Binding FilterCommand}"
Text="{Binding CompanyFilterText}"/>
编辑 2 2022 年 12 月 19 日 解答沉立群-MSFT的问题。
显然
ObservableCollection
(OC) 的行为如下:
示例:删除 1 个项目,然后向 OC 添加 2 次,会将删除的项目的模板分配给第一个添加的项目(不触发 DataTemplateSelector)。对于第二项,触发了 DataTemplateSelector(正在分配正确的模板)
示例: 使用 DataTemplate1 删除一项。 使用 DataTemplate2 删除另一个项目。 添加新项目 => 正在分配 DataTemplate2(无论如何)。 添加新项目 => DataTemplate1 正在被分配(无论如何)。
我找到了一种方法来解决这个问题。 顺便说一句:这个问题在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>
如果对此行为有任何进一步的解释,我将不胜感激。这可能是一个错误并应该报告吗?
我终于找到了答案。
如果
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
,就像第一次一样。
最重要的是,这是针对肯定是错误的问题的优雅解决方案。
信用这个问答给了我这个想法
别忘了点赞和订阅哦!