create a swipe view programmatically xamarin.forms - xamarin.forms

i am trying to implement a swipe view that performs action when swiped using Mode=Execute. the code in xaml works properly:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="swipe.MainPage">
<StackLayout x:Name="hg">
<SwipeView Threshold="100">
<SwipeView.LeftItems>
<SwipeItems Mode="Execute">
<SwipeItem Text="Delete"
IconImageSource="delete.png"
BackgroundColor="LightPink"
Invoked="SwipeItem_Invoked"
/>
</SwipeItems>
</SwipeView.LeftItems>
<!-- Content -->
<Grid HeightRequest="60"
WidthRequest="300"
BackgroundColor="LightGray">
<Label Text="Swipe right"
HorizontalOptions="Center"
VerticalOptions="Center" />
</Grid>
</SwipeView>
</StackLayout>
</ContentPage>
xaml.cs
public MainPage()
{
InitializeComponent();
}
private void SwipeItem_Invoked(object sender, EventArgs e)
{
DisplayAlert("xj", "cc", "dd");
}
but when i try to create the same swipe view programmatically the function when swipe item is invoked doesn't work. here is the code:
public MainPage()
{
InitializeComponent();
Grid grid = new Grid
{
HeightRequest = 60,
WidthRequest = 300,
BackgroundColor = Color.LightGray
};
grid.Children.Add(new Label
{
Text = "Swipe right",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center
});
SwipeItem delete = new SwipeItem
{
Text = "Remove",
BackgroundColor = Color.Pink
};
SwipeItems items = new SwipeItems
{
Mode = SwipeMode.Execute
};
items.Add(delete);
SwipeView swipe = new SwipeView
{
Threshold = 80,
LeftItems = new SwipeItems(items),
Content = grid
};
delete.Invoked += SwipeItem_Invoked;
hg.Children.Add(swipe);
}
private void SwipeItem_Invoked(object sender, EventArgs e)
{
DisplayAlert("xj", "cc", "dd");
}
why doesn't it work though i specified the mode to be execute. what am i doing wrong? thanks in advance

Here is the full code I tested:
MainPage.xaml
<StackLayout x:Name="hg">
</StackLayout>
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
Grid grid = new Grid
{
HeightRequest = 60,
WidthRequest = 300,
BackgroundColor = Color.LightGray
};
grid.Children.Add(new Label
{
Text = "Swipe right",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center
});
SwipeItem delete = new SwipeItem
{
Text = "Remove",
BackgroundColor = Color.Pink
};
SwipeItems items = new SwipeItems
{
Mode = SwipeMode.Execute
};
items.Add(delete);
SwipeView swipe = new SwipeView
{
Threshold = 80,
LeftItems = new SwipeItems(items),
Content = grid
};
delete.Invoked += SwipeItem_Invoked;
hg.Children.Add(swipe);
}
private void SwipeItem_Invoked(object sender, EventArgs e)
{
DisplayAlert("xj", "cc", "dd");
}
}

Related

Move control freely inside AbsoluteLayout in .Net Maui

