Let's say I have a list (editable) of some options. I want to have a maintainable list of items that a user has picked. I want the following UI for picking items: ListBox of pairs "option name", "ispicked". I hope ASCII art below will make things more clear:
All cars Select! Selected cars
____________ _________________ ____________
|BMW | |BMW | [x] | |BMW |
|Audi | |Audi | [x] | |Audi |
|Volkswagen| |Volkswagen| [x] | |Volkswagen|
|Honda | |Honda | [ ] | | |
|Toyota | |Toyota | [ ] | | |
|Nissan | |Nissan | [ ] | | |
|Ford | |Ford | [ ] | | |
|__________| |__________|_____| |__________|
If a user marks "Nissan" checkbox everything should like:
All cars Select! Selected cars
____________ _________________ ____________
|BMW | |BMW | [x] | |BMW |
|Audi | |Audi | [x] | |Audi |
|Volkswagen| |Volkswagen| [x] | |Volkswagen|
|Honda | |Honda | [ ] | |Hond |
|Toyota | |Toyota | [ ] | |Nissan |
|Nissan | |Nissan | [x] | | |
|Ford | |Ford | [ ] | | |
|__________| |__________|_____| |__________|
Adding "Nissan" to collection of selected cars should mark corresponding checkbox in the middle ListBox. Removing an Item from the collection of all cars should remove it's name from second and third ListBoxes. E.g. removing Volkswagen from the second picture should result in:
All cars Select! Selected cars
____________ _________________ ____________
|BMW | |BMW | [x] | |BMW |
|Audi | |Audi | [x] | |Audi |
|Honda | |Honda | [ ] | |Hond |
|Toyota | |Toyota | [ ] | |Nissan |
|Nissan | |Nissan | [x] | | |
|Ford | |Ford | [ ] | | |
|__________| |__________|_____| |__________|
Here is some viewmodel code:
[ImplementPropertyChanged]
public class ChoiceViewModel : ViewModelBase
{
public string Title { get; set; }
public bool IsChecked { get; set; }
}
[ImplementPropertyChanged]
public class TestViewModel : ViewModelBase
{
public ObservableCollection<string> AllOptions { get; set; }
public ObservableCollection<string> SelectedOptions { get; set; }
public ObservableCollection<ChoiceViewModel> Choices { get; set; }
public TestViewModel()
{
AllOptions = new ObservableCollection<string>();
SelectedOptions = new ObservableCollection<string>();
Choices = new ObservableCollection<ChoiceViewModel>();
if(IsInDesignMode)
{
AllOptions.Add("BMW");
AllOptions.Add("Audi");
AllOptions.Add("Volkswagen");
AllOptions.Add("Honda");
AllOptions.Add("Toyota");
AllOptions.Add("Nissan");
AllOptions.Add("Ford");
// German cars ftw!
SelectedOptions.Add("BMW");
SelectedOptions.Add("Audi");
SelectedOptions.Add("Volkswagen");
}
}
}
And the XAML for window:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
DataContext="{Binding TestViewModel">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="22" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock Text="All cars" Grid.Row="0" Grid.Column="0" />
<DataGrid ItemsSource="{Binding AllOptions}" Grid.Row="1" Grid.Column="0" Margin="10"
AutoGenerateColumns="False"
CanUserAddRows="True"
CanUserDeleteRows="True">
<DataGrid.Columns>
<DataGridTextColumn Width="1*" Binding="{Binding}"/>
</DataGrid.Columns>
</DataGrid>
<TextBlock Text="Select!" Grid.Row="0" Grid.Column="1" />
<ListBox ItemsSource="{Binding Choices}" Grid.Row="1" Grid.Column="1" Margin="10" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}" />
<CheckBox IsChecked="{Binding IsChecked}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Text="Selected cars" Grid.Row="0" Grid.Column="2" />
<DataGrid ItemsSource="{Binding SelectedOptions}" Grid.Row="1" Grid.Column="2" Margin="10"
AutoGenerateColumns="False"
CanUserAddRows="True"
CanUserDeleteRows="True">
<DataGrid.Columns>
<DataGridTextColumn Width="1*" Binding="{Binding}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
So the question is how to sync AllOptions
, SelectedOptions
and Choices
collections WPF\MVVM style?
Edit:
I've ended up with the accepted answer. The limitation of SelectedItems binding can be circumvented either by making ListBoxEx
wrapper with additional BindableSelectedItems
dependency property exposed or by using attached properteis. See this question for details