Xamarin: get if a pangesture touch is inside a certain element - xamarin.forms

Is there a way to check if a touch of a pangesture is inside a certain element, like a Frame?
I added a PanGestureRecognizer to my screen. In my screen I have two Frame containing two Image. What I want is that when I move my finger on the screen I got noticed when my finger ends on one of that frames. I know how to get my finger coordinates (e.TotalX; e.TotalY;) and my thought was to check if they are inside the bounds of the frames, but I don't know how.
Any suggestion?
CODE
public MainPage()
{
InitializeComponent();
var panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += PanUpdated;
mainLayout.GestureRecognizers.Add(panGesture);
dog = new Frame
{
BorderColor = Color.Blue,
Padding = 4,
Content = new Image
{
Source = ImageSource.FromResource("AccessibilityImagesSound.Immagini.dog.png"),
Aspect = Aspect.Fill,
},
};
AutomationProperties.SetIsInAccessibleTree(dog, true);
AutomationProperties.SetName(dog, "dog");
AbsoluteLayout.SetLayoutBounds(dog, new Rectangle(0.2, 0.5, 0.30, 0.30));
AbsoluteLayout.SetLayoutFlags(dog, AbsoluteLayoutFlags.All);
mainLayout.Children.Add(dog);
cat = new Frame
{
BorderColor = Color.Blue,
Padding = 4,
Content = new Image
{
Source = ImageSource.FromResource("AccessibilityImagesSound.Immagini.cat.png"),
Aspect = Aspect.Fill,
},
};
AutomationProperties.SetIsInAccessibleTree(cat, true);
AutomationProperties.SetName(cat, "cat");
AbsoluteLayout.SetLayoutBounds(cat, new Rectangle(0.8, 0.5, 0.30, 0.30));
AbsoluteLayout.SetLayoutFlags(cat, AbsoluteLayoutFlags.All);
mainLayout.Children.Add(cat);
}
private void PanUpdated(object sender, PanUpdatedEventArgs e)
{
switch (e.StatusType)
{
case GestureStatus.Started:
break;
case GestureStatus.Running:
//Here I think I have to check E.TotalX and e.TotalY
break;
case GestureStatus.Completed:
case GestureStatus.Canceled:
break;
}
}

As far as I know the only way to get location of Touch in xamarin is through native touch gestures.
Native gestures can be used using the Effects and this MSDocs link has full implementation of it.
Using the TouchEffect the exact location of the touch can be fetched easily. A sample to change color of the touched frame is given below.
XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.Effects>
<touch:TouchEffect TouchAction="Grid_TouchAction"/>
</Grid.Effects>
<Frame
Grid.Row="1"
BackgroundColor="Green"
x:Name="leftFrame"/>
<Frame
Grid.Row="1"
Grid.Column="1"
BackgroundColor="Blue"
x:Name="rightFrame"/>
</Grid>
CS :
private void Grid_TouchAction(object sender, TouchTracking.TouchActionEventArgs args)
{
switch (args.Type)
{
case TouchActionType.Moved:
if (leftFrame.Bounds.Contains(args.Location))
{
leftFrame.BackgroundColor = Color.Red;
}
else
{
leftFrame.BackgroundColor = Color.Green;
}
if (rightFrame.Bounds.Contains(args.Location))
{
rightFrame.BackgroundColor = Color.Red;
}
else
{
rightFrame.BackgroundColor = Color.Blue;
}
break;
}
}
UI working:
Change the TouchAction event handler as per your requirement.

Related

How to make CollectionView's item loop when scrolling in Xamarin

I am using CollectionView to display data by scroll. However, there is a problem that after the data, it scrolls iteratively? Means after I reach the last element, it will show me the first element again. I know CarouselView has a property called loop for that. However, for some reason, I don't use CarouselView. This is the code I used:
PageOne.xaml
<CollectionView x:Name="_data" HeightRequest="115" ItemsUpdatingScrollMode="KeepItemsInView" HorizontalScrollBarVisibility="Never" VerticalScrollBarVisibility="Never" Scrolled="_data_Scrolled">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal" SnapPointsType="MandatorySingle" SnapPointsAlignment="Start" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
...
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
PageOne.xaml.cs
Do I autorun items in CollectionView
....
Device.StartTimer(TimeSpan.FromSeconds(4), (Func<bool>)(() =>
{
_data.ScrollTo(listData.Count + 2, -1, ScrollToPosition.Start, true);
//return true;
}));
In my code, check the _data_Scrolled event when the last element is reached
private void _data_Scrolled(object sender, ItemsViewScrolledEventArgs e)
{
if(e.LastVisibleItemIndex + 1 == countHotSeling)
{
Device.StartTimer(TimeSpan.FromSeconds(4), (Func<bool>)(() =>
{
_data.ScrollTo(listData.Count - 2, +1, ScrollToPosition.Start, true);
return true;
}));
}
}
When t debug, it actually jumps to the event when the last element is reached. However, it does not loop?
Looking forward to everyone's help. Thank you!
You can use the ​RemainingItemsThresholdReached​ event to load more items by setting the ​RemainingItemsThreshold​ parameter when reaching the last item. However, since the items in Collectionview are not listed in order so we can't reach the last element.We only can replicate the _data.
Here is the code sample below for your reference:
public Test2()
{
InitializeComponent();
LoadData();
_data.RemainingItemsThreshold = 13;
_data.RemainingItemsThresholdReached += _data_RemainingItemsThresholdReached;
}
private async void _data_RemainingItemsThresholdReached(object sender, EventArgs e)
{
var monkeyJson = await httpClient.GetStringAsync(monkeyUrl);
var monkeys = JsonConvert.DeserializeObject<Monkey[]>(monkeyJson);
foreach (var monkey in monkeys)
{
Monkeys.Add(monkey);
count++;
}
_listProd = Monkeys.ToList();
_data.ItemsSource = _listProd;
}
Reference link:https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/populate-data#load-data-incrementally