I'm trying to make a layout that allow me to move controls inside it freely, I found a working solution but it has a very strange behavior, when I try to move the label, the movement is very laggy and sometimis it has an effect like it duplicate the label.
I implemented the movement with a PanGestureRecognizer adding labels inside an AbsoluteLayout programatically with a button event
This is the XAML, with the empty AbsoluteLayout and the button at the end to add de label
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Views.MoveControlsView"
Title="MoveControlsView">
<StackLayout>
<AbsoluteLayout
x:Name="ParentLayout"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
</AbsoluteLayout>
<StackLayout
HorizontalOptions="FillAndExpand"
VerticalOptions="End">
<Button
x:Name="AddLabel"
Text="Add label"
Clicked="AddLabel_Clicked"/>
</StackLayout>
</StackLayout>
</ContentPage>
This is the code behind, I generate a Label when the button is clicked and add to it the PanGestureRecognizer that I also suscribed it to the PanUpdated event.
public partial class MoveControlsView : ContentPage
{
public MoveControlsView()
{
InitializeComponent();
}
private void AddLabel_Clicked(object sender, EventArgs e)
{
var label = new Label()
{
Text = "This is a label",
BackgroundColor = Colors.LightGray,
Padding = 10
};
var panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += PanGestureRecognizer_PanUpdated;
label.GestureRecognizers.Add(panGesture);
ParentLayout.Children.Add(label);
}
private void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e)
{
var label = sender as Label;
switch (e.StatusType)
{
case GestureStatus.Running:
label.TranslationX = e.TotalX;
label.TranslationY = e.TotalY;
break;
case GestureStatus.Completed:
label.TranslateTo(label.TranslationX, label.TranslationY);
break;
}
}
}
You could make changes to the code in PanGestureRecognizer_PanUpdated event handler. Try the following code:
double tempx = 0;
double tempy = 0;
private void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e)
{
var label = sender as Label;
switch (e.StatusType)
{
case GestureStatus.Started:
if(Device.RuntimePlatform == Device.iOS)
{
tempx = label.TranslationX;
tempy = label.TranslationY;
}
break;
case GestureStatus.Running:
if (Device.RuntimePlatform == Device.iOS)
{
label.TranslationX = e.TotalX + tempx;
label.TranslationY = e.TotalY + tempy;
}
else if (Device.RuntimePlatform == Device.Android)
{
label.TranslationX += e.TotalX;
label.TranslationY += e.TotalY;
}
break;
case GestureStatus.Completed:
tempx = label.TranslationX;
tempy = label.TranslationY;
break;
}
}
For more information, you could refer to Xamarin.Forms AbsoluteLayout and Add a pan gesture recognizer
Hope it works for you.

How to change TabbedPage icon and title and page with selected , Switch between two pages

I want to change the tab icon and page after selected, I have 2 pages but I want to change with 1 tab and change the icon and page after selected, how can I do that?
public MainPage()
{
InitializeComponent();
var login = new NavigationPage(new login())
{
Title = "login",
Icon = "login.png"
};
var register = new NavigationPage(new register())
{
Title = "register",
Icon = "register.png"
};
if(CurrentPage is register)
{
Children.Add(login);
}
else
{
Children.Add(register);
}
this.CurrentPageChanged += (object sender, EventArgs e) =>
{
var i = this.Children.IndexOf(this.CurrentPage);
if (i == 0)
{
login.Title = "login";
login.Icon = "login.png";
}
else
{
register.Title = "register";
register.Icon = "register.png";
}
};
You can create two layout and use button to switch between the two layouts:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
Content = firstlayout();
}
public StackLayout firstlayout() {
StackLayout stacklayout = new StackLayout
{
Margin = new Thickness(20),
Children =
{
new Label { Text = "Primary colors" },
new BoxView { Color = Color.Red },
new BoxView { Color = Color.Yellow },
new BoxView { Color = Color.Blue },
}
};
Button button = new Button
{
Text = "Click to change content2",
VerticalOptions = LayoutOptions.EndAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
HeightRequest = 60,
BackgroundColor = Color.Green,
TextColor = Color.White
};
button.Clicked += (sender, args) => this.Content = secondlayout();
stacklayout.Children.Add(button);
return stacklayout;
}
public StackLayout secondlayout()
{
StackLayout stacklayout = new StackLayout
{
Margin = new Thickness(20),
Children =
{
new Label { Text = "Secondary colors" },
new BoxView { Color = Color.Green },
new BoxView { Color = Color.Orange },
new BoxView { Color = Color.Purple }
}
};
Button button = new Button
{
Text = "Click to change content1",
VerticalOptions = LayoutOptions.EndAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
HeightRequest = 60,
BackgroundColor= Color.Green,
TextColor = Color.White
};
button.Clicked += (sender, args) => this.Content = firstlayout();
stacklayout.Children.Add(button);
return stacklayout;
}
}
Result:
Or you can change Application.Current.MainPage to different pages:
private void Button_Clicked(object sender, EventArgs e)
{
Application.Current.MainPage = new MainPage();
//Or
Application.Current.MainPage = new LoginPage();
}

