Reputation: 318
I have the following requirements:
ListView
with multiple items.As of now, I have the following incomplete code.
MainWindow XAML:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="520.149" Width="732.463">
<Window.Resources>
<ResourceDictionary Source="MainWindowResource.xaml" />
</Window.Resources>
<Grid>
<ListView x:Name="myListBox" ItemTemplate="{StaticResource OfferingTemplate}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>
</Window>
DataTemplete for ListView:
<DataTemplate x:Key="OfferingTemplate">
<StackPanel>
<Grid IsEnabled="{Binding IsEnabled}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8"></ColumnDefinition>
<ColumnDefinition Width="120"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<Rectangle Grid.Column="0" Grid.RowSpan="3" Fill="#F4CA16" />
<Label
Grid.Column="1"
Grid.Row="0"
Content="{Binding Title}"
FontSize="18" FontWeight="Bold"
Margin="0,0,0,0" />
<TextBlock
Grid.Column="1"
Grid.Row="1"
FontSize="10"
Text="{Binding Description}"
Foreground="Black"
TextWrapping="WrapWithOverflow"
Margin="5,0,0,0" />
<CheckBox
Grid.Column="1"
Grid.Row="2"
FontSize="14"
IsChecked="{Binding IsSelected}"
VerticalAlignment="Bottom"
Margin="5,0,0,0">
<TextBlock Text="Select" Margin="0,-2,0,0"/>
</CheckBox>
</Grid>
</StackPanel>
</DataTemplate>
Model:
class MyModel
{
public string Title { get; set; }
public string Description { get; set; }
public bool IsSelected { get; set; }
public bool IsEnabled { get; set; }
}
ViewModel:
class MyViewModel : INotifyPropertyChanged
{
private MyModel offering;
public MyViewModel()
{
offering = new MyModel();
}
public int ID { get; set; }
public string Title
{
get { return offering.Title; }
set
{
offering.Title = value;
RaisePropertyChanged("Title");
}
}
public string Description
{
get { return offering.Description; }
set
{
offering.Description = value;
RaisePropertyChanged("Description");
}
}
public bool IsSelected
{
get { return offering.IsSelected; }
set
{
offering.IsSelected = value;
RaisePropertyChanged("IsSelected");
}
}
public bool IsEnabled
{
get { return offering.IsEnabled; }
set
{
offering.IsEnabled = value;
RaisePropertyChanged("IsEnabled");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Upvotes: 4
Views: 8971
Reputation: 1919
This is an interesting question. Since the action you want applies to all items in the list, this logic should in list class level. Your MyViewModel class is fine. You need add some logic in your list class and XAML but thanks to Prism, it is quite easy.
The list class (not shown in your post) Contains:
public ObservableCollection<MyViewModel> MyItems { get; set; } //Binding to ItemsSource
private ICommand _selectCommand;
public ICommand SelectCommand
{
get { return _selectCommand ?? (_selectCommand = new DelegateCommand<MyViewModel>(DoSelect)); }
}
private void DoSelect(MyViewModel myViewModel)
{
foreach(var item in MyItems)
if (item != myViewModel)
{
item.IsSelected = false;
item.IsEnabled = false;
}
}
private ICommand _unselectCommand;
public ICommand UnselectCommand
{
get { return _unselectCommand ?? (_unselectCommand = new DelegateCommand<MyViewModel>(DoUnselect)); }
}
private void DoUnselect(MyViewModel myViewModel)
{
foreach (var item in MyItems)
if (item != myViewModel)
{
item.IsEnabled = true;
}
}
There are two commands, one for selecting and the other for unselecting. The magic is on XAML:
<ListView ItemsSource="{Binding Path=MyItems}" x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsSelected}" IsEnabled="{Binding Path=IsEnabled}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding ElementName=listView, Path=DataContext.SelectCommand}"
CommandParameter="{Binding}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<i:InvokeCommandAction Command="{Binding ElementName=listView, Path=DataContext.UnselectCommand}"
CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Using Prism's triggers, you can map CheckBox's Checked and Unchecked event to your list view model's commands and passing the item view model as parameter.
It is working perfectly but one thing is annoying, that setting item's IsSelected is separate. When you check a CheckBox, the item behind is set to true through DataBinding but all others are set through parent view model. If your post is all your requirement, you can remove IsChecked binding and put the logic of setting one IsSelected inside list view model, which looks clenaer and easier to write test code.
Upvotes: 5