An emptyView for loading data and another for when there is no data available

I have a case of using a CarouselView that is displayed based on certain data brought from an API, the point is that I need to see a certain view or at least text while the API data is being downloaded and another one in case That there is no data.
I tried to get to this using RefreshView and EmptyView but I cannot achieve the required behavior, I can make an EmptyView appear immediately the data begins to load since at that moment the ItemSource is null, then when the data reaches the app the Carousel appears , which seems to me quite ugly, the ideal would be to show some view that next to the RefreshView indicator shows that the data is loading and then in case of not bringing any data show a view that of the feedback that API data did not return .
I hope I have made myself understood and I hope someone can give me an idea on how to achieve this behavior.
MyViewModel:
public MyViewModel()
{
IsRefreshing = true;
Things = new ObservableCollection<Things>();
var t = Task.Run(async () =>
{
await LoadThings();
});
Task.WhenAll(t);
IsRefreshing = false;
}
private async Task LoadThings()
{
Things = new List<Thing>(await App.WebApiManager.GetThingsAsync(Id));
}
My IsRefreshing property is linked to the IsRefreshing property in the RefreshView that encompasses my CarouselView
I think you could use two empty view and switch between them when the refreshing status changes, and here is the code:
add two content view in in XAML and set default empty view to LoadingData:
<ContentPage.Resources>
<ContentView x:Key="LoadingData">
<StackLayout>
<Label Text="Loading data..."
Margin="10,25,10,10"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Fill"
HorizontalTextAlignment="Center" />
</StackLayout>
</ContentView>
<ContentView x:Key="NoDataLoaded">
<StackLayout>
<Label Text="No items to display."
Margin="10,25,10,10"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Fill"
HorizontalTextAlignment="Center" />
</StackLayout>
</ContentView>
</ContentPage.Resources>
<StackLayout Margin="20">
<RefreshView IsRefreshing="{Binding IsRefreshing}"
Command="{Binding RefreshCommand}">
<CarouselView x:Name="carouselView"
EmptyView="{StaticResource LoadingData}">
... ...
and in code, show different empty view accordingly:
public partial class HorizontalPullToRefreshPage : ContentPage
{
AnimalsViewModel viewModel;
public HorizontalPullToRefreshPage()
{
InitializeComponent();
viewModel = new AnimalsViewModel();
this.BindingContext = viewModel;
viewModel.PropertyChanged += ViewModel_PropertyChanged;
}
private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName.Equals("IsRefreshing"))
{
if (viewModel.IsRefreshing && viewModel.Animals.Count==0)
{
carouselView.EmptyView = Resources["LoadingData"];
}
else if (!viewModel.IsRefreshing && viewModel.Animals.Count == 0)
{
carouselView.EmptyView = Resources["NoDataLoaded"];
}
}
}
protected override async void OnAppearing()
{
base.OnAppearing();
await Task.Delay(2000);
carouselView.ItemsSource = viewModel.Animals;
}
}
then, every time the property IsRefreshing changed, you got a chance to switch the empty view.
Hope it helps.

Styling the list view of Picker class

