ItemSource Binding on Custom Picker in Xamarin Forms - xamarin

I have a Custom Control, that consists of a Label and a Custom Picker. I need to bind the values to the ItemSource property in Xaml. This is what I have tried so far, but there is'nt any values showing in the picker.
Edit 04/10/2017 : I have upload a sample project to github which has the custom control: https://github.com/libin85/BindablePicker
This is the CustomControl Xaml file
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:WoolQ.Core.Controls;assembly=WoolQ.Core"
x:Class="WoolQ.Core.Controls.CustomLabelWithPicker">
<Grid x:Name="grid" VerticalOptions="Start">
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="45" />
</Grid.RowDefinitions>
<controls:RoundedFrame OutlineColor="Red" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" />
<Label x:Name="title" Text="" Grid.Row="0" Margin="5,5,5,0" Style="{StaticResource CustomConrtolLabelStyle}" />
<controls:BorderlessPicker x:Name="pickerValue" Title="_" Grid.Row="1" Style="{StaticResource CustomConrtolPickerStyle}" ItemsSource="{Binding ItemSource}">
</controls:BorderlessPicker>
</Grid>
And in the code behind I have:
public static readonly BindableProperty ItemSourceProperty =
BindableProperty.Create(nameof(ItemSource), typeof(List<string>), typeof(CustomLabelWithPicker), null);
public List<string> ItemSource
{
get
{
return (List<string>)GetValue(ItemSourceProperty);
}
set
{
SetValue(ItemSourceProperty, value);
}
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == nameof(ItemSource))
{
this.pickerValue.Items.Clear();
if (ItemSource != null)
{
foreach (var item in ItemSource)
{
this.pickerValue.Items.Add(item);
}
}
}
}
And finally in my View, I bind the Itemsource with a List like this.
<controls:CustomLabelWithPicker Grid.Row="1" Grid.Column="2"
LabelText="Bin Codes" ItemSource="{Binding BinCodes}"/>
But I dont see any values binded. Appreciate your help

Try below code in your custom picker:
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(CustomPicker), null, BindingMode.TwoWay, null, OnItemsSourceChanged);
public IList ItemsSource
{
get { return (IList)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
private static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
{
var picker = (CustomPicker)bindable;
picker.ItemsSource = (IList)newValue;
}

Related

.NET MAUI CollectionView dont get populated

I made a very simple .NET MAUI App based on the ClickMe Code thats generated by VS2022.
The "ClickMe" Button should add a entry to a CollectionView which is binded to a ObservableCollection, but it don't populate the view if click the button although "monkeys" are added to the ObservableCollection maybe somebody can help what I'm missing.
public class Monkey
{
public string Name { get;set; }
public Monkey(string name) {Name = name; }
}
<?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="MauiObservableList.MainPage">
<ScrollView>
<VerticalStackLayout Spacing="25" Padding="30,0" >
<Button x:Name="CounterBtn" Text="Click me" Clicked="OnCounterClicked" HorizontalOptions="Center" />
<CollectionView ItemsSource="{Binding Monkeys}">
<CollectionView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
MainPage.xaml.cs:
int count = 0;
public ObservableCollection<Monkey> Monkeys { get; set; } = new ObservableCollection<Monkey>();
public MainPage()
{
InitializeComponent();
BindingContext = this;
}
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
if (count == 1)
CounterBtn.Text = $"Clicked {count} time";
else
CounterBtn.Text = $"Clicked {count} times";
Monkeys.Add(new Monkey(CounterBtn.Text));
}
Replace TextCell with a Label. TextCell only works with ListView

how to set name for checkbox in ListView

i want set name for check box and use in code for post method for api
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="#eee" Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<controls:CheckBox DefaultText="{Binding Name}" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Since you had used MVVM . I suggest that you should handle all logic in your ViewModel .You can get the value and index of CheckBox in ViewModel.
I used the CheckBox plugin from https://github.com/enisn/Xamarin.Forms.InputKit .
in your xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:App12"
xmlns:input="clr-namespace:Plugin.InputKit.Shared.Controls;assembly=Plugin.InputKit"
mc:Ignorable="d"
x:Name="contentPage" // set the name of content page
x:Class="xxx.MainPage">
<ListView x:Name="listview" ItemsSource="{Binding MyItems}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="#eee" Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<input:CheckBox Text="{Binding Name}" Type="Check" IsChecked="{Binding IsCheck,Mode=TwoWay}" CheckChangedCommand="{Binding Source={x:Reference contentPage}, Path=BindingContext.CheckCommand}" CommandParameter="{Binding }"/>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
in your model
public class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Name { get; set; }
private bool isCheck;
public bool IsCheck
{
get
{
return isCheck;
}
set
{
if (isCheck != value)
{
isCheck = value;
NotifyPropertyChanged();
}
}
}
}
in Viewmodel or Code behind
public ObservableCollection<Model> MyItems { get; set; }
public ICommand CheckCommand { get; private set; }
public YourViewModel()
{
MyItems = new ObservableCollection<Model>() {
new Model(){Name="xxx",IsCheck=true },
//...
};
CheckCommand = new Command((arg)=> {
var model = arg as Model;
for(int i=0;i<MyItems.Count;i++)
{
if (model == MyItems[i])
{
// i is the index that you checked
bool ischeck = MyItems[i].IsCheck;
// do some thing you want
}
}
});
}
I would suggest adding a Binding for the CheckBox State:
<controls:CheckBox x:Name="chechBox" DefaultText="{Binding Name}" IsChecked="{Binding IsChecked}" />
And then, in the ListView ItemTapped event:
void OnSelection (object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null) {
return; //ItemSelected is called on deselection, which results in SelectedItem being set to null
}
var item = (YourModel)e.SelectedItem;
if(item != null)
{
var checkBoxState = item.IsChecked;
}
}

