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();
Related
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.
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");
}
}
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
How to add the Title "State" in the middle of the ToolBar for the PickerRenderer in Xamarin ios(Forms) ?
You could check the following code
using System;
using xxx.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(Picker), typeof(MyiOSPickerRenderer))]
namespace xxx.iOS
{
public class MyiOSPickerRenderer : PickerRenderer, IUIPickerViewDelegate, IUIPickerViewDataSource
{
string SelectedValue;
public MyiOSPickerRenderer()
{
}
public nint GetComponentCount(UIPickerView pickerView)
{
return 1;
}
public nint GetRowsInComponent(UIPickerView pickerView, nint component)
{
return Element.Items.Count;
}
[Export("pickerView:viewForRow:forComponent:reusingView:")]
public UIView GetView(UIPickerView pickerView, nint row, nint component, UIView view)
{
UILabel label = new UILabel
{
//here you can set the style of item!!!
TextColor = UIColor.Blue,
Text = Element.Items[(int)row].ToString(),
TextAlignment = UITextAlignment.Center,
};
return label;
}
[Export("pickerView:didSelectRow:inComponent:")]
public void Selected(UIPickerView pickerView, nint row, nint component)
{
Control.Text = Element.Items[(int)row];
SelectedValue = Element.Items[(int)row];
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (Control != null)
{
SelectedValue = Element.Items[0];
UIPickerView pickerView = (UIPickerView)Control.InputView;
pickerView.WeakDelegate = this;
pickerView.DataSource = this;
UIToolbar toolbar = (UIToolbar)Control.InputAccessoryView;
UIBarButtonItem save = new UIBarButtonItem("Save", UIBarButtonItemStyle.Done, (object sender, EventArgs click) =>
{
Control.Text = SelectedValue;
toolbar.RemoveFromSuperview();
pickerView.RemoveFromSuperview();
Control.ResignFirstResponder();
});
UIBarButtonItem Title = new UIBarButtonItem("States", UIBarButtonItemStyle.Done, null);
UIBarButtonItem cancel = new UIBarButtonItem("Cancel", UIBarButtonItemStyle.Bordered, (object sender, EventArgs click) =>
{
toolbar.RemoveFromSuperview();
pickerView.RemoveFromSuperview();
Control.ResignFirstResponder();
});
UIBarButtonItem empty = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace, null);
toolbar.Items = new UIBarButtonItem[] { cancel, empty, Title, empty, save };
}
}
}
}
I implemented this CustomStepper:
using System;
using Xamarin.Forms;
namespace AppXamarin
{
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: 0,
defaultBindingMode: BindingMode.TwoWay);
public int Text
{
get { return (int)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public CustomStepper()
{
PlusBtn = new Button { WidthRequest = 30, HeightRequest = 30 };
MinusBtn = new Button { WidthRequest = 30, HeightRequest = 30 };
PlusBtn.Image = "exp20181029Artboard51";
MinusBtn.Image = "exp20181029Artboard52";
switch (Device.RuntimePlatform)
{
case Device.UWP:
case Device.Android:
{
PlusBtn.BackgroundColor = Color.Transparent;
MinusBtn.BackgroundColor = Color.Transparent;
break;
}
case Device.iOS:
{
PlusBtn.BackgroundColor = Color.Transparent;
MinusBtn.BackgroundColor = Color.Transparent;
break;
}
}
Orientation = StackOrientation.Horizontal;
PlusBtn.Clicked += PlusBtn_Clicked;
MinusBtn.Clicked += MinusBtn_Clicked;
Entry = new Entry { PlaceholderColor = Color.Gray, Keyboard = Keyboard.Numeric, WidthRequest = 30, BackgroundColor = Color.Transparent, FontSize = 15 };
Entry.Keyboard = Keyboard.Numeric;
Entry.Behaviors.Add(new NumericValidationBehavior());
Entry.SetBinding(Entry.TextProperty, new Binding(nameof(Text), BindingMode.TwoWay, source: this));
Entry.HorizontalTextAlignment = TextAlignment.Center;
Entry.TextChanged += Entry_TextChanged;
Children.Add(MinusBtn);
Children.Add(Entry);
Children.Add(PlusBtn);
}
private void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
if (!string.IsNullOrEmpty(e.NewTextValue) && e.NewTextValue != ".")
this.Text = int.Parse(e.NewTextValue);
}
private void MinusBtn_Clicked(object sender, EventArgs e)
{
if (Text > 0)
Text--;
}
private void PlusBtn_Clicked(object sender, EventArgs e)
{
Text++;
}
}
}
When placing normally in the page I can access it and take the text property and use it in my Xaml.cs code. But in my case, I'm placing it inside a listview and as you know in listview the items are bindable I can't access it directly. In the regular stepper when it is placed in the listview we can use the "ValueChanged" method and can easily get the value by using e.NewValue in the "ValueChanged" method in the Xaml.cs file. Is there a way that I can add something to the CustomStepper class that can help me access the Text property and uses it in the Xaml.cs file? Thanks in advance
You can create a property for EventHandlers. In this case, you would use the event modifier on the property to tell the program that the property is triggering an event. For example:
private EventHandler onValueChangedEvent = null;
public event EventHandler OnValueChanged
{
add
{
onValueChangedEvent = null;
onValueChangedEvent = value;
}
remove
{
// Will show a warning. You can ignore it.
onValueChangedEvent = null;
}
}
private void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
if (!string.IsNullOrEmpty(e.NewTextValue) && e.NewTextValue != ".")
this.Text = int.Parse(e.NewTextValue);
onValueChangedEvent?.Invoke(this, e);
}
You would then bind/assign an event handler in your xaml.cs code to the OnValueChanged property, which will get triggered when the value changes.