I am sorry to be asking what seems really obvious question but I've been unable to set the properties (e.g. background color) of the list view / contents when using the picker
The Picker properties style what you see before you expand the list to select contents, but I cannot see or find how to affect the latter
In this example from my XAML the collapsed view of the Picker is styled correctly, but when it opens the background is white / transparent
Sorry, I have looked in many links and just can't find the info
<Picker
VerticalOptions="CenterAndExpand"
Grid.Column="1"
Grid.Row="1"
Title="PICKER"
BackgroundColor="Transparent"
TitleColor="White"
FontSize="Medium"
Style="{StaticResource AlphabetPicker}"
x:Name="AlphabetPicker"
ItemsSource="{Binding Alphabet}"
SelectedIndexChanged="GetLetterSelected"
HorizontalOptions="Start">
You could use custom renderer.
I follow the code in the link: Customize the Xamarin.Forms Picker Popup List
MyPicker.cs
public class MyPicker : Xamarin.Forms.Picker
{
}
MyPickerRenderer.cs
[assembly: ExportRenderer(typeof(MyPicker), typeof(MyPickerRenderer))]
namespace XamarinDemo.Droid.Renderer
{
class MyPickerRenderer : PickerRenderer
{
IElementController ElementController => Element as IElementController;
public MyPickerRenderer(Context context) : base(context)
{
}
private AlertDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Picker> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || e.OldElement != null)
return;
Control.Click += Control_Click;
}
protected override void Dispose(bool disposing)
{
Control.Click -= Control_Click;
base.Dispose(disposing);
}
private void Control_Click(object sender, EventArgs e)
{
Xamarin.Forms.Picker model = Element;
var picker = new NumberPicker(Context);
if (model.Items != null && model.Items.Any())
{
// set style here
picker.MaxValue = model.Items.Count - 1;
picker.MinValue = 0;
picker.SetBackgroundColor(Android.Graphics.Color.Yellow);
picker.SetDisplayedValues(model.Items.ToArray());
picker.WrapSelectorWheel = false;
picker.Value = model.SelectedIndex;
}
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.AddView(picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
builder.SetTitle(model.Title ?? "");
builder.SetNegativeButton("Cancel ", (s, a) =>
{
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
_dialog = null;
});
builder.SetPositiveButton("Ok ", (s, a) =>
{
ElementController.SetValueFromRenderer(Xamarin.Forms.Picker.SelectedIndexProperty, picker.Value);
// It is possible for the Content of the Page to be changed on SelectedIndexChanged.
// In this case, the Element & Control will no longer exist.
if (Element != null)
{
if (model.Items.Count > 0 && Element.SelectedIndex >= 0)
Control.Text = model.Items[Element.SelectedIndex];
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is also possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
}
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (ssender, args) =>
{
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
};
_dialog.Show();
}
}
}
Xaml:
<StackLayout>
<local:MyPicker x:Name="picker"
Title="Select a monkey"
TitleColor="Red">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Baboon</x:String>
<x:String>Capuchin Monkey</x:String>
<x:String>Blue Monkey</x:String>
<x:String>Squirrel Monkey</x:String>
<x:String>Golden Lion Tamarin</x:String>
<x:String>Howler Monkey</x:String>
<x:String>Japanese Macaque</x:String>
</x:Array>
</Picker.ItemsSource>
</local:MyPicker>
</StackLayout>

Xamarin forms: How to add events in `XamForms.Controls.Calendar`?

I am using XamForms.Controls.Calendar for showing calendar in my application, I have added this package in all platforms.
Added following codes in xaml:
<StackLayout>
<controls:Calendar
HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand"
x:Name="calendar"
DateClicked="CurrentDate"/>
</StackLayout>
In c#:
XamForms.Controls.Calendar calendar = new XamForms.Controls.Calendar()
{
WidthRequest = 300,
HeightRequest = 300
};
}
public async void CurrentDate(Object sender, EventArgs args)
{
var dateSelect = calendar.SelectedDate;
}
I need to add events for dates in the calendar like school day, school mass or exam(Please see the screenshot added below). Is this possible in XamForms.Controls.Calendar?
This plugin can add special dates: https://github.com/rebeccaXam/XamForms.Controls.Calendar/wiki/SpecialDates
calendar.SpecialDates = new List<SpecialDate>
{
new SpecialDate(DateTime.Now.AddDays(3))
{
Selectable = true,
BackgroundPattern = new BackgroundPattern(1)
{
Pattern = new List<Pattern>
{
new Pattern { WidthPercent = 1f, HightPercent = 0.6f, Color = Color.Transparent },
new Pattern{ WidthPercent = 1f, HightPercent = 0.4f, Color = Color.Transparent, Text = "Mass", TextColor=Color.Black, TextSize=11, TextAlign=TextAlign.Middle},
}
}
}
};
Is this effect what you want?

How to use both GestureRecognizers and Effects in Xamarin Forms at the same time?

I have added a tap gesture recognizer to my StackLayout and I want to change the background color when it is tapped so that the user recognizes that the layout has been tapped
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
Tapped="Preferences_Clicked"
NumberOfTapsRequired="1"/>
</StackLayout.GestureRecognizers>
Should I use animation for this?
UPDATE:
by changing background color, I mean an effect, something like a highlight, just like when you selsct an item in a ListView
Use this code for toggle color
int tapCount=0;
void Preferences_Clicked(object sender, EventArgs args)
{
tapCount++;
var stackLayout = (StackLayout)sender;
if (tapCount % 2 == 0) {
stackLayout.BackgroundColor = Color.Default;
} else {
stackLayout.BackgroundColor = Color.Accent;
}
}
I could find my answer, I simply added this bit of code to the Tapped method, and got what I wanted
public async void Preferences_Clicked(object sender, EventArgs e)
{
const int _animationTime = 50;
try
{
var layout = (StackLayout)sender;
await layout.FadeTo(0.5, _animationTime);
await layout.FadeTo(1, _animationTime);
}
catch (Exception ex)
{
}
}

Resources