Timer not working for an automatic CarouselView - c#

I'm making an automatic CarouselView (slider) to slide between images every x seconds. I know there are plenty of examples but none of them helped me to make mine work. So here is the deal:
This is my xaml page:
<CarouselView x:Name="carou_slideshow" Grid.Row="1" IsVisible="False">
<CarouselView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
Whenever the user clicks on the button to start the slideshow, it shows my carousel with its images without any issue but it doesn't change the current image after x seconds. Here's how I've done it :
private void btn_slideShow_Clicked(object sender, EventArgs e)
{
stk_header.IsVisible = false;
stk_options.IsVisible = false;
grid_images.IsVisible = false;
carou_slideshow.IsVisible = true;
carou_slideshow.ItemsSource = sources;
Device.StartTimer(TimeSpan.FromSeconds(5), (Func<bool>)(() =>
{
carou_slideshow.Position = (carou_slideshow.Position + 1) % sources.Count;
return true;
})
);
}
Any idea why ?
EDIT : I realized that in my project I already had a timer in the same class. After removing it and keeping the one in the button method, my application crashes without any error.
EDIT2 : Someone gave me a solution on this post and deleted it after. I tried it and it doesn't work for me.. As I was sure it would, I tried to increment something else and the timer works perfectly fine, but the image doesn't swipe. Also, it can randomly close the application as well as not having any issue.

Related

Maui: different events for single and double tapped

How do I create different methods when double-tap vs. single-tap?
How to recreate:
Take the default "New Maui App"
Add the first two code blocks from this documentation: https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/gestures/tap?view=net-maui-7.0
Then add a second function for single-tap
The MainPage.Xaml now looks like this (truncated after image part since the rest is the same):
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp2.MainPage">
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Image
Source="dotnet_bot.png"
SemanticProperties.Description="Cute dot net bot waving hi to you!"
HeightRequest="200"
HorizontalOptions="Center" >
<!--******* add gesture recognizers to default app ******-->
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="OnTapGestureRecognizerDoubleTapped"
NumberOfTapsRequired="2" />
<TapGestureRecognizer Tapped="OnTapGestureRecognizerSingleTapped"
NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
<!--******** end add gesture recognizers to default app *******-->
</Image>
<Label
Text="Hello, World!"
...
Then in MainPage.xaml.cs, I've added the two methods:
void OnTapGestureRecognizerDoubleTapped(object sender, TappedEventArgs args)
{
CounterBtn.Text = $"Double tapped image";
}
void OnTapGestureRecognizerSingleTapped(object sender, TappedEventArgs args)
{
CounterBtn.Text = $"Single tapped image";
}
Now, when you run and double-click/tap the image, it goes first to the single-tap method, then the double-tap method, then back to the single-tap method.
What is the best way to prevent calls to the single-tap method?
By default, the Image will respond to single taps. So single-tap method would be triggered at first tap. When the NumberOfTapsRequired property is set to greater than one, the event handler will only be executed if the taps occur within a set period of time. If the second (or subsequent) taps don't occur within that period, they're effectively ignored.
So as Jason suggested, it's better to use a single tap gesture and track if a 2nd tap is received within some time span.
based on these comments, I implemented the double-tap vs single-tap like this:
bool doubleTapped;
bool ignoreNextTap;
void OnTapGestureRecognizerDoubleTapped(object sender, TappedEventArgs args)
{
doubleTapped = true;
}
async void OnTapGestureRecognizerSingleTapped(object sender, TappedEventArgs args)
{
var delay = Task.Delay(555);
await delay;
if (ignoreNextTap)
{
ignoreNextTap = false;
return;
}
if (doubleTapped)
{
doubleTapped = false;
ignoreNextTap = true;
CounterBtn.Text = $"Double tapped image";
}
else
{
CounterBtn.Text = $"Single tapped image";
}
}
It works, but it sure seems hacky. Basically, what single tap method sleeps for half-of-a-second, to see if the doubleTapped flag will be raised. If it is, then it calls the code that should be executed for doubleTap ("CounterBtn.Text = $"Double tapped image";"). Otherwise, it calls the singleTap code ("CounterBtn.Text = $"Single tapped image";").
However, in order to prevent the second single click from registering, I put another Boolean for ignoreNextTap. I saw similar posts from other languages (How to customize singleclick to doubleclick? js) and the general approach is the same as this. I guess there is no other way to do this other than the Delay/Sleep approach to see if a second click happens. This will generally cut into the snappy-ness of the UI.