Xamarin Forms Custom Control binding issue

I have a custom control that I supply a List<string> parameter and it draws a box for each string as a label.
Here is a version that is working. Clicking 'Add Tab' adds one tab at a time for each click.
I want to change the code so the List is converted into a different type of object and that is what the control displays.
First I show the code that is working for the image above. Then I show a changed version of the code that I am unable to get working. Hopefully for anyone answering this question, seeing the before code that works and the after code that doesn't work, you can easily spot the issue.
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:App7"
x:Class="App7.MainPage">
<StackLayout>
<BoxView HeightRequest="100" />
<local:CustomControl
SideTabs="{Binding MainNavigationTabs}"
/>
<Button Text="Add Tab" Command="{Binding AddTab}" />
</StackLayout>
</ContentPage>
MainPageModel.cs
public class MainPageModel : FreshBasePageModel
{
public MainPageModel() { }
public List<string> MainNavigationTabs { get; set; }
private int _index = 0;
public Command AddTab
{
get
{
return new Command(() =>
{
_index++;
var tabs = new List<string>();
for (var index = 1; index <= _index; index++)
{
tabs.Add($"Tab {index}");
}
MainNavigationTabs = tabs;
});
}
}
}
CustomControl.xaml
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App7.CustomControl"
BackgroundColor="Beige"
x:Name="this">
<ContentView.Content>
<StackLayout>
<StackLayout Orientation="Vertical"
BindableLayout.ItemsSource="{Binding Source={x:Reference this}, Path=SideTabs}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<ContentView WidthRequest="237"
Margin="0"
BackgroundColor="Blue"
Padding="10">
<Label Text="{Binding .}" TextColor="White" />
</ContentView>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</ContentView.Content>
</ContentView>
CustomControl.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomControl : ContentView
{
public CustomControl()
{
InitializeComponent();
}
public static readonly BindableProperty SideTabsProperty = BindableProperty.Create(
propertyName: "SideTabs",
returnType: typeof(List<string>),
declaringType: typeof(CustomControl),
defaultBindingMode: BindingMode.OneWay,
defaultValue: new List<string>());
public List<string> SideTabs
{
get { return base.GetValue(SideTabsProperty) as List<string>; }
set { base.SetValue(SideTabsProperty, value); }
}
}
I changed the CustomControl to transform the List<string> to a List<SideTab> object and have the control bind to that. Here's the code...
CustomControl.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomControl : ContentView
{
public CustomControl()
{
InitializeComponent();
}
public static readonly BindableProperty SideTabsProperty = BindableProperty.Create(
propertyName: "SideTabs",
returnType: typeof(List<string>),
declaringType: typeof(CustomControl),
defaultBindingMode: BindingMode.OneWay,
defaultValue: new List<string>());
public List<string> SideTabs
{
get
{
var tabs = new List<string>();
foreach (var tab in _SideTabs)
{
tabs.Add(tab.Text);
}
return tabs;
}
set
{
var tabs = new List<SideTab>();
foreach (var tab in value)
{
tabs.Add(new SideTab() { Text = tab });
}
_SideTabs = tabs;
}
}
public List<SideTab> _SideTabs
{
get { return base.GetValue(SideTabsProperty) as List<SideTab>; }
set { base.SetValue(SideTabsProperty, value); }
}
}
public class SideTab
{
public string Text { get; set; }
}
CustomControl.xaml
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App7.CustomControl"
BackgroundColor="Beige"
x:Name="this">
<ContentView.Content>
<StackLayout>
<StackLayout Orientation="Vertical"
BindableLayout.ItemsSource="{Binding Source={x:Reference this}, Path=_SideTabs}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<ContentView WidthRequest="237"
Margin="0"
BackgroundColor="Blue"
Padding="10">
<Label Text="{Binding Text}" TextColor="White" />
</ContentView>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</ContentView.Content>
</ContentView>
Notice the addition of a property _SideTabs. When SideTabs is set, it transforms the List<string> into a List<SideTab>.
How can I make this work? Here is the result from the above code changes...
Try like this,
public static readonly BindableProperty TabsListProperty = BindableProperty.Create(nameof(TabsList), typeof(List<TabItem>), typeof(ScrollableTabs), null, propertyChanged: (bindable, oldValue, newValue) =>
{
((ScrollableTabs)bindable).InitializeTabs();
});
private void InitializeTabs()
{
//Write your logic here
}
public List<TabItem> TabsList
{
get
{
return (List<TabItem>)GetValue(TabsListProperty);
}
set
{
SetValue(TabsListProperty, value);
}
}

