Xamarin forms Carousel view does not allow skipping position - c#

So i am trying to implement a wizard using carousel view that has next and previous buttons. For some reason the carousel view does not allow me to navigate multiple positions. For example if the view is currently at position 0 and i want to jump to position 2 it will go to position 1.
So view in xaml:
<CarouselView CurrentItemChanged="CurrentCarouselItemChanged" x:Name="EventWizardCarouselView" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Grid.Row="0">
<CarouselView.ItemsSource>
<x:Array Type="{x:Type ContentView}">
<views:EventBasicInfoView x:Name="eventWizardViewEventBasicInfo"/>
<views:EventRulesView x:Name="eventWizardViewEventRules"/>
<views:EventLocationsView x:Name="eventWizardViewEventLocations"/>
</x:Array>
</CarouselView.ItemsSource>
<CarouselView.ItemTemplate>
<DataTemplate>
<ContentView Content="{Binding .}"/>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
private void OnNextClicked(object sender, EventArgs e) {
EventWizardCarouselView.Position = 2;
OnPropertyChanged("Position");
}
Moral of the story here is that when in position 0 and i programatically set it to position 2 it will display position 1? However from position 1 it will go to position 2 just fine?

I was able to solve this on UWP by turning off animation. This is more of a band-aid than a fix but works for now. For those that run into this note that animation is set to true by default and i had to use ScrollTo(index, groupindex, type, false).

On Android, set the CurrentItem property of the carouselview. On iOS, set the Position property. Something like this code:
if (Device.RuntimePlatform == Device.Android) {
var selected = YourViewModelCollection[2];
EventWizardCarouselView.CurrentItem = selected;
} else if (Device.RuntimePlatform == Device.iOS) {
EventWizardCarouselView.Position = 2;
}

There are two kinds of way to change position of CarouselView , however the effects of animation all will show the middle position when skipping position.
One is using CurrentItem or Position , they all can used in Android /iOS even in UWP (if no problem when running UWP).
CurrentItem, of type object, the current item being displayed. This property has a default binding mode of TwoWay, and has a null value when there isn't any data to display.
eg : carouselView.CurrentItem = viewmodel;
Position, of type int, the index of the current item in the underlying collection. This property has a default binding mode of TwoWay, and has a 0 value when there isn't any data to display.
eg :carouselView.Position= position;
Another is using Scrolling .
carouselView.ScrollTo(position);
or
carouselView.ScrollTo(ViewModel);
Note :
Not too much focus on UWP , because CarouselView is available on iOS and Android, but some functionality may only be partially available on the Universal Windows Platform.
And CarouselView is a preview Control which statrs available in Xamarin.Forms 4.3.

I have not found the "real" solution for this problem, but I developed a quick workaround that looks nice enough.
In my app I got 3 tabs. Moving by 1 tab works fine so I just check if I want to got from the first to the last or the other way round and then just make it two movements:
if (Device.RuntimePlatform == Device.iOS
&& Math.Abs(oldIndex - newIndex) > 1) // Tab index is changed by 2?
{
_carouselView.ScrollTo(1);
// Second move after delay
Task.Delay(500).ContinueWith((_) => Device.BeginInvokeOnMainThread(() =>
_carouselView.ScrollTo(newIndex)),
TaskScheduler.FromCurrentSynchronizationContext()
);
}
else
{
_carouselView.ScrollTo(newIndex);
}
No customer complained so far :-)

Related

uwp - scrollviewer doesn't keep scroll position when content is updated

