Reputation: 1459
in my .Net Maui App I want to display Data stored in an ObservableCollection
using a DataTemplateSelector
. Therefore I created an example using Microsoft's documentation. https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/datatemplate?view=net-maui-7.0
I have an ObservableCollection<DataBaseItem> DataBaseItemsFiltered;
in a ViewModel. In the View I got a CollectionView to display the Data using a DataTemplateSelector
.
<CollectionView ItemsSource="{Binding DataBaseItemsFiltered}"
ItemTemplate="{StaticResource DBISelector}">
Everything works as expected until I update the ObservableCollection
. This is what it looks like after starting the app. Every item owned by "BestCompany" is highlighted (as expected).
But if I update/filter the 'ObservableCollection' (For testing purpose just clearing and adding the originial items)
[RelayCommand]
void Filter()
{
...
DataBaseItemsFiltered.Clear();
foreach (DataBaseItem item in dataBaseItems)
{
DataBaseItemsFiltered.Add(item);
}
}
This happens.
This behaviour alternates every time I am calling the Filter()
function.
I found out that only on start up the custom DataTemplateSelector is called for each Item in the collection. Then it is never called again.
However if I change the Filter()
function to instantiate a new ObservableCollection
DataBaseItemsFiltered = new(dataBaseItems);
It works and while debugging I can see that everytime the DataTemplateSelector is called (as expected) when I call the filter function.
My question: Does anybody how to fix this? Am I using the ObservableCollection
in a way that it is not intended for? Is there a workaround to tell the view that the DataTemplateSelector has to be called for each item in the collection view?
If you need more code just tell me. And thank you in advance.
Edit 19.12.2022 Answering ToolMakerSteve's questions.
[ObservableProperty]
ObservableCollection<DataBaseItem> dataBaseItemsFiltered;
<SearchBar Grid.Column="1"
SearchCommand="{Binding FilterCommand}"
Text="{Binding CompanyFilterText}"/>
Edit 2 19.12.2022 Answering Liqun Shen-MSFT's questions.
Apparently the ObservableCollection
(OC) behaves like this:
Example: Deleting 1 item from then adding 2 times to the OC will assign the template of the deleted item to the first item being added (not triggering the DataTemplateSelector). For the second item the DataTemplateSelector is triggered (Correct template is being assigend)
Example: Deleting one item with DataTemplate1. Deleting another item with DataTemplate2. Adding a new item => DataTemplate2 is being assigned (no matter what). Adding a new item => DataTemplate1 is being assigned (no matter what).
Upvotes: 3
Views: 1135
Reputation: 520
I finally found an answer to this.
The DataTemplateSelector
will be forced to be reread if the ItemTemplate
property of the CollectionView
is nullified and then re set (Using the same DataTemplateSelector
instance works fine)
The ObservableCollection
assigned to the ItemsSource
property of the CollectionView
will have a CollectionChanged
event
. Subscribe to it check if the NotifyCollectionChangedAction = Reset
(as is issued upon calling ObservableCollection
Clear
method) Then in the event
handler you can null and set the DataTemplate
again as described above like so:
myObservableCollection.CollectionChanged += (sender, notifyCollectionChangedEventArgs) =>
{
if (notifyCollectionChangedEventArgs.Action == NotifyCollectionChangedAction.Reset)
{
ItemTemplate = null;
ItemTemplate = dataTemplate;
}
};
Now when the list is repopulated, the items get a new DataTemplate
just like it did the first time.
Best of all, this is an elegant solution to something that is surely a bug.
Credit this Q/A giving me the idea
Don't forget to like and subscribe!
Upvotes: 0
Reputation: 1459
I found a way to somehow get rid of the problem. As a side note: This problem does not occur on android. Cannot check for iOS and MAC.
So the issue seems to be the DataTemplate
(or more precisely its child) used in the DataTemplateSelector
.
Working version
If the child inherits from Layout
(Grid
, VerticalStackLayout
etc.), everything will work as expected. No matter what, the DataTemplateSelector
is triggered when an item is added to the ObservableCollection.
Example: (Using Grid
as ChildElement of the 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>
Version not working
Otherwise (child of DataTemplate
inherits from View
or ContentView
) will show the behaviour I described in my question. DataTemplateSelector only triggers, when an items is added to the ObservableCollection and the size of the ObservableCollection is bigger than it ever was since instantiation.
Example: (Using Frame
as ChildElement of the 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>
I'd appreciate any further explanation to this behaviour. Might this be a bug and shall be reported?
Upvotes: 1