Xamarin Boolean Bindable Property

I am trying to make clickable Icon which will be using for Wish List, for this I have created boolean property which will return Image.
This is my code, but it does not support onClick event, Please advise to figure out this problem.
public class WishIconImg : Image, IDisposable
{
static FontImageSource unselected_source = new FontImageSource();
static FontImageSource selected_source = new FontImageSource();
public WishIconImg()
{
unselected_source.FontFamily = "FA-S";
unselected_source.Glyph = "\U000f02d5";
unselected_source.Color = Color.DarkOrange;
selected_source.FontFamily = "FA-S";
selected_source.Glyph = "\U000f02d1";
selected_source.Color = Color.DarkOrange;
OnClick += Checkbox_OnClick;
}
public static BindableProperty IsCheckedProperty = BindableProperty.Create(
nameof(IsChecked), typeof(bool), typeof(WishIconImg), defaultBindingMode: BindingMode.TwoWay,
propertyChanged: IsCheckedChanged);
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
private static void IsCheckedChanged(BindableObject bindable, object oldValue, object newValue)
{
var cb = (WishIconImg)bindable;
if (cb == null)
return;
if ((bool)newValue)
{
cb.Source = selected_source;
}
else
{
cb.Source=unselected_source ;
}
}
void Checkbox_OnClick(object sender, EventArgs e)
{
IsChecked = !IsChecked;
}
public void Dispose()
{
OnClick -= Checkbox_OnClick;
}
}
}
Xaml
<controls:WishIconImg x:Name="HeartChk" IsChecked="{Binding AddWish, Mode=TwoWay}" HeightRequest="35" WidthRequest="35" HorizontalOptions="End"/>
Even I have tried with Label property but it doesnt work
You could modify the class like following
public WishIconImg()
{
unselected_source.FontFamily = "FA-S";
unselected_source.Glyph = "\U000f02d5";
unselected_source.Color = Color.DarkOrange;
selected_source.FontFamily = "FA-S";
selected_source.Glyph = "\U000f02d1";
selected_source.Color = Color.DarkOrange;
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.Tapped += (s, e) => {
// handle the tap
IsChecked = !IsChecked;
};
this.GestureRecognizers.Add(tapGestureRecognizer);
}
Try adding TapGestureRecognizer for an click event
Do something like this
<StackLayout HeightRequest="35" WidthRequest="35" HorizontalOptions="End">
<controls:WishIconImg x:Name="HeartChk" IsChecked="{Binding AddWish, Mode=TwoWay}" />
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Checkbox_OnClick}" />
</StackLayout.GestureRecognizers>
</StackLayout>
In your ViewModel Bind the command for it
public System.Windows.Input.ICommand Checkbox_OnClick => new Xamarin.Forms.Command(Checkbox_OnClickTapped);
Checkbox_OnClickTapped will be your method called when your view will be clicked

Flexlayout does not work with command and command parameter