I have this following XAML for my ScrollViewer
<!-- Scroller -->
<ScrollViewer x:Name="Scroller"
Margin="0 75 0 0"
Width="1080"
Height="560"
HorizontalScrollMode="Enabled"
HorizontalScrollBarVisibility="Auto"
HorizontalSnapPointsType="MandatorySingle"
HorizontalSnapPointsAlignment="Center">
<StackPanel x:Name="StackItems"
Orientation="Horizontal"
Background="Transparent"
HorizontalAlignment="Center"
Width="Auto"
Padding="920 0 0 0">
</StackPanel>
</ScrollViewer>
The problem is when I use method Insert of StackPanel to add a new item, the new item push the current view to either left or right according to the index I add.
For example:
There are 5 items in my StackPanel now, and I scrolled to the last index (4th), then while I am viewing index 4th and I added new Item to the first as shown below, my current view will be pushed to item 3rd and I need to scroll right once to get the 4th Item again.
this.StackItems.Children.Insert(0, new Item());
For this following matter, Could anyone tell me how to keep the current viewport active even if there is new item is added to the StackPanel ?
Appreciate that. Thank you!
There are two ways I can think of right away that use the ScrollViewer.
All methods below assume you know the index of the item you want to bring into view. If you don't know this you can use the current scrolllocation or selected item (in case of a ListView) to obtain this index.
The first method is by using ScrollViewer.ChangeView():
After you have added a new item to StackItems you can add this line of code:
Scroller.ChangeView(
StackItems.Padding.Left +
StackItems.Children
.Take(indexToBringIntoView)
.Sum(u => u.ActualSize.X),
null,
null
);
If the width of all items are equal you can instead just write of course:
Scroller.ChangeView(StackItems.Padding.Left + itemWidth * indexToBringIntoView, null, null);
The second method is by using UIElement.StartBringIntoView():
This method is easier than the first method, however I have noticed some strange behaviour of the ScrollViewer when using this.
After adding your new item to the StackItems you can use the following the bring you previous item into view:
StackItems.Children[indexToBringIntoView].StartBringIntoView();
Another way of achieving your goal is to use a horizontal ListView
You can set this up following the accepted answer from this SO thread.
Then if you have the index of the item you want to bring into view you can use ListView.ScrollIntoView(object item) like so:
listView.ScrollIntoView(Lister.Items[indexToBringIntoView]);
I have included this method as well since this is a very solid working method and I never had a problem with it.
If this is not what you are looking for, let me know and I'll adjust my answer.

xamarin 4.2 CarouselView automatic slider

Is there a way to make automatic these changes of image on a carouselView in xamarin 4.2? I named the xamarin forms version because in this version there is a CarouselView, so I am not able to use the old nuget.org/packages/Xamarin.Forms.CarouselView/2.3.0-pre1
In that old version there was a Position property which cold be set to any item in this new version included on xamarinForms 4.2, that property is missing
What I want to create is something to show a different image every 4 seconds, do you have any suggestion?
i have create automatic slider using the following code in constructor.
Device.StartTimer(TimeSpan.FromSeconds(5), () =>
{
if (listOfSpotlight.Count == 0 || listOfSpotlight == null)
{
return true;
}
slidePosition++;
if (slidePosition == listOfSpotlight.Count) slidePosition = 0;
caroselView.Position = slidePosition;
return true;
});
The Xamarin.Forms.CarouselView has been having a bunch of issues lately and I am not sure how exactly all of a sudden Xamarin decided to add it to the 4.2 release. Also it seems as if there are no properties in it which is weird. Anyway, I was facing weird issues with Xamarin's Carousel so I started using the CarouselView.FormsPlugin and it works amazingly in all situations.
Nuget: https://www.nuget.org/packages/CarouselView.FormsPlugin/
Github: https://github.com/alexrainman/CarouselView
FYI it has all the properties that XF carousel does and more:
Add the following namespace:
xmlns:controls="clr-namespace:CarouselView.FormsPlugin.Abstractions;assembly=CarouselView.FormsPlugin.Abstractions"
And then use it something like this:
<controls:CarouselViewControl Orientation="Horizontal" InterPageSpacing="10" Position="{Binding myPosition}" ItemsSource="{Binding myItemsSource}" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<controls:CarouselViewControl.ItemTemplate>
<DataTemplate>
<local:MyView /> <!-- where MyView is a ContentView -->
</DataTemplate>
</controls:CarouselViewControl.ItemTemplate>
</controls:CarouselViewControl>
For finding additional properties look for Bindable Properties section of the homepage at Git.
You can try Chetan Rawat's answer or
Device.StartTimer(TimeSpan.FromSeconds(4), (Func<bool>)(() =>
{
cView.Position = (cView.Position + 1) % imagesList.Count;
return true;
}));
*here cView is my carousel view and imageList is my list that contains the images that have to be slided in carousel view .
Happy Coding :)

