Swipegesture only in title in xamarin forms - xamarin

I need a view where there will be a header and a content.
Suppose the app displays fruit names and its details.
Say first view will have header -"strawberry" and in content it will be strawberry's picture and details. I need a swipe action only on the header "Strawberry", so that on swiping it, the next item "Mango" comes in the header with details of Mango below.
So now the header "Mango" must be swipable to both left and right. Swiping right to view the previous fruit Strawberry's details or swiping left to view the next fruit-Orange's details.
If Orange is the last fruit, it should have swipe action in it's header only to view the previous image, as it has nothing to show as next item.
I have all the fruit name and other details in a List. Kindly give me a heads up on how to achieve this

The easiest way to do this is to use a CarouselView for the title.
A simple sample like below:
the page xaml:
<?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="NewForms.CollectionViewPage">
<ContentPage.Content>
<StackLayout>
<CarouselView x:Name="collection" ItemSource={Binding Fruits} HeightRequest="50" Loop="False">
<CarouselView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}"></Label> //this for the header content
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<StackLayout Orientation="Vertical" BindingContext="{Binding Path=CurrentItem,Source={x:Reference collection}}">
<Label Text="{Binding Details}"></Label> //binding the details content
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
the page.xaml.cs:
public CarouselViewPage()
{
InitializeComponent();
FruitModel fruitModel = new FruitModel();
BindingContext = fruitModel;
}
the viewmodel:
class FruitModel
{
public ObservableCollection<Fruit> Fruits { get; set; }
public FruitModel()
{
Fruits = new ObservableCollection<Fruit>();
Fruits.Add(new Fruit() { Name = "Apple", Details = "Thi is an apple" });
Fruits.Add(new Fruit() { Name = "Pear", Details = "Thi is a Pear" });
Fruits.Add(new Fruit() { Name = "Banana", Details = "Thi is a Banana" });
Fruits.Add(new Fruit() { Name = "Strawberry", Details = "Thi is a Strawberry" });
}
}
the model:
class Fruit
{
public string Name { get; set; }
public string Details { get; set; }
}
you could change the template and data base on your needs.

Xamarin.Forms provides swipe gesture, you can apply swipe gesture to the header.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/gestures/swipe

Related

Collectionview items font size

