Reputation: 133
I have a cart system on a catalog of items with 2 DataTemplates. one is for item with a count of 0. a second for items with a count greater than 0.
We use an Add button within each item For the greater than 0 template we also have a Remove button and change the background color for that grid to make it easier to find which items in the catalog have nonzero purchase numbers.
I have a height set for the collectionView This code works correctly on Android. When I switch and test on Windows, any change that reassigns the item in order to force a template change causes the collectionview to scroll to the top. I have already tried replacing the top VerticalStackLayout with a grid. Also tried CollectionView as top element and putting the stuff in the HorizontalStackLayout in the CollectionView header. And tried using FillAndExpand as the VerticalOptions on the collection view. All of these alternatives still scrolled to the top when running as a Windows app on any change that replaces the element in the collection view. On Android it works fine.
<DataTemplate
x:DataType="viewmodel:FlatRateUI"
x:Key="FlatRateNoItems">
<Grid Padding="10" Margin="0,5,0,0"
BackgroundColor="{AppThemeBinding Light={StaticResource FlatRateBGNoItemsLight}, Dark={StaticResource FlatRateBGNoItemsDark}}"
>
<DataTemplate
x:DataType="viewmodel:FlatRateUI"
x:Key="FlatRateWithItems">
<Grid Padding="10" Margin="0,5,0,0"
BackgroundColor="{AppThemeBinding Light={StaticResource FlatRateBGHasItemsLight}, Dark={StaticResource FlatRateBGHasItemsDark}}"
>
<viewmodel:FlatRateTemplateSelector x:Key="FlatRateItemSelector"
NoItemsSelector="{StaticResource FlatRateNoItems}"
HasItemsSelector="{StaticResource FlatRateWithItems}"
/>
<VerticalStackLayout>
<HorizontalStackLayout>
<Button
x:Name="BackButton"
Text="< Back"
VerticalOptions="Center"
Clicked="OnBackButtonClicked"
HorizontalOptions="Center" />
<Button
x:Name="CartButton"
Text="Cart"
VerticalOptions="Center"
Clicked="OnCartButtonClicked"
HorizontalOptions="Center" />
<Button
x:Name="SCButton"
Text="SC"
VerticalOptions="Center"
Clicked="OnSCButtonClicked"
HorizontalOptions="Center" />
</HorizontalStackLayout>
<CollectionView ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource FlatRateItemSelector}"
VerticalOptions="Fill"
HeightRequest="680"
HorizontalOptions="Fill">
</CollectionView>
</VerticalStackLayout>
</ContentPage>
[RelayCommand]
async Task Add(FlatRateUI i)
{
System.Diagnostics.Debug.WriteLine(i.ItemName + " " + i.Qty);
i.Qty += 1;
if (i.Qty == 1)
{
// If Qty=1, then this slot need to re-render with a different DataTemplate
// have to affect the Items collection object or it doesn't re-render with new Template
int idx = Items.IndexOf(i);
Items[idx] = i;
}
}
[RelayCommand]
async Task Remove(FlatRateUI i)
{
System.Diagnostics.Debug.WriteLine(i.ItemName + " " + i.Qty);
if (i.Qty > 0)
{
i.Qty -= 1;
if (i.Qty == 0)
{
// If Qty=0, then this slot need to re-render with a different DataTemplate
// have to affect the Items collection object or it doesn't re-render with new Template
int idx = Items.IndexOf(i);
Items[idx] = i;
}
}
}
Upvotes: 1
Views: 135
Reputation: 1549
Add this to your CollectionView to set KeepScrollOffset to ItemsUpdatingScrollMode, applicable only on Windows:
<CollectionView ....>
<CollectionView.ItemsUpdatingScrollMode>
<OnPlatform x:TypeArguments="ItemsUpdatingScrollMode">
<On Platform="WinUI" Value="KeepScrollOffset" />
</OnPlatform>
</CollectionView.ItemsUpdatingScrollMode>
</CollectionView>
Upvotes: 0
Reputation: 192
Instead of directly setting indexes try using Insert and Remove methods like this:
int idx = Items.IndexOf(i);
Items.RemoveAt(idx) // or just Items.Remove(i);
Items.Insert(idx,i);
I believe windows may have some problems with reordering, but inserting and removing should call native methods. Didn't try myself, so please reply if it worked.
Upvotes: 0
Reputation: 2049
I would save the position of the selected element (to variable like positionOfSelectedElement
) before the operation that causes the reported issue.
And after the operation I would set the scroll position to the memorized state via
collectionView.ScrollTo(positionOfSelectedElement);
More details can be found here: https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/collectionview/scrolling
=== Update
In case you're using view-model, try this way. Set two-way binding to SelectedItem
<CollectionView
<!-- ... skipped other properties ... -->
ItemsSource = {Binding ListElementsCollection}
SelectedItem="{Binding SelectedListElement, Mode=TwoWay}">
</CollectionView>
Viewmodel:
// before replacing the item, find the index of SelectedListElement
int positionOfCurrentlySelectedElement = ListElementsCollection.FindIndex(...);
....
// after replacing the item,
// we're assuming that positionOfCurrentlySelectedElement is inside of correct bounds
SelectedListElement = ListElementsCollection[positionOfCurrentlySelectedElement];
// if necessary call OnPropertyChanged
OnPropertyChanged(nameof(SelectedListElement));
Upvotes: 0