Select items from a checkbox in the group header
Environment
Product Version | 2021.1.413.1 |
Product | ListView for Xamarin Cross-Platform |
Description
This article will show you how to select all items from a group uisng checkbox in the groupo header.
Solution
You can create a GroupHeaderTemplate and inside it place a CheckBox control with a Command handled in the ViewModel, basically through an EventToCommandBehavior you can subscribe to the IsCheckedChanged event and implement the needed logic for checking/unchecking the group child items.
ListView definition in XAML. The CheckBox control is added inside the GroupHeaderTemplate:
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="ListViewItemTemplate">
<telerikListView:ListViewTemplateCell>
<telerikListView:ListViewTemplateCell.View>
<Grid Padding="16, 0, 0, 0" BackgroundColor="#F1F2F5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<telerikPrimitives:RadCheckBox IsChecked="{Binding IsSelected} "/>
<Label Grid.Column="1" Text="{Binding Name}" TextColor="#6F6F70" FontSize="Small" />
</Grid>
</telerikListView:ListViewTemplateCell.View>
</telerikListView:ListViewTemplateCell>
</DataTemplate>
<DataTemplate x:Key="ListViewGroupHeaderTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Text="▸" Margin="8, 12, 0, 6" TextColor="DarkGray" FontSize="Medium">
<Label.Triggers>
<DataTrigger TargetType="Label" Binding="{Binding IsExpanded}" Value="True">
<Setter Property="Text" Value="▾" />
</DataTrigger>
</Label.Triggers>
</Label>
<telerikPrimitives:RadCheckBox Margin="0, 12, 0, 6" Grid.Column="1">
<telerikPrimitives:RadCheckBox.Behaviors>
<behaviors:EventToCommandBehavior EventName="IsCheckedChanged"
Command="{Binding BindingContext.IsCheckedChangedCommand, Source={x:Reference listView}}"
CommandParameter="{Binding}" />
</telerikPrimitives:RadCheckBox.Behaviors>
</telerikPrimitives:RadCheckBox>
<Label Margin="0, 12, 0, 6" Text="{Binding }" Grid.Column="2" TextColor="DarkGray" FontSize="Medium" HorizontalOptions="Start" />
</Grid>
</DataTemplate>
<telerikListView:ListViewGroupStyle x:Key="ListViewGroupHeaderStyle" BackgroundColor="Transparent" />
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<telerikDataControls:RadListView x:Name="listView" ItemsSource="{Binding Cities}"
ItemTemplate="{StaticResource ListViewItemTemplate}"
GroupHeaderTemplate="{StaticResource ListViewGroupHeaderTemplate}"
GroupHeaderStyle="{StaticResource ListViewGroupHeaderStyle}">
<telerikDataControls:RadListView.GroupDescriptors>
<telerikListView:PropertyGroupDescriptor PropertyName="Country"/>
</telerikDataControls:RadListView.GroupDescriptors>
</telerikDataControls:RadListView>
</Grid>
The ViewModel and business model used:
public class ViewModel
{
public ObservableCollection<City> Cities { get; set; }
public ObservableCollection<string> SelectedGroupKeys { get; set; }
public ViewModel()
{
this.Cities = new ObservableCollection<City>()
{
new City() { Name = "Barcelona", Country = "Spain"},
new City() { Name = "Madrid", Country = "Spain"},
new City() { Name = "Rome", Country = "Italy"},
new City() { Name = "Florence", Country = "Italy"},
new City() { Name = "London", Country = "England"},
new City() { Name = "Manchester", Country = "England"},
new City() { Name = "New York", Country = "USA"},
new City() { Name = "Boston", Country = "USA"}
};
this.IsCheckedChangedCommand = new Command(this.IsCheckedChangedCommandExecute);
this.SelectedGroupKeys = new ObservableCollection<string>();
}
public ICommand IsCheckedChangedCommand { get; private set; }
private void IsCheckedChangedCommandExecute(object param)
{
var currentGroupkey = (string) (param as GroupHeaderContext).Key;
var childitems = (param as GroupHeaderContext).Items;
bool shouldBeSelected;
if (this.SelectedGroupKeys.Contains(currentGroupkey))
{
this.SelectedGroupKeys.Remove(currentGroupkey);
shouldBeSelected = false;
}
else
{
this.SelectedGroupKeys.Add(currentGroupkey);
shouldBeSelected = true;
}
foreach(City city in this.Cities.Where(c => c.Country == currentGroupkey).ToList())
{
city.IsSelected = shouldBeSelected;
}
}
}
public class City: NotifyPropertyChangedBase
{
private bool isSelected;
public string Name { get; set; }
public string Country { get; set; }
public bool IsSelected
{
get { return this.isSelected; }
set
{
if(this.isSelected != value)
{
this.isSelected = value;
this.OnPropertyChanged();
}
}
}
}
Set the BindingContex
t after InitializeComponent()
:
this.BindingContext = new ViewModel();
EventToCommand behavior implementation:
Create a BehaviorBase
public class BehaviorBase<T> : Behavior<T> where T : BindableObject
{
public T AssociatedObject { get; private set; }
protected override void OnAttachedTo(T bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject = bindable;
if (bindable.BindingContext != null)
{
BindingContext = bindable.BindingContext;
}
bindable.BindingContextChanged += OnBindingContextChanged;
}
protected override void OnDetachingFrom(T bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= OnBindingContextChanged;
AssociatedObject = null;
}
void OnBindingContextChanged(object sender, EventArgs e)
{
OnBindingContextChanged();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
BindingContext = AssociatedObject.BindingContext;
}
}
Create a class EventToCommandBehavior which inherits from BehaviorBase
public class EventToCommandBehavior : BehaviorBase<VisualElement>
{
Delegate eventHandler;
public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty InputConverterProperty = BindableProperty.Create("Converter", typeof(IValueConverter), typeof(EventToCommandBehavior), null);
public string EventName
{
get { return (string)GetValue(EventNameProperty); }
set { SetValue(EventNameProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public IValueConverter Converter
{
get { return (IValueConverter)GetValue(InputConverterProperty); }
set { SetValue(InputConverterProperty, value); }
}
protected override void OnAttachedTo(VisualElement bindable)
{
base.OnAttachedTo(bindable);
RegisterEvent(EventName);
}
protected override void OnDetachingFrom(VisualElement bindable)
{
DeregisterEvent(EventName);
base.OnDetachingFrom(bindable);
}
void RegisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName));
}
MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent");
eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);
eventInfo.AddEventHandler(AssociatedObject, eventHandler);
}
void DeregisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
if (eventHandler == null)
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName));
}
eventInfo.RemoveEventHandler(AssociatedObject, eventHandler);
eventHandler = null;
}
void OnEvent(object sender, object eventArgs)
{
if (Command == null)
{
return;
}
object resolvedParameter;
if (CommandParameter != null)
{
resolvedParameter = CommandParameter;
}
else if (Converter != null)
{
resolvedParameter = Converter.Convert(eventArgs, typeof(object), null, null);
}
else
{
resolvedParameter = eventArgs;
}
if (Command.CanExecute(resolvedParameter))
{
Command.Execute(resolvedParameter);
}
}
static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
{
var behavior = (EventToCommandBehavior)bindable;
if (behavior.AssociatedObject == null)
{
return;
}
string oldEventName = (string)oldValue;
string newEventName = (string)newValue;
behavior.DeregisterEvent(oldEventName);
behavior.RegisterEvent(newEventName);
}
}