假设我有一些选项的列表(可编辑)。我想要一个用户选择的项目的可维护列表。我想要以下 UI 来挑选项目:ListBox 对“选项名称”,“ispicked”。我希望下面的 ASCII 艺术能让事情变得更清楚:
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 | [ ] | | |
|__________| |__________|_____| |__________|
如果用户标记“Nissan”复选框,一切都应该是:
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 | [ ] | | |
|__________| |__________|_____| |__________|
将“Nissan”添加到所选汽车的集合应在中间的 ListBox 中标记相应的复选框。从所有汽车的集合中删除一个项目应该从第二个和第三个 ListBoxes 中删除它的名称。例如。从第二张图片中删除大众汽车应该会导致:
All cars Select! Selected cars
____________ _________________ ____________
|BMW | |BMW | [x] | |BMW |
|Audi | |Audi | [x] | |Audi |
|Honda | |Honda | [ ] | |Hond |
|Toyota | |Toyota | [ ] | |Nissan |
|Nissan | |Nissan | [x] | | |
|Ford | |Ford | [ ] | | |
|__________| |__________|_____| |__________|
这是一些 View 模型代码:
[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");
}
}
}
和窗口的 XAML:
<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>
所以问题是如何同步
AllOptions
, SelectedOptions
和 Choices
集合 WPF\MVVM 样式?编辑:
我最终得到了接受的答案。可以通过设置
ListBoxEx
来规避 SelectedItems 绑定(bind)的限制。带有附加 BindableSelectedItems
的包装器依赖属性暴露或使用附加属性。见 this询问详情
最佳答案
显然有多种方法可以解决这个问题,但我倾向于使用单个集合——这样你根本不需要同步。
SelectedItems
属性(property)。 棘手的部分是创建复选框样式列表,但我相信这比同步 2 或 3 个单独的列表和 subview 模型更简单。
编辑
下面是一个简单的例子。
<Grid>
<Grid.Resources>
<Style TargetType="ListBox" x:Key="CheckBoxListStyle">
<Setter Property="SelectionMode" Value="Multiple" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding}" />
<CheckBox Grid.Column="1"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem},Path=IsSelected,Mode=TwoWay}"
/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding AllOptions}" />
<ListBox x:Name="selectionList"
Grid.Column="1"
ItemsSource="{Binding AllOptions}" Style="{StaticResource CheckBoxListStyle}"
/>
<ListBox Grid.Column="2"
ItemsSource="{Binding ElementName=selectionList,Path=SelectedItems}"
/>
</Grid>
编辑#2
现在我不喜欢这个解决方案了,因为我看到“SelectedItems”列表没有保留原始排序。
关于WPF:将所有选项列表和选定选项列表绑定(bind)到带有选择复选框的列表框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27377437/