I am new to Xamarin.Forms development, so I apologize if this is a basic question.
Below is the out-of-the-box code for a Xamarin.Forms Master-Detail page "ItemsPage.xaml"
I would like to add a Picker above the dynamically displayed list of items.
Question 1) What XAML should I insert to add a Picker? I've tried a few variations, but the xaml keeps complaining.
When a user selects an option in the Pickerm, I would like to filter the displayed list in the CollectionView to only show the items that are of the type that the user chose in the Picker.
Question 2) How would I put that onchange behavior to apply a filter to the displayed list?
Thank you advance for any advice or sample code!
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="NB_App.Views.ItemsPage"
Title="{Binding Title}"
x:Name="BrowseItemsPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" Clicked="AddItem_Clicked" />
</ContentPage.ToolbarItems>
<!-- This is where I want to insert the Picker -->
<RefreshView IsRefreshing="{Binding IsBusy, Mode=TwoWay}" Command="{Binding LoadItemsCommand}">
<CollectionView x:Name="ItemsCollectionView" ItemsSource="{Binding Items}" >
<CollectionView.ItemTemplate>
You will bind ItemsSource of CollectionView when Picker raises SelectedIndexChanged event
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="App9.MainPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" Clicked="AddItem_Clicked" />
</ContentPage.ToolbarItems>
<StackLayout>
<Picker Title="My Picker" SelectedIndexChanged="Picker_SelectedIndexChanged">
<Picker.Items>
<x:Array>
<x:String>A</x:String>
<x:String>B</x:String>
<x:String>C</x:String>
<x:String>D</x:String>
</x:Array>
</Picker.Items>
</Picker>
<!-- This is where I want to insert the Picker -->
<RefreshView IsRefreshing="{Binding IsBusy, Mode=TwoWay}" Command="{Binding LoadItemsCommand}">
<CollectionView x:Name="ItemsCollectionView" />
</RefreshView>
</StackLayout>
</ContentPage>
Code behind
private void Picker_SelectedIndexChanged(object sender, EventArgs e)
{
var picker = sender as Picker;
switch (picker.SelectedIndex)
{
case 0:
ItemsCollectionView.ItemsSource = new ObservableCollection<string>() { "apple", "america"};
break;
case 1:
ItemsCollectionView.ItemsSource = new ObservableCollection<string>() { "ball", "basket" };
break;
case 2:
ItemsCollectionView.ItemsSource = new ObservableCollection<string>() { "cat", "coronavirus" };
break;
case 3:
ItemsCollectionView.ItemsSource = new ObservableCollection<string>() { "dog", "disease" };
break;
default:
break;
}
}
You can do this through MVVM with commands as well. Since you are novice code behind approach should help learn it.
According to your description, you want to add Picker to select item to filter Collectionview, am I right?
If yes, I do one sample that you can take a look:
<ContentPage
x:Class="demo3.collectionviewsample.Page2"
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"
mc:Ignorable="d">
<ContentPage.Content>
<StackLayout>
<Picker
ItemDisplayBinding="{Binding .}"
ItemsSource="{Binding FilterOptions}"
SelectedItem="{Binding SelectedFilter}" />
<CollectionView ItemsSource="{Binding FilterSource}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Name}" />
<Label Text="{Binding Role}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage.Content>
public partial class Page2 : ContentPage
{
public Page2()
{
InitializeComponent();
this.BindingContext = new ItemViewModel();
}
}
ViewModel:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string Role { get; set; }
}
public class ItemViewModel:ViewModelBase
{
public ObservableCollection<string> FilterOptions { get; set; }
public ObservableCollection<Item> source { get; set; }
public List<Item> _FilterSource;
public List<Item> FilterSource
{
get { return _FilterSource; }
set
{
_FilterSource = value;
RaisePropertyChanged("FilterSource");
}
}
private string _SelectedFilter;
public string SelectedFilter
{
get { return _SelectedFilter; }
set
{
_SelectedFilter = value;
RaisePropertyChanged("SelectedFilter");
filteritems(SelectedFilter);
}
}
public ItemViewModel()
{
FilterOptions = new ObservableCollection<string>()
{
"All",
"Admin",
"Editor",
"Student"
};
source = new ObservableCollection<Item>()
{
new Item() { Id = 1, Name = "cherry", Role = "Admin" },
new Item() { Id = 2, Name = "barry", Role = "Admin" },
new Item() { Id = 3, Name = "json", Role = "Editor" },
new Item() { Id = 3, Name = "json", Role = "Editor" },
new Item() { Id = 5, Name = "Leo", Role = "Student" },
new Item() { Id = 6, Name = "Cole", Role = "Student" }
};
FilterSource = new List<Item>();
}
private void filteritems(string selecteditem)
{
FilterSource.Clear();
if(selecteditem=="All")
{
FilterSource = source.ToList();
}
else
{
FilterSource = source.Where(item => item.Role == selecteditem).ToList();
}
}
}
ViewModelBase is the class that implementing INotifyPropertyChanged interface, to inotify data changed.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The Screenshot:
Related
I am trying to output a value from my object to two different entries. Both entries are on the same view but in different ContentPages as follows:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myApp.Views.ViewTabs.ViewHome"
xmlns:localTabs="clr-namespace:myApp.Views.ViewTabs"
xmlns:localObjPages="clr-namespace:myApp.Objects"
>
<ContentPage Title="PageOne">
<ContentPage.BindingContext>
<localObjPages:PagesObj/>
</ContentPage.BindingContext>
<ScrollView>
<StackLayout>
<Entry
x:Name="EntryOne" Text="{Binding BananaCount}"/>
<Entry
x:Name="EntryTwo" Text="{Binding BananaCount}"/>
</StackLayout>
</ScrollView>
</ContentPage>
<ContentPage Title="PageTwo">
<ContentPage.BindingContext>
<localObjPages:PagesObj/>
</ContentPage.BindingContext>
<ScrollView>
<StackLayout>
<Entry
x:Name="EntryThree" Text="{Binding BananaCount}"/>
</StackLayout>
</ScrollView>
</ContentPage>
</TabbedPage>
My Model:
public string BananaCount
{
get { return _bananaCount; }
set
{
if (_bananaCount != value)
{
_bananaCount = value;
NotifyPropertyChanged("BananaCount");
}
}
}
The object is updated and returned in EntryOne or in EntryTwo when I change it either in EntryOne or in EntryTwo. However, it is not updated in EntryThree. Why is this? Am I Binding this correctly? Thank you.
The object is updated and returned in EntryOne or in EntryTwo when I change it either in EntryOne or in EntryTwo. However, it is not updated in EntryThree. Why is this? Am I Binding this correctly?
Do one sample about TabbedPage, assign datasource for TabbedPage, not contentpage, that you can take a look:
<TabbedPage
x:Class="FormsSample.tabbedpage.TabbedPage6"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FormsSample.tabbedpage">
<!--Pages can be added as references or inline-->
<ContentPage Title="PageOne">
<ScrollView>
<StackLayout>
<Entry x:Name="EntryOne" Text="{Binding str}" />
<Entry x:Name="EntryTwo" Text="{Binding str}" />
<Button
x:Name="btn1"
Clicked="btn1_Clicked"
Text="change data" />
</StackLayout>
</ScrollView>
</ContentPage>
<ContentPage Title="PageTwo">
<ScrollView>
<StackLayout>
<Entry x:Name="EntryThree" Text="{Binding str}" />
</StackLayout>
</ScrollView>
</ContentPage>
public partial class TabbedPage6 : TabbedPage
{
public tabclass tabc { get; set; }
public TabbedPage6()
{
InitializeComponent();
tabc = new tabclass();
this.BindingContext = tabc;
}
private void btn1_Clicked(object sender, EventArgs e)
{
tabc.str = "this is test!";
}
}
public class tabclass:ViewModelBase
{
private string _str;
public string str
{
get { return _str; }
set
{
_str = value;
RaisePropertyChanged("str");
}
}
}
The ViewModel is one class that implement INotifyPropertyChanged.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Trying to fire a command inside a stacklayout with itemssource. I wonder why the NavigateToProductListViewShopTappedCommand is not getting fired.
Tried multiple command approaches:
1)
Command="{Binding Source={RelativeSource AncestorType={x:Type local:MyShopsListViewModel}}, Path=NavigateToProductListViewShopTappedCommand}"
Command="{Binding BindingContext.NavigateToProductListViewShopTappedCommand, Source={x:Reference Page}}"
Command="{Binding Path=BindingContext.NavigateToProductListViewShopTappedCommand, Source={x:Reference Page}}"
None are not working
Code:
<?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:controls="clr-namespace:BoerPlaza.Controls.Shop"
xmlns:local="clr-namespace:BoerPlaza.ViewModels"
xmlns:behaviors="clr-namespace:BoerPlaza.Behaviors"
x:Class="BoerPlaza.Views.Shop.MyShopsPage"
x:Name="Page"
Title="Mijn winkels">
<ContentPage.Content>
<ScrollView>
<StackLayout Margin="{StaticResource margin-side-std}"
Padding="{StaticResource padding-top-bottom-std}"
BindableLayout.ItemsSource="{Binding Shops}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<controls:ShopCardTemplateView Shop="{Binding .}"
ControlTemplate="{StaticResource ShopCardTemplateView}">
<controls:ShopCardTemplateView.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:MyShopsListViewModel}}, Path=NavigateToProductListViewShopTappedCommand}"
CommandParameter="{Binding .}">
<!--Command="{Binding BindingContext.NavigateToProductListViewShopTappedCommand, Source={x:Reference Page}}"-->
</TapGestureRecognizer>
</controls:ShopCardTemplateView.GestureRecognizers>
</controls:ShopCardTemplateView>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
Code behind
public partial class MyShopsPage : ContentPage
{
private readonly MyShopsListViewModel _viewModel;
public MyShopsPage()
{
InitializeComponent();
BindingContext = _viewModel = new MyShopsListViewModel(App.ShopDataStore, App.DialogService);
_viewModel.LoadShopsOnUserIdCommand.Execute("B22698B8-42A2-4115-9631-1C2D1E2AC5F7");
}
protected override void OnAppearing()
{
base.OnAppearing();
_viewModel.OnAppearing();
}
}
ViewModel:
[QueryProperty(nameof(UserId), nameof(UserId))]
public class MyShopsListViewModel : BaseViewModel
{
private string _userId;
private ObservableCollection<ShopDbViewModel> _shops;
private readonly IShopDataStore _shopDataStore;
private readonly IDialogService _dialogService;
public ObservableCollection<ShopDbViewModel> Shops
{
get
{
return _shops;
}
set
{
_shops = value;
OnPropertyChanged(nameof(Shops));
}
}
public void OnAppearing()
{
IsBusy = true;
}
public ICommand LoadShopsOnUserIdCommand { get; set; }
public ICommand NavigateToProductListViewShopTappedCommand { get; set; }
public MyShopsListViewModel(IShopDataStore shopDataStore, IDialogService dialogService)
{
this._shopDataStore = shopDataStore;
this._dialogService = dialogService;
Shops = new ObservableCollection<ShopDbViewModel>();
LoadShopsOnUserIdCommand = new Command<string>(async (string userId) => await ExecuteLoadShopsOnUserId(userId));
NavigateToProductListViewShopTappedCommand = new Command<ShopDbViewModel>(async (ShopDbViewModel shop) => await ExecuteNavigateToProductListViewShopTappedCommandAsync(shop));
}
private async Task ExecuteNavigateToProductListViewShopTappedCommandAsync(ShopDbViewModel shop)
{
if (shop == null)
return;
await Shell.Current.GoToAsync($"{nameof(ProductsPage)}?{nameof(MyProductsListViewModel.ShopId)}={shop.Id}");
}
public string UserId
{
get
{
return _userId;
}
set
{
_userId = value;
LoadShopsOnUserIdCommand.Execute(value);
}
}
private async Task ExecuteLoadShopsOnUserId(string userId)
{
var current = Connectivity.NetworkAccess;
if (current == NetworkAccess.Internet)
{
try
{
Shops.Clear();
var shops = await _shopDataStore.GetShopOnUserIdAsync(userId);
foreach(var shop in shops)
{
Shops.Add(shop);
}
}
catch (Exception ex)
{
await _dialogService.ShowDialog(ex.Message, "An error has occurred", "OK");
}
finally
{
IsBusy = false;
}
}
else
{
await _dialogService.ShowDialog("No active internet connection", "Connection error", "OK");
IsBusy = false;
}
}
}
If you define the ICommand in the ViewModel directly , you could set the binding path like following
Command="{Binding Path=BindingContext.NavigateToProductListViewShopTappedCommand, Source={x:Reference Page}}"
I've found the problem
<?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:ffimage="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
xmlns:customcontrols="clr-namespace:BoerPlaza.Controls"
x:Class="BoerPlaza.Controls.Shop.ShopCardTemplateView">
<ContentView.Resources>
<ControlTemplate x:Key="ShopCardTemplateView">
<!-- Card Header -->
<!-- for displaying products and categories on homepage -->
<StackLayout Spacing="1"
HorizontalOptions="FillAndExpand"
Margin="{StaticResource margin-card}">
<!-- On click - shows the product detail view page -->
<StackLayout.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" />
</StackLayout.GestureRecognizers>
<!-- Image frame -->
<Frame BackgroundColor="{StaticResource image-box-color}"
CornerRadius="0"
HasShadow="False"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
HeightRequest="100">
<!-- Product Image -->
....
As you can see this is the control template I'm using for the MyShopsPage. This is a shop card. Inside this shop card I already had an StackLayout.GestureRecognizers. Somehow when I was clicking on the control template, I was actually clicking on this.
I always thought everything flows from top to bottom in events, but this seems different. Something that is on top on something else does not mean anything in xaml.
I am using a bindable StackLayout to show a series of Entry bound to an ObservableCollection<string> (Addresses in the the viewModel down).
It is not a problem to show on the UI the content of the collection, but if I modify the content of any of the Entry, it does not get reflected back in the original ObservableCollection
Here is the view model:
public class MainViewModel
{
public ObservableCollection<string> Addresses { get; set; }
public ICommand AddCommand { get; private set; }
public MainViewModel()
{
AddCommand = new Command(AddEmail);
Addresses = new ObservableCollection<string>();
Addresses.Add("test1");
Addresses.Add("test2");
}
void Add()
{
AddCommand(string.Empty);
}
}
And here is the view:
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="TestList.MainPage"
x:Name="page">
<StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Addresses"
FontSize="Large"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"/>
<Button Command="{Binding AddCommand}"
Text="+" FontSize="Title"
VerticalOptions="Center"/>
</StackLayout>
<StackLayout BindableLayout.ItemsSource="{Binding Addresses}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<Entry Text="{Binding ., Mode=TwoWay}" HorizontalOptions="FillAndExpand"/>
<Button Text="-" FontSize="Title""/>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</ContentPage>
I suspect that this is due to the fact that I am working on strings, and as such they cannot be modified in place. Do you have a suggestion on how to solve this problem without introducing a wrapper class or similar?
If you want to change the value of source in code behind by editing the text in Entry .You need to implement the interface INotifyPropertyChanged in class of ObservableCollection .
Define a model class
public class MyModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
string content;
public string Content
{
get
{
return content;
}
set
{
if (content != value)
{
content = value;
OnPropertyChanged("Content");
}
}
}
}
in ViewModel
public ObservableCollection<MyModel> Addresses { get; set; }
Addresses = new ObservableCollection<MyModel>();
Addresses.Add(new MyModel() {Content = "test1" });
Addresses.Add(new MyModel() { Content = "test2" });
in xaml
<Entry Text="{Binding Content, Mode=TwoWay}" HorizontalOptions="FillAndExpand"/>
You really need a wrapper class for this to work, besides if the syntax is too lengthy you can install PropertyCHanged.Fody package
Then all you need to do is add this tag:
[AddINotifyPropertyChangedInterface]
public class MainViewModel
{
public List<Address> Addresses { get; set; }
And in the wrapper class:
[AddINotifyPropertyChangedInterface]
public class Address
{
public string Street { get; set; }
Ihave this scenario: A ListView and every Cell is a frame. When showing the list all frames backgroudcolor is white when i click on frame i want the color to change in blie. I change the color but not refreshing. This is my code:
In xaml page
<pages:PopupPage.Resources>
<local1:ChangeFrameBackgroudColor x:Key="ChangeFrameBackgroudColor" />
</ResourceDictionary>-->
</pages:PopupPage.Resources>
<ListView x:Name="IzberiFirmaListView" HasUnevenRows="True" ItemsSource="{Binding KorisnikFirmi}" SelectedItem="{Binding IzbranaFirmaId } " Header="{Binding}" ItemTapped="IzberiFirmaListView_ItemTapped">
<ListView.ItemTemplate >
<DataTemplate>
<local:ExtendedViewCell SelectedBackgroundColor="#2188ff" >
<StackLayout Padding="20, 10" >
<Frame x:Name="frameLabel" BorderColor="#2188ff" BackgroundColor="{Binding IsActive, Converter={StaticResource ChangeFrameBackgroudColor}}">
<Label FontAttributes="Bold" FontSize="18" TextColor="Black" Text="{Binding Naziv}" ></Label>
</Frame>
</StackLayout>
</local:ExtendedViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.HeaderTemplate>
<DataTemplate>
<ContentView BackgroundColor="#006BE6" >
<Label Margin="10" HorizontalOptions="CenterAndExpand" Text="ОДБЕРЕТЕ ФИРМА" TextColor="White" FontSize="20" FontAttributes="Bold"/>
</ContentView>
</DataTemplate>
</ListView.HeaderTemplate>
</ListView>
In xaml.cs Page:
private void IzberiFirmaListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var vm = BindingContext as OdberiFirmaPopupViewModel;
var firm = e.Item as SysDashFirma;
vm.ChangeColorOnItemSelected(firm);
}
In viewModel
public List<SysDashFirma> KorisnikFirmi
{
get { return korisnikFirmi; }
set
{
if (korisnikFirmi != value)
{
korisnikFirmi = value;
SetProperty(ref korisnikFirmi, value);
OnPropertyChanged("KorisnikFirmi");
}
}
}
public void ChangeColorOnItemSelected(SysDashFirma firm)
{
if (_oldFirmSelected == firm)
{
//firm.BackColor = "#2188ff";
firm.IsActive = true;
UpdateSelectedFirmItemColor(firm);
}
else
{
if(_oldFirmSelected != null)
{
//_oldFirmSelected.BackColor = "#f5f5f5";
_oldFirmSelected.IsActive = false;
UpdateSelectedFirmItemColor(_oldFirmSelected);
}
// firm.BackColor = "#2188ff";
firm.IsActive = true;
UpdateSelectedFirmItemColor(firm);
}
_oldFirmSelected = firm;
}
private void UpdateSelectedFirmItemColor(SysDashFirma firm)
{
var index = KorisnikFirmi.IndexOf(firm);
KorisnikFirmi.Remove(firm);
KorisnikFirmi.Insert(index,firm);
}
Changes are made in the list ,flag is changed, probably cant refresh the bidning context to converter read all from start.
Use OnPropertyChanged for the IsActive property to notify the UI about its change.
public class Firm : INotifyPropertyChanged
{
private string name = "Unknown";
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged();
}
}
private bool isActive;
public bool IsActive
{
get
{
return isActive;
}
set
{
isActive = value;
OnPropertyChanged();
}
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
I would suggest changing the IsActive property alone as removing and adding the item is not necessary.
<ListView
ItemTapped="ListView_ItemTapped"
ItemsSource="{Binding FirmCollection}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame
Padding="2"
BackgroundColor="{Binding IsActive, Converter={StaticResource converterTest}}">
<Label Text="{Binding Name}"/>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel
public class ViewModel : INotifyPropertyChanged
{
....
....
private Firm oldSelectedFirm;
public Firm OldSelectedFirm
{
get
{
return oldSelectedFirm;
}
set
{
oldSelectedFirm = value;
OnPropertyChanged();
}
}
...
...
}
Xaml.cs
void ListView_ItemTapped(System.Object sender, Xamarin.Forms.ItemTappedEventArgs e)
{
var tappedFirm = (e.Item as Firm);
var vm = (BindingContext as ViewModel);
if (vm.OldSelectedFirm != null)
vm.OldSelectedFirm.IsActive = false;
tappedFirm.IsActive = true;
vm.OldSelectedFirm = tappedFirm;
}
I want to create a ListView which contains the department name. The ListView contains the CheckBox with department names. An user can check and unchecked the department and also the user can on clicking on select all check box user can select all department.
Which listview you want either a simple listview with textcell or imagecellits upto you, Here I'm posting code for listview with imagecell, also cell swipe option and just add Checkbox where you want to with its event and apply logics. Hope it works for you!
<AbsoluteLayout>
<ListView x:Name="Demolist" BackgroundColor="White" ItemSelected="Demolist_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell Height="30"
Text="{Binding deparment_name }"
Detail="{Binding department_description}"
ImageSource="ImageName.png">
<ImageCell.ContextActions>
<MenuItem x:Name="OnMore" Clicked="OnMore_Clicked" CommandParameter="{Binding .}" Text="More" />
<MenuItem x:Name="OnDelete" Clicked="OnDelete_Clicked" CommandParameter="{Binding .}" Text="Delete" IsDestructive="True" />
</ImageCell.ContextActions>
</ImageCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</AbsoluteLayout>
Checkbox is not a control present in XF framework, so I think you can not add checkbox in listview in Xamarin.form, but you can use different to display check and uncheck status.
<ContentPage
x:Class="test2.Page3"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:convert="clr-namespace:test2"
x:Name="ToDoPage">
<ContentPage.Resources>
<convert:converter1 x:Key="converterbool" />
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<ListView x:Name="listview1" ItemsSource="{Binding todoList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding ItemDescription}" VerticalOptions="Center" />
<Button
Grid.Column="1"
Command="{Binding Source={x:Reference ToDoPage}, Path=BindingContext.UpdateCheckBoxCommand}"
CommandParameter="{Binding Id}"
Opacity="0" />
<Image
Grid.Column="1"
HeightRequest="20"
HorizontalOptions="Center"
Source="{Binding IsDone, Converter={StaticResource converterbool}}"
VerticalOptions="Center" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
public class TodoItem:INotifyPropertyChanged
{
private string _Id;
public string Id
{
get { return _Id; }
set
{
_Id = value;
RaisePropertyChanged("Id");
}
}
private string _ItemDescription;
public string ItemDescription
{
get { return _ItemDescription; }
set
{
_ItemDescription = value;
RaisePropertyChanged("ItemDescription");
}
}
private bool _IsDone;
public bool IsDone
{
get { return _IsDone; }
set
{
_IsDone = value;
RaisePropertyChanged("IsDone");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
class ToDoViewModel:INotifyPropertyChanged
{
public ObservableCollection<TodoItem> todoList { get; set; }
public ICommand UpdateCheckBoxCommand { get; set; }
public ToDoViewModel()
{
todoList = new ObservableCollection<TodoItem>()
{
new TodoItem(){ Id = "1", ItemDescription = "Task 1", IsDone = false},
new TodoItem(){ Id = "2", ItemDescription = "Task 2", IsDone = false},
new TodoItem(){ Id = "3", ItemDescription = "Task 3", IsDone = false},
new TodoItem(){ Id = "4", ItemDescription = "Task 4", IsDone = false},
new TodoItem(){ Id = "5", ItemDescription = "Task 5",IsDone=false }
};
UpdateCheckBoxCommand = new Command((Id) => UpdateCheckBox(Id.ToString()));
}
private void UpdateCheckBox(string id)
{
IEnumerable<TodoItem> items = todoList.Where(x=>x.Id==id);
foreach(var item in items )
{
if (item.IsDone) item.IsDone = false;
else item.IsDone = true;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
class converter1 : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool ischeck = (bool)value;
if(ischeck==false)
{
return "uncheck.png";
}
else
{
return "check.png";
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Page3 : ContentPage
{
public Page3 ()
{
InitializeComponent ();
this.BindingContext = new ToDoViewModel();
}
}