Unable to Programmatically Navigate Dynamic ListView

<ListView x:Name="myListView" ItemsSource="{x:Bind PageViewModel.myCollectionOfThings, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewmodels:ThingViewModel>
<TextBlock Text="{x:Bind Name, Mode=OneWay}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Name="TestButton" Tapped="TestButton_Tapped"/>
private void TestButton_Tapped(object sender, TappedRoutedEventArgs e)
{
PageViewModel.myCollectionOfThings.Add(newItem);
myListView.SelectedIndex++;
}
I'm trying to create a carousel control**. Using the code above I can dynamically add an item to the end of the collection each time its selected index increases. It works as one would expect except one problem. Even though the UI is reflecting the new items added to the end of the ListView, if I iterate past the last index of the original collection size, the UI jumps back to the beginning of the list and the selected index becomes 0. I've tried many variations of the above code and tried different collection types. I've also tried re-assigning the ListView's ItemSource each iteration which didn't do anything. Any help would be appreciated.
**I know there's something called Carousel in the UWP Community Toolkit but it isn't actually a carousel. A carousel can scroll endlessly as its collection will loop, which that control does not do.
If you want Carousel that can scroll endlessly then take a look at the Carousel control in the Windows AppStudio NuGet package. Download Windows App Studio UWP Samples to learn about the control.
Here an image of this Carousel Control
It looks like the listview is rebinding with binding source when the collection is changed. Try to re-set the index manually after adding the item.
int currentIndex = myListView.SelectedIndex;
PageViewModel.myCollectionOfThings.Add(newItem);
myListView.SelectedIndex = currentIndex++;
// If above doesn't work try setting selectedItem property to newItem.

DataTemplate not loading in pivot windows phone 8.1

I'm trying to display different layouts in pivot items in a wp8.1 app (UNIAPP ). Ideally I would like to load different pages but since I could figure this out, I thought I'd try with the basics first as I'd use this before but for some reason I can't get this to work.
My pivot items are loaded dynamically based on the provided ViewModel
<Pivot.ItemTemplate>
<DataTemplate>
<controls:DataTemplateSelector Content="{Binding}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch">
</controls:DataTemplateSelector>
/DataTemplate>
</Pivot.ItemTemplate>
My resources are defined as follows within the same xaml page
<Page.Resources>
<DataTemplate x:Key="MyApp.ViewModel.PIDetailsVM">
<Button Content="test" Foreground="White"></Button>
</DataTemplate>
<DataTemplate x:Key="MyApp.ViewModel.PIListVM">
<Button Content="test" Foreground="White"></Button>
</DataTemplate>
</Page.Resources>
My DataTemplateSelector is defined as follows:
public class DataTemplateSelector : ContentControl
{
protected override void OnContentChanged(object oldContent,
object newContent)
{
ContentTemplate = this.FindResource<DataTemplate>(newContent.GetType
().FullName);
}
}
It is being triggered whenever I go to a new pivot item, but the ContentTemplate is always null.
The newContent.GetType().FullName returns the relevant viewmodel name which I can see being displayed in the relevant pivot.
One thing I noticed is that the DataTemplateSelector class (this) has no resources when I check it via this.Resources.count(), so it's obviously not finding them but how do I fix this?
UPDATE:
My DataTemplates are not getting loaded in my Pivot Items. There is obviously a problem with the .NET IDE as whenever I add or remove a from Content="{Binding}" it displays the button within the pivot item but that's within the IDE. Unfortunately, at run-time, it just displays the name of my viewmodel.
Thought the behaviour is erratic in the IDE, the fact that the button from my DataTemplate is displaying when messing around with the Content="{Binding<space>" would make you think that the code and xaml are correct but it's definitely not working at run-time.
Any idea what's wrong why my DataTemplates are not displaying in pivot item?
Thanks.
This is a partial answer. By this I mean that I did find a work-around to my problem but I did not resolve the issue itself.
My DataTemplateSelector which gets triggered whenever the pivot changes call a extension function called FindResource:
public static class ControlExtensions
{
public static T FindResource<T>(this DependencyObject initial,
string key) where T : DependencyObject
{
DependencyObject current = initial;
while (current != null)
{
if (current is FrameworkElement)
{
if ((current as FrameworkElement).Resources.
ContainsKey(key))
{
return (T)(current as FrameworkElement).Resources[key];
}
}
current = VisualTreeHelper.GetParent(current);
}
if (Application.Current.Resources.ContainsKey(key))
{
return (T)Application.Current.Resources[key];
}
return default(T);
}
}
For some strange reason, Windows Phone 8.1 (WinRT) does not like having the data templates in while it is not a problem in WP8/WP8.1 Silverlight.
As mentioned, this is unstable in the IDE where it sometimes displays the DataTemplate, and sometimes it doesn't depending on whether or not I add a space after the Binding keyword to the Content="{Binding}". One thing for sure is that it never works at run-time, well not at least not with the above code.
VisualTreeHelper.GetParent(current) always returns null no matter what. I've checked at debug time if I somehow could access the resources, but to no avail.
How did I fix it? Well, I moved my data templates to a resource dictionary
<DataTemplate x:Key="MyApp.ViewModel.PIDetailsVM">
<Button Content="test" Foreground="White"></Button>
</DataTemplate>
<DataTemplate x:Key="MyApp.ViewModel.PIListVM">
<Button Content="test" Foreground="White"></Button>
</DataTemplate>
The second I did this, the second part of my FindResources kicks in since the Current object is always null, no matter what
if (Application.Current.Resources.ContainsKey(key))
{
return (T)Application.Current.Resources[key];
}
and it finds the relevant DataTemplate and displays it accordingly in my pivot control based on the relevant PivotItem ViewModel.
Now, I'm not out of the woods yet as I have no idea if binding to the relevant viewmodel will work but that's a whole other story!
If anyone knows why DataTemplate cannot be found when defined in Pages.Resources or Grid.Resources, please update the post as I'd love to know why.
Thanks.