I am being trying to use the flexlayout and it's great but now for each item in the stack layout i need to add tapped gesturer,command etc..
Whatever I try does not work.The only thing that works is the stacklayout.TapGestureRecognizer but as soon as I try to use the command does not work.
I even tried the https://taubensee.net/adding-touch-events-to-flexlayouts/
and added a commandparameter bindable property but does not work either.
How do you add a command with commandparameter to flexlayout .below is my code
<FlexLayout BindableLayout.ItemsSource="{Binding Customers}"
AlignContent="Start"
AlignItems="Start"
Direction="Row"
JustifyContent="Start"
Wrap="Wrap">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout
FlexLayout.AlignSelf="Start"
FlexLayout.Basis="50%">
<!--<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
</StackLayout.GestureRecognizers>-->
<Frame>
<!--<Frame.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding ItemTappedCommand}"
CommandParameter="{Binding .}" />
</Frame.GestureRecognizers>-->
<StackLayout>
<Label Text="whatever"></Label>
<!--<Image Source="myimage.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ItemTappedCommand}" CommandParameter="AAAA"
NumberOfTapsRequired="1"></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>-->
</StackLayout>
</Frame>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
<!--<FlexLayout.Behaviors>
<behaviors:FlexLayoutItemTappedBehavior
Command="{Binding ItemTappedCommand2}" CommandParameter="{Binding .}"/>
</FlexLayout.Behaviors>-->
</FlexLayout>
FlexLayout maybe miss the touch events and commands , have a try with Behavior to realize it.
Create a new class that inherits from Behavior<T>:
public class FlexLayoutItemTappedBehavior : Behavior<FlexLayout>
{
public static readonly BindableProperty CommandProperty =
BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(FlexLayoutItemTappedBehavior), defaultBindingMode: BindingMode.OneWay);
public static readonly BindableProperty ParamaterProperty =
BindableProperty.Create(nameof(Paramater), typeof(object), typeof(FlexLayoutItemTappedBehavior), defaultBindingMode: BindingMode.OneWay);
public ICommand Command
{
get => (ICommand)this.GetValue(CommandProperty);
set => this.SetValue(CommandProperty, value);
}
public object Paramater
{
get => (object)this.GetValue(ParamaterProperty);
set => this.SetValue(ParamaterProperty, value);
}
protected override void OnAttachedTo(FlexLayout bindable)
{
base.OnAttachedTo(bindable);
if (bindable.BindingContext != null)
{
this.BindingContext = bindable.BindingContext;
}
bindable.BindingContextChanged += this.OnFlexLayoutBindingChanged;
bindable.ChildAdded += this.OnFlexLayoutChildAdded;
}
protected override void OnDetachingFrom(FlexLayout bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= this.OnFlexLayoutBindingChanged;
bindable.ChildAdded -= this.OnFlexLayoutChildAdded;
foreach (var child in bindable.Children)
{
if (child is View childView && childView.GestureRecognizers.Any())
{
var tappedGestureRecognizers = childView.GestureRecognizers.Where(x => x is TapGestureRecognizer).Cast<TapGestureRecognizer>();
foreach (var tapGestureRecognizer in tappedGestureRecognizers)
{
tapGestureRecognizer.Tapped -= this.OnItemTapped;
childView.GestureRecognizers.Remove(tapGestureRecognizer);
}
}
}
}
private void OnFlexLayoutBindingChanged(object sender, EventArgs e)
{
if (sender is FlexLayout flexLayout)
{
this.BindingContext = flexLayout.BindingContext;
}
}
private void OnFlexLayoutChildAdded(object sender, ElementEventArgs args)
{
if (args.Element is View view)
{
var tappedGestureRecognizer = new TapGestureRecognizer();
tappedGestureRecognizer.Tapped += this.OnItemTapped;
view.GestureRecognizers.Add(tappedGestureRecognizer);
}
}
private async void OnItemTapped(object sender, EventArgs e)
{
if (sender is VisualElement visualElement)
{
var animations = new List<AnimationBase>();
var scaleIn = new ScaleToAnimation
{
Target = visualElement,
Scale = .95,
Duration = "50"
};
animations.Add(scaleIn);
var scaleOut = new ScaleToAnimation
{
Target = visualElement,
Scale = 1,
Duration = "50"
};
animations.Add(scaleOut);
var storyBoard = new StoryBoard(animations);
await storyBoard.Begin();
}
if (sender is BindableObject bindable && this.Command != null && this.Command.CanExecute(null))
{
object resolvedParameter;
if (Paramater != null)
{
resolvedParameter = Paramater;
}
else
{
resolvedParameter = e;
}
if (Command.CanExecute(resolvedParameter))
{
this.Command.Execute(bindable.BindingContext);
}
}
}
}
Lastly, in order to use this Behavior from XAML, you can reference it like this:
<FlexLayout.Behaviors>
<behaviors:FlexLayoutItemTappedBehavior
Command="{Binding NavigateToDetailCommand}" Paramater="{Binding .}"/>
</FlexLayout.Behaviors>
About Reusable EventToCommandBehavior , you can refer to here .