How can I pass a Tapped event into a XAML template?

I code that I use often in my application so I created this template. With a lot of help from the user gannaway the code I have so far is this:
<?xml version="1.0" encoding="utf-8" ?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.SwitchViewCellTemplate"
x:Name="this">
<Grid VerticalOptions="CenterAndExpand" Padding="20,0" >
<local:StyledLabel Text="{Binding Text, Source={x:Reference this}}" HorizontalOptions="StartAndExpand" />
<local:StyledLabel IsVisible="{Binding IsVisible, Source={x:Reference this}}" TextColor="Gray" HorizontalOptions="End" Text="✓" />
</Grid>
</ViewCell>
Here's the code behind:
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace Japanese
{
public partial class SwitchViewCellTemplate : ViewCell
{
public event EventHandler SelectAction;
public SwitchViewCellTemplate()
{
InitializeComponent();
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(SwitchViewCellTemplate));
public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(SwitchViewCellTemplate));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public bool IsVisible
{
get { return (bool)GetValue(IsVisibleProperty); }
set { SetValue(IsVisibleProperty, value); }
}
protected override void OnTapped()
{
base.OnTapped();
this.SelectAction?.Invoke(this, new EventArgs());
}
}
}
and the way I would like to use it:
<template:SwitchViewCellTemplate Text="{Binding [1].Name}"
IsVisible="{Binding [1].IsSelected}"
SelectAction="selectValue" />
Here is the method used to handle toggled:
void Handle_SelectAction(object sender, System.EventArgs e)
{
var viewCell = sender as ViewCell;
if (viewCell == null)
return;
}
Where selectValue is a function in the CS code behind of the pages where I use the template.
The code gives an error:
The type initializer for 'Japanese.SwitchViewCellTemplate' threw an exception.
Can anyone give me advice on what might be wrong that is causing this error.
To solve, an event can be added to the template.
XAML Template
<?xml version="1.0" encoding="utf-8" ?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.SwitchViewCellTemplate"
x:Name="this">
<Grid VerticalOptions="CenterAndExpand" Padding="20,0" >
<local:StyledLabel Text="{Binding Text, Source={x:Reference this}}" HorizontalOptions="StartAndExpand" />
<local:StyledLabel IsVisible="{Binding IsVisible, Source={x:Reference this}}" TextColor="Gray" HorizontalOptions="End" Text="✓" />
</Grid>
</ViewCell>
Add an event to your template's code-behind:
public partial class SwitchViewCellTemplate : ViewCell
{
public event EventHandler SelectAction;
public SwitchViewCellTemplate()
{
InitializeComponent();
}
public static readonly BindableProperty TextProperty =
BindableProperty.Create(
nameof(Text),
typeof(string),
typeof(SwitchViewCellTemplate),
default(string));
public static readonly BindableProperty IsVisibleProperty =
BindableProperty.Create(
nameof(IsVisible),
typeof(bool),
typeof(SwitchViewCellTemplate),
default(bool));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public bool IsVisible
{
get { return (bool)GetValue(IsVisibleProperty); }
set { SetValue(IsVisibleProperty, value); }
}
protected override void OnTapped()
{
base.OnTapped();
this.SelectAction?.Invoke(this, new EventArgs());
}
}
Page XAML
<template:SwitchViewCellTemplate Text="{Binding [1].Name}"
IsVisible="{Binding [1].IsSelected}"
SelectAction="Handle_SelectAction" />
Page Code-Behind
void Handle_SelectAction(object sender, System.EventArgs e)
{
}