I am trying to make funcionality, to make larger text across whole application for user when he clicks a 'increase font size' button. Using MVVM, I have done it like this:
Increase font size button click
increase value of double field 'fontSize' which is binded to almost every text in layout
Update UI with new value after button click
However I don't know how to achieve this in Collectionview where I have got Binding in .xaml file, with some particular List (item is model class). The collectionview DataTemplate contains labels where I want to increase font size. Is there a way to do this without adding 'fontSize' field in my model class. If not how to update UI with 'new' List with increased font sizes.
I appreciate any help, tips and discussions.
Thank you.
You can create bindableproperty(fontsize) in your viewmodel and use Relative Binding so the label in Collectionview can change it's fontsize,code like:
ViewMode:
public class ColViewModel:BindableObject
{
public ObservableCollection<Student> students { set; get; }
public static readonly BindableProperty FontSizeProperty =
BindableProperty.Create("fontsize", typeof(int), typeof(ColViewModel), null);
public int fontsize
{
get { return (int)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
public ICommand IncreaseCommand { private set; get; }
public ColViewModel()
{students = new ObservableCollection<Student>();
getStudents();
fontsize = 24;
IncreaseCommand = new Command(() => {
fontsize++;
});
}
View:
<StackLayout>
<Label Text="This is a Title" FontSize="{Binding fontsize}"/>
<CollectionView ItemsSource="{Binding students}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}" FontSize="{Binding Source={RelativeSource AncestorType={x:Type local:ColViewModel}}, Path=fontsize}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button Text="Click to increase fontsize" Command="{Binding IncreaseCommand}"/>
</StackLayout>
Edit:
xmlns:local="clr-namespace:MyForms2.ViewModels"

Xamarin Forms - SelectionChangedCommand not firing for CollectionView

The code below shows a simple example of a CollectionView. I am not receiving the event for the SelectionChangedCommand. Can someone see what I am doing wrong?
btw, the complete source for this can be found on GitHub here.
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ControlDemo"
x:Class="ControlDemo.MainPage">
<StackLayout>
<CollectionView SelectionMode ="Single"
ItemsSource="{Binding Tags}"
SelectionChangedCommand="{Binding SelectedTagChanged}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding .}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>
MainPageModel.cs
public class MainPageModel : FreshBasePageModel
{
public override void Init(object initData)
{
Tags = new List<string>() { "A", "B", "C" };
base.Init(initData);
}
public List<string> Tags { get; set; }
public Command SelectedTagChanged
{
get
{
return new Command(() =>
{
});
}
}
}
Couple of things that worked at my side (additionally to the SelectionMode = Single):
Make sure your Command' signature is <object> at your PageModel and do any cast according to your needs (specially if your collection becomes more complex).
Also at your XAML you want to give your CollectionView a name and use the SelectedItem property.
SelectionChangedCommandParameter="{Binding SelectedItem, Source={x:Reference cvTagsCollectionView}}"
I use your code and created a demo on my side, I add the widthRequest and HeightRequest to make the collectionView work:
<CollectionView
HeightRequest="170"
WidthRequest="200"
SelectionMode="Single"
SelectionChangedCommand="{Binding SelectedTagChangedCommand}"
ItemsSource="{Binding Tags}"
>
The SelectionChangedCommand did triggered after I click different items in the CollectionView.
I uploaded a sample here and you can check it: collectionView-selectItemChanged-xamarin.forms
If you want to use your ViewModel, then you should use the Binding for the SelectedItem:
<CollectionView ItemsSource="{Binding Monkeys}"
SelectionMode="Single"
SelectedItem="{Binding SelectedMonkey, Mode=TwoWay}">
...
</CollectionView>
and, in your ViewModel:
Monkey selectedMonkey;
public Monkey SelectedMonkey
{
get
{
return selectedMonkey;
}
set
{
if (selectedMonkey != value)
{
selectedMonkey = value;
}
}
}
So everytime you select a new object, the SelectedMonkey will be updated.
If you want to track the SelectionChanged, then, it should be in the code-behind (not sure how to implement within the viewmodel, nothing about that in the docs)
<CollectionView ItemsSource="{Binding Monkeys}"
SelectionMode="Single"
SelectionChanged="OnCollectionViewSelectionChanged">
...
</CollectionView>
And, in your Page.xaml.cs:
void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var previous = e.PreviousSelection;
var current = e.CurrentSelection;
...
}
I improved Fabricio P. answer in this way:
Used {RelativeSource Self} for SelectionChangedCommandParameter. It helps to omit named collection views.
So your xaml part will be something like this:
<CollectionView
ItemsSource="{Binding Objects}"
SelectionMode="Single"
SelectionChangedCommand="{Binding SelectObjectCommand}"
SelectionChangedCommandParameter="{Binding SelectedItem, Source={RelativeSource Self}}">
And in your view model:
public ICommand SelectObjectCommand => new Command<string>(i => { Debug.WriteLine("Selected: " + i); });
public IEnumerable<string> Objects { get; set; }
It doesn't look like you set the SelectionMode property. According to the docs:
By default, CollectionView selection is disabled. However, this behavior can be changed by setting the SelectionMode property value to one of the SelectionMode enumeration members:
None – indicates that items cannot be selected. This is the default value.
Single – indicates that a single item can be selected, with the selected item being highlighted.
Multiple – indicates that multiple items can be selected, with the selected items being highlighted.
Adding SelectionMode = Single to the CollectionView will resolve your problem.
if you are using the Model for it you can use the following method
My C# Model View Class
public class MainView : INotifyPropertyChanged
{
public ICommand SelectionChangedCommands => new Command<GroupableItemsView>((GroupableItemsView query) =>
{
GO_Account test = query.SelectedItem as GO_Account;
});
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And this is my XAML
<CollectionView x:Name="myAccounts"
SelectionMode="Single"
ItemsSource="{Binding Products}"
SelectionChangedCommand="{Binding SelectionChangedCommands}"
SelectionChangedCommandParameter="{Binding Source={x:Reference myAccountsModel}}">
</CollectionView>

Set focus of an entry from ViewModel

I've read about messaging, triggers, behaviors, etc. ... all seems a bit overkill for what I am trying to accomplish.
I have a repeating data entry screen in xaml that has 1 picker, 2 entries, and 1 button. The picker, once a value is selected, keeps that selection. The 1st entry does the same as the picker. The 2nd entry is the one that is always getting new values.
I want to collect the filled in values on click of the button and then clear the last entry field of its data and put focus back on that entry so the user can enter a new value and hit save. repeat repeat repeat etc.
I understand the MVVM model and theory - but I just want to put the focus on an entry field in the xaml view and am completely stumped.
EDIT to add code samples
view.xaml:
<StackLayout Spacing="5"
Padding="10,10,10,0">
<Picker x:Name="Direction"
Title="Select Direction"
ItemsSource="{Binding Directions}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding SelectedDirection}"/>
<Label Text="Order"/>
<Entry Text="{Binding Order}"
x:Name="Order" />
<Label Text="Rack"/>
<Entry Text="{Binding Rack}"
x:Name="Rack" />
<Button Text="Save"
Style="{StaticResource Button_Primary}"
Command="{Binding SaveCommand}"
CommandParameter="x:Reference Rack" />
<Label Text="{Binding Summary}"/>
</StackLayout>
viewmodel.cs
public ICommand SaveCommand => new DelegateCommand<View>(PerformSave);
private async void PerformSave(View view)
{
var scan = new Scan()
{
ScanType = "Rack",
Direction = SelectedDirection.Name,
AreaId = 0,
InsertDateTime = DateTime.Now,
ReasonId = 0,
ScanItem = Rack,
OrderNumber = Order,
ScanQty = SelectedDirection.Value,
IsUploaded = false
};
var retVal = _scanService.Insert(scan);
if (!retVal)
{
await _pageDialogService.DisplayAlertAsync("Error", "Something went wrong.", "OK");
}
else
{
view?.Focus();
Rack = string.Empty;
Summary = "last scan was great";
}
}
Error shows up in this section:
private void InitializeComponent() {
global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(RackPage));
Direction = global::Xamarin.Forms.NameScopeExtensions.FindByName<global::Xamarin.Forms.Picker>(this, "Direction");
Order = global::Xamarin.Forms.NameScopeExtensions.FindByName<global::Xamarin.Forms.Entry>(this, "Order");
Rack = global::Xamarin.Forms.NameScopeExtensions.FindByName<global::Xamarin.Forms.Entry>(this, "Rack");
}
You can send the Entry as a View parameter to your view model's command. Like that:
public YourViewModel()
{
ButtonCommand = new Command<View>((view) =>
{
// ... Your button clicked stuff ...
view?.Focus();
});
}
And from XAML you call this way:
<Entry x:Name="SecondEntry"
... Entry properties ...
/>
<Button Text="Click Me"
Command="{Binding ButtonCommand}"
CommandParameter="{Reference SecondEntry}"/>
I hope it helps.
You can set the CommandParameter in the XAML and use that in the viewmodel.
In Xaml:
<ContentView Grid.Row="0" Grid.Column="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Image Source="downarrow" HeightRequest="15" WidthRequest="15" VerticalOptions="Center" HorizontalOptions="End" Margin="0,0,5,0" />
<ContentView.GestureRecognizers>
<TapGestureRecognizer Command="{Binding SecurityPopupCommand}" CommandParameter="{x:Reference AnswerEntry}"/>
</ContentView.GestureRecognizers>
</ContentView>
In view model:
public ICommand SecurityPopupCommand { get { return new Command(OpenSecurityQuestionPopup); } }
private void OpenSecurityQuestionPopup(object obj)
{
var view = obj as Xamarin.Forms.Entry;
view?.Focus();
}
Not sure you can set the focus of an entry from the XAML, but from the page, you could just use the function Focus of the Entry on the Clicked Event:
saveButton.Clicked += async (s, args) =>
{
//save the data you need here
//clear the entries/picker
yourEntry.Focus();
};

how to use radio buttons in xamarin forms

Creating a Registration page, I need to get the following data from user.
First Name
Last Name
Username
Email
Password
Date of Birth
Gender
User Role
For the last two parameters, I am unable to find how to use radio buttons in Xamarin.Forms. Following is my code for the Registration Page.
<StackLayout BackgroundColor="#30af91" Padding="60">
<Entry Text="{Binding FirstName}" Placeholder="First Name"/>
<Entry Text="{Binding LastName}" Placeholder="Last Name"/>
<Entry Text="{Binding UserName}" Placeholder="Last Name"/>
<Entry Text="{Binding Email}" Placeholder="Email" />
<Entry Text="{Binding Password}" Placeholder="Password" IsPassword="True"/>
<Entry Text="{Binding ConfirmPassword}" Placeholder="Confirm Password" IsPassword="True"/>
<DatePicker MinimumDate="1/1/1948" MaximumDate="12/31/2007"/>
<!--Radio buttons for Gender
1. Male 2.Female-->
<!--Radio Buttons for UserRole
1. Admin 2.Participant-->
<Button Command="{Binding RegisterCommand}" Text="Register"/>
<Label Text="{Binding Message}" />
</StackLayout>
Xamarin forms does not provide Radio Button.
You can either use
1)Switch
2)Picker
or any other component to fulfill your requirement
UPDATE
The xamarin forms update version 4.6 has introduced the Radio button control, Here is the official documentation
I think there is a simpler solution that is fairly easy and requires no libraries. Really a a radio group is just a fancy ListView. You would just need to create a viewModel for each radio button that has a IsSelected flag and switch between 2 images. I had a need to allow a user to select how long a token persisted:
XAML
<ListView
HasUnevenRows="True"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Durations}"
ItemSelected="ListView_ItemSelected"
SelectedItem="{Binding SelectedDuration}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout
Orientation="Horizontal">
<Image
HeightRequest="18"
IsVisible="{Binding IsSelected}"
Source="radioButtonChecked.png"
WidthRequest="18"/>
<Image
HeightRequest="18"
IsVisible="{Binding IsUnselected}"
Source="radioButtonUnchecked.png"
WidthRequest="18"/>
<Label
Margin="8,0,0,0"
Text="{Binding Caption}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
We create a listview in our content page and listen for the ItemSelected event. Each list item is a horizontal stack panel where we flip between two images depending on the selected state
Code Behind
public partial class LoginPage : ContentPage
{
LoginPageViewModel LoginPageViewModel { get; }
public LoginTwoFactorFrequencyPage ()
{
BindingContext = LoginPageViewModel = new LoginPageViewModel();
InitializeComponent ();
}
private void ListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
LoginPageViewModel.UpdateSelected(e.SelectedItem as PersistenceDuration);
}
}
The page's code behind instantiates a view model and calls an UpdateSelected method with the newly selected item on the page's view model*
RadioButton ViewModel
The view model for each radio button:
public class PersistenceDuration : ViewModelBase
{
bool isSelected;
public string Caption { get; set; }
public TwoFactorTokenPersistenceDuration Duration { get; set; }
public bool IsSelected
{
get => isSelected;
set
{
isSelected = value;
OnPropertyChanged();
OnPropertyChanged("IsUnselected");
}
}
public bool IsUnselected => !IsSelected;
public PersistenceDuration(string caption, TwoFactorTokenPersistenceDuration duration)
{
Caption = caption;
Duration = duration;
IsSelected = false;
}
}
The radio button view model holds selection info and the caption. We make sure to fire OnPropertyChanged whenever the selected state changes
Page ViewModel
public class LoginPageViewModel : ViewModelBase
{
PersistenceDuration duration;
PersistenceDuration selectedDuration;
public ObservableCollection<PersistenceDuration> Durations { get; }
public PersistenceDuration SelectedDuration
{
get => selectedDuration;
set
{
if (value != null)
{
duration = value;
UpdateSelected(duration);
}
OnPropertyChanged();
}
}
public LoginTwoFactorFrequencyViewModel()
{
Durations = new ObservableCollection<PersistenceDuration>(
new List<PersistenceDuration>()
{
new PersistenceDuration(AppResources.Save_code__forever, TwoFactorTokenPersistenceDuration.Forever),
new PersistenceDuration(AppResources.ChatRequireEvery30Days, TwoFactorTokenPersistenceDuration.ThirtyDays),
new PersistenceDuration(AppResources.ChatRequireEveryLogin, TwoFactorTokenPersistenceDuration.None),
});
}
public void UpdateSelected(PersistenceDuration persistenceDuration)
{
foreach (var item in Durations)
item.IsSelected = persistenceDuration == item;
}
}
In the page view model we create a list of radio button view models that the XAML binds to. When we UpdateSelected() all the IsSelected states are updated which trigger binding updates which flip the image.
You will still need to do something about the highlight when someone selects an item, but that is easy enough to find on the internet :)
You can use XLabs plugin from manage NuGets package. After installing you can use like this:
In Xaml:
controls:BindableRadioGroup x:Name="Radiobtn"
In C#:
string[] gender = {"MAlE","FEMALE"}
Radiobtn.Add(gender)
Refer Link
https://github.com/XLabs/Xamarin-Forms-Labs/tree/master/samples/XLabs.Samples/XLabs.Samples/Pages/Controls
You can get the radio button effect without a package. Use Labels with text unicode circle \u26AA or \u25CB. Attach a tab gesture recognizer to each label.
When tapped, change the text of the selected button to unicode circle bullet \u29BF and change the text of the other button(s) back to unicode circle \u26AA.
Test on your preferred platforms as each platform may display somewhat differently. You may need to adjust the font size as you change the text.
If you want real radiobuttons you can xlabs their package (https://github.com/XLabs/Xamarin-Forms-Labs/tree/master/src/Forms/XLabs.Forms/Controls/RadioButton)
Personally I'd just use a picker, Xlabs package hasn't been updated in a while so their might be some bugs in the radiobutton
You can use image as a radio button. When tou you click on it, it can change. It is not a good way to do it though.
This is xaml code:
<Image Scale="0.7" HorizontalOptions="Start" x:Name="radioButton" Source="unRadioBtn.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="radioButton_Clicked"></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>
And this is .cs:
private void radioButton_Clicked(object sender, EventArgs e)
{
radioButton.Source = "radioBtn.png";
}
Xamarin.Forms 4.6 introduced a new RadioButton control. You can find the documentation here: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/radiobutton
You can use the switch component. Also you can see the implementation for a checkbox component from the XLabs project which is now discontinued, get the code and modify it as you need.
Hint: You're gonna need the custom renderers per platform for it to work .
You need to use Picker
https://developer.xamarin.com/api/type/Xamarin.Forms.Picker/
Actually it is the best alternative to RadionButton On Xamarin.forms
XLabs RadioButton and BindableRadioGroup work well: XLabs RadioButton for Xamarin Forms
Here's a simple Yes/No radio using the BindableRadioGroup:
var answers = new List<string>();
answers.Add("Yes");
answers.Add("No");
var RadioGroup = new XLabs.Forms.Controls.BindableRadioGroup()
{
ItemsSource = answers,
Orientation = StackOrientation.Horizontal
};
Xamarin Forms now provides a Radio Button control.
See docs here:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/radiobutton
As of XF 4.8 this is still experimental and I've not yet used this feature so can't comment on its stability.

Databinding + Dynamic Pivot

I am having troubles with dynamically adding a PivotItem to a templated and databound Pivot.
The classes in use (a bit simplified to keep it quickly comprehensable);
class Menu {
string Name
List<Dish> Dishes_1;
List<Dish> Dishes_2;
List<Dish> Dishes_3;
}
class Dish {
string Description
string Price;
}
I want to use a Pivot to display a list of Menu-Objects. I create PivotItems dynamically based on the number of items in that list. Each PivotElement should thus follow the same layout and behave the same. The lay-out template and databinding is done in the .xaml as following;
<phone:Pivot x:Name="Mainpivot">
<phone:Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</phone:Pivot.HeaderTemplate>
<phone:Pivot.ItemTemplate>
<DataTemplate>
<ListBox>
<TextBlock Text="Dishes_1"/>
<ListBox ItemsSource="{Binding Dishes_1}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Description}"/>
<TextBlock Text="{Binding Price}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
// ...
// this is repeated 3 times;
//a textblock and listbox per List<Dishes> in the Menu-class
</ListBox>
</DataTemplate>
</phone:Pivot.ItemTemplate>
</phone:Pivot>
I populate the Pivot in de .cs file with the following:
foreach (Menu m in List_Menus) {
PivotItem p = new PivotItem();
p.DataContext = m;
Mainpivot.Items.Add(p);
}
As I set the DataContext as a Menu-Object, the DataBinding (through xaml) should not require any more code here (so I think?).
The problem now being; it doesn't work...
Through looking with the debugger, It appears that the created PivotItem doesn't behave like the template defined in Mainpivot tells (or so I think). Looking at Mainpivot does show that PivotItems have been added, but that's it, I believe they're just empty all-null PivotItems. When executing in the emulator, It just shows an empty Pivot.
Any thoughts?
//PS: I don't use ViewModels, as I find them quite confusing (as a beginner) as concept. I don't think that has anything to do with the problem though?
A few things here. First, for your binding to work you'll need to use properties instead of fields.
public class Menu {
public string Name {get;set;}
public List<Dish> Dishes_1 { get; set; }
public List<Dish> Dishes_2 { get; set; }
public List<Dish> Dishes_3 { get; set; }
}
public class Dish {
public string Description { get; set; }
public string Price { get; set; }
}
Next, instead of your foreach loop to add the items to the pivot just set the items source:
Mainpivot.ItemsSource = List_Menus;
BTW - you really should look into learning MVVM. It is worth the time.

Resources