Xamarin.Forms
Xamarin.Forms copied to clipboard
[Bug] Binding CurrentItem of CarouselView overwrites value of backing property when CarouselView initializes
Description
When CarouselView initializes, it forces CurrentItem to be set to the first item in its collection. If CurrentItem is bound to a backing property, that causes it to be overwritten. Part of the issue may be that setting CurrentItem doesn't set Position?
Steps to Reproduce
- Initialize a property to a value that doesn't appear first in a CarouselView.
- Bind CarouselView.CurrentItem to the property.
- Run the application.
Expected Behavior
CarouselView.CurrentItem should take on the value of its backing property at initialization and the backing property should be unchanged.
Actual Behavior
CarouselView.CurrentItem is initialized to the first item in ItemsSource, which is propagated to the property bound to CurrentItem.
Basic Information
- Version with issue: 4.8.x, 5.0.0-pre3
- Last known good version: 4.7
- IDE: Visual Studio 16.8.1
- Platform Target Frameworks:
- Android: 11.0
Reproduction Link
CarouselInitializationBinding.zip
Workaround
No, it takes control. I can't figure out how to hook it so that the position changes after loading. EDIT: see next post.
Found an ugly workaround:
bool skipNext;
private void Carousel_CurrentItemChanged(object sender, CurrentItemChangedEventArgs e)
{
// workaround for initial set bug
bool isBad = new StackTrace().GetFrames().Select(x => x.GetMethod().Name).Any(x => x.Contains("SetUpNewElement"));
if (isBad)
{
if (!skipNext)
{
skipNext = true;
((CarouselView)sender).Position = _GridLayouts.IndexOf((GridLayout)e.PreviousItem);
}
else
skipNext = false;
return;
}
// your code
}
You have to set Position, not CurrentItem, here. And the skipping thing may be specific to how I'm doing things. GridLayout is the type of item the CarouselView has in its ItemsSource.
Ahhhhh the workaround breaks when Loop == false for some reason??? But when Loop == true, the displayed item is out of order and changes as soon as you touch the carousel.
😩
Found an actual workaround:
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
gridLayoutCarousel.ScrollTo(_GridLayouts.IndexOf(_viewModel.Hemocytometer.GridLayout)); // workaround for initial set bug
}
private void GridLayoutCarousel_CurrentItemChanged(object sender, CurrentItemChangedEventArgs e)
{
// workaround for initial set bug
bool isBad = new StackTrace().GetFrames().Select(x => x.GetMethod().Name).Any(x => x.Contains("UpdateAdapter"));
if (isBad)
return;
// handle the event
}
OnSizeAllocated fires after the control is in place. Still need to avoid handling any events during the initialization.
I'm having the same issue. Putting in Task.Delay on setting the backing property can help, but it's a bad work around.