How to restrict a xaml flipview to flipping in a single direction only?

I'm presently using a Windows.UI.Xaml.Controls.FlipView class to display a series of images in a Windows 8 Metro app. In the normal use case, the user can flip back and forth between the images, so a FlipView works wonderfully.
However, there is a specific scenario where the user enters a mode where they can only flip the images forward, i.e. they can't flip backward, until exiting this mode. I've been trying to figure out if there's a simple way to disable flipping backward on a FlipView, but have not found an obvious solution.
Is there a good way to do this using a standard FlipView, or do I have to switch to a different control, or even write a custom one?
What a strange request.
Hopefully, this doesn't seem too simple. It gets the job done in 3 lines!
Here's how (just remove them after they view them):
private void FlipView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var _ItemsCollection = (sender as FlipView).Items;
foreach (var item in e.RemovedItems)
_ItemsCollection.Remove(item);
}
<Grid Background="Black">
<FlipView FontSize="100" SelectionChanged="FlipView_SelectionChanged">
<x:String>0</x:String>
<x:String>1</x:String>
<x:String>2</x:String>
<x:String>3</x:String>
<x:String>4</x:String>
<x:String>5</x:String>
<x:String>6</x:String>
<x:String>7</x:String>
<x:String>8</x:String>
<x:String>9</x:String>
<x:String>10</x:String>
<x:String>11</x:String>
<x:String>12</x:String>
<x:String>13</x:String>
<x:String>14</x:String>
<x:String>15</x:String>
<x:String>16</x:String>
<x:String>17</x:String>
<x:String>18</x:String>
<x:String>19</x:String>
<x:String>20</x:String>
<x:String>21</x:String>
<x:String>22</x:String>
<x:String>23</x:String>
<x:String>24</x:String>
<x:String>25</x:String>
<x:String>26</x:String>
<x:String>27</x:String>
<x:String>28</x:String>
<x:String>29</x:String>
</FlipView>
</Grid>
Best of luck!

Categories

Resources