How to Create a UserControl using MVVM in Xamarin.Forms

I have tried to develop a usercontrol in Xamarin.Forms but I am not able to bind the controls of that Usercontrol using ViewModel.
My CustomeEntryUserControl is look like
<?xml version="1.0" encoding="UTF-8"?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="LearningApp.UserControls.CustomeEntry"
xmlns:local="clr-namespace:LearningApp.Extension">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Entry Grid.Column="0" x:Name="entry" />
<local:RoundedButton Grid.Column="1" Text="X" BackgroundColor="Gray" TextColor="White" HeightRequest="20" WidthRequest="10" VerticalOptions="Center" Margin="-30,0,30,0" x:Name="cancelbtn" />
</Grid>
Code behind of this usercontrol is look like
public partial class CustomeEntry : Grid
{
public CustomeEntry()
{
InitializeComponent();
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(CustomeEntry), default(string));
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(CustomeEntry), typeof(ICommand), default(ICommand));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public string PlaceHolder
{
get { return entry.Placeholder; }
set { entry.Placeholder = value; }
}
}
I have tried to use this control in one of my content page is like
<?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="LearningApp.Views.TriggersDemo.DataTriggers"
xmlns:local="clr-namespace:LearningApp.UserControls">
<StackLayout Orientation="Vertical">
<Label FontSize="Large" FontAttributes="Bold" Text="{Binding lblText}"></Label>
<local:CustomeEntry Text="{Binding Name}" Command="{Binding CancelCommand}" PlaceHolder="Enter Name"/>
<Button Command="{Binding CheckCommand}" Text="Check" />
</StackLayout>
</ContentPage>
code behind of above mentioned content page is
namespace LearningApp.Views.TriggersDemo
{
public partial class DataTriggers : ContentPage
{
public DataTriggers()
{
InitializeComponent();
this.BindingContext = new TriggersViewModel();
}
}
}
and My ViewModel contain the one Property like
private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; RaisePropertyChanged(); }
}
Can anyone please guide how I can bind the Entry Text of UserControl to my ViewModel property Name direct?
In you CustomerEntry there is no control that displays the Text you're binding to.
You have to set up all the bindings in your custom control too.
You can also pass a handler to the TextProperty's `propertyChanged' parameter, and set other view's properties on behalf.
Remove the properties from the custom control and bind directly to the BindingContext which will be passed along (containing you viewmodel).
Then set Text="{Binding Text=.}" in your custom control's entry (same with Button's Command) to have it bind to the text from the BindingContext, in fact you can remove all your properties from the custom control, and bind directly to the ViewModel.

Resources