Xamarin Forms custom stepper

I am trying to make a custom stepper to use in my listview such as this one
Any idea how to do this? Thanks.
Solution 1:
A Stepper allows inputting a discrete value that is constrained to a range. You could display the value of the Stepper using data binding in a label as follows :
Define in XAML:
<StackLayout x:Name="Container">
<Label BindingContext="{x:Reference stepper}" Text="{Binding Value}" />
<Stepper Minimum="0" Maximum="10" x:Name="stepper" Increment="0.5" />
</StackLayout>
Solution 2:
You could create a BindableProperty to implement this function, for example:
public class CustomStepper : StackLayout
{
Button PlusBtn;
Button MinusBtn;
Entry Entry;
public static readonly BindableProperty TextProperty =
BindableProperty.Create(
propertyName: "Text",
returnType: typeof(int),
declaringType: typeof(CustomStepper),
defaultValue: 1,
defaultBindingMode: BindingMode.TwoWay);
public int Text
{
get { return (int)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public CustomStepper()
{
PlusBtn = new Button { Text = "+", WidthRequest = 40, FontAttributes = FontAttributes.Bold, FontSize = 15 };
MinusBtn = new Button { Text = "-", WidthRequest = 40, FontAttributes = FontAttributes.Bold, FontSize = 15 };
switch (Device.RuntimePlatform)
{
case Device.UWP:
case Device.Android:
{
PlusBtn.BackgroundColor = Color.Transparent;
MinusBtn.BackgroundColor = Color.Transparent;
break;
}
case Device.iOS:
{
PlusBtn.BackgroundColor = Color.DarkGray;
MinusBtn.BackgroundColor = Color.DarkGray;
break;
}
}
Orientation = StackOrientation.Horizontal;
PlusBtn.Clicked += PlusBtn_Clicked;
MinusBtn.Clicked += MinusBtn_Clicked;
Entry = new Entry
{
PlaceholderColor = Color.Gray,
Keyboard = Keyboard.Numeric,
WidthRequest = 40, BackgroundColor = Color.FromHex("#3FFF")
};
Entry.SetBinding(Entry.TextProperty, new Binding(nameof(Text), BindingMode.TwoWay, source: this));
Entry.TextChanged += Entry_TextChanged;
Children.Add(PlusBtn);
Children.Add(Entry);
Children.Add(MinusBtn);
}
private void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
if (!string.IsNullOrEmpty(e.NewTextValue))
this.Text = int.Parse(e.NewTextValue);
}
private void MinusBtn_Clicked(object sender, EventArgs e)
{
if (Text > 1)
Text--;
}
private void PlusBtn_Clicked(object sender, EventArgs e)
{
Text++;
}
}
For more detailed information, please refer to the following documents:
Stepper in Xamarin Forms
Stepper Control In Xamarin.Forms Application For Android And UWP
C# (CSharp) Xamarin.Forms.Stepper Code Examples
Xamarin Forms Guide -- Stepper
Update:
In the CustomStepper class, the Entry value is binding with the Text property, so you could get the value of the entry via customStepper.Text.
For example:
<local:CustomStepper x:Name="MyCustomerStepper"/>
You could get its Entry value in your xaml.cs file via:
var yourCustomerStepperEntryValue = MyCustomerStepper.Text.ToString();

Resources