Xamarin forms Carousel view does not allow skipping position

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 :-)

How to set inktoolbar's pen color

As the code shown, I add a ballpointpen, and it support 30 colors, but not enough.
I got colorSelected(Color type) using some other ways, not discuss here.
Now I want to click ballpointPen, using my colorSelected to draw.
How? Thanks.
<Grid>
<InkToolbar TargetInkCanvas="{x:Bind inkCanvas}" InitialControls="AllExceptPens" VerticalAlignment="Top">
<InkToolbarBallpointPenButton x:Name="ballpointPen" Click="xxx_Click"/>
<InkToolbarCustomToolButton x:Name="toolButtonColorPicker" Click="ToolButton_ColorPicker">
<Image Height="20" Width="20" Source="ms-appx:///Assets/Palette.png"/>
<ToolTipService.ToolTip>
<ToolTip Content="ColorPicker"/>
</ToolTipService.ToolTip>
</InkToolbarCustomToolButton>
</InkToolbar>
<InkCanvas x:Name="inkCanvas" Margin="0,48,0,0"/>
</Grid>
The code below seems not working...
private void xxx_Click(object sender, RoutedEventArgs e)
{
if(bUserDefinedColor)
{
InkDrawingAttributes drawingAttributes = inkCanvas.InkPresenter.CopyDefaultDrawingAttributes();
drawingAttributes.Color = colorSelected;
inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
}
}
by the way, I upload the test project to GitHub https://github.com/hupo376787/Test.git
Here is a better solution to your problem, without the need of calling UpdateDefaultDrawingAttributes directly.
What I would do is, whenever the user selects a new color from your ColorPicker and hits OK, add this color to the Palette of the InkToolbarBallpointPenButton, and then set the SelectedBrushIndex to the index of the newly created color.
In way you can completely remove your xxx_Click handler, and replace what's in LeftClick with the following
cpx.LeftClick += (ss, ee) =>
{
bUserDefinedColor = true;
colorSelected = cpx.pickerColor;
ballpointPen.Palette.Add(new SolidColorBrush(colorSelected));
ballpointPen.SelectedBrushIndex = ballpointPen.Palette.Count - 1;
};
This is it! You will see the selected color visual on the pen icon automatically reflects the new color, which gives a great user experience.
Here are two more things you might want to do to further enhance the UX.
Cache the added colors and manually add them back to the Palette at app startup so next time when the user opens the app, they are still available.
Instead of adding another icon to display the ColorPicker, try putting it inside the color popup of the InkToolbarBallpointPenButton so all color related things are in the same place. The control that sits inside this popup is called InkToolbarPenConfigurationControl. You should be able to locate its style (see path below) and add your ColorPicker to it.
C:\Program Files (x86)\Windows
Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.xxxxx.0\Generic\generic.xaml
Hope this helps!

WPF - Dispatcher not working like expected - to slow

I am having a problem with updating the Window of a Wpf-Application. After googling a long time, I broke my problem down to the following example. I hope you can help me.
Let´s say we have a Window with a Button and a TextBlock ... nothing special:
...
<Grid>
<Button ... Click="button_Click"/>
<TextBox x:Name="textBox" Text="DefaultText" .../>
</Grid>
...
The button_Click is a simple event like this:
private void button_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 20; i++)
{
textBox.Dispatcher.Invoke(new Action(() => textBox.Text = i.ToString());
}
}
And here is my Problem. If I debug the loop, the Text of my TextBlock is always the "DefaultText". Only at the end of my loop, the Text is "19". Why is my dispatcher not able to change the Text? Like described here (What's the difference between Invoke() and BeginInvoke()) the Dispatcher should stop my Thread until the job is done.
I tried different DispatcherPriorities ... but no difference. What´s my problem?
Thank you for helping me.
For anyone googling around looking for a solution to their slow WPF application during debugging;
When instantiating the DispatcherTimer object use DispatcherPriority.Send as an argument. This sets the priority of your UI higher, making your application or game run smoother while debugging. Source
DispatcherTimer gameTimer = new DispatcherTimer(DispatcherPriority.Send);
I hope this will help out someone else in the future :)

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