我在水平线上有一个带有radiobutton的ListBox。单选按钮的数量是可选的。每个单选按钮的文本取自模型列表。选择哪个单选按钮将由属性SelectedOption确定。如果没有,则选择它应设置为-1。 问题是我希望除了模型提供的选择之外,我还希望选择“不知道”将SelectedOption放到-1。如何为我的ListBox编写XAML来获取它?
我还希望“不知道”有另一种背景颜色和边距。
型号:
IEnumerable<String> Descriptions
- 除了“不知道”Int SelectedOption
- 所选说明的索引。 -1如果选择“不知道”示例:
---------------------------------------------------------
| () Option1 () Option2 () Option3 () Don’t know |
---------------------------------------------------------
()
是一个单选按钮
() Don’t know
有另一种背景颜色
答案 0 :(得分:3)
这是一个有趣的项目,需要不时进行一些黑客攻击。但我主要通过多绑定和几个值转换器来管理它。此示例涵盖您请求的每个功能,并且已封装到单个Window
中以便于演示。首先,让我们从窗口的XAML开始,大部分魔法发生在那里:
<Window x:Class="TestWpfApplication.BoundRadioButtonListBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:TestWpfApplication"
Title="BoundRadioButtonListBox" Height="200" Width="500"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<local:ItemContainerToIndexConverter x:Key="ItemContainerToIndexConverter"/>
<local:IndexMatchToBoolConverter x:Key="IndexMatchToBoolConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Models}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ItemsControl x:Name="DescriptionList" ItemsSource="{Binding Descriptions}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding}" Margin="5"
Command="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ItemsControl}}, Path=DataContext.CheckCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Tag}"
GroupName="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ItemsControl}}, Path=DataContext.GroupName}">
<RadioButton.Tag>
<MultiBinding Converter="{StaticResource ItemContainerToIndexConverter}">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}"
Mode="OneWay"/>
<Binding RelativeSource="{RelativeSource Self}"
Path="DataContext"/>
</MultiBinding>
</RadioButton.Tag>
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource IndexMatchToBoolConverter}">
<Binding RelativeSource="{RelativeSource Self}"
Path="Tag"/>
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}"
Path="DataContext.SelectedOption"/>
</MultiBinding>
</RadioButton.IsChecked>
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Border Background="LightGray" Margin="15,5">
<RadioButton Content="Don't Know"
Command="{Binding CheckCommand}"
GroupName="{Binding GroupName}">
<RadioButton.CommandParameter>
<sys:Int32>-1</sys:Int32>
</RadioButton.CommandParameter>
</RadioButton>
</Border>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="1">
<Label>The selected index for each line is shown here:</Label>
<ItemsControl ItemsSource="{Binding Models}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding SelectedOption}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
这里的诀窍是第一个ListBox
绑定到顶级模型。每个模型ItemTemplate
创建另一个嵌入式ItemsControl
,我们用它来显示项目描述。这就是我们如何支持动态数量的描述(这适用于任何数字)。
接下来,让我们看看这个窗口的代码隐藏:
/// <summary>
/// Interaction logic for BoundRadioButtonListBox.xaml
/// </summary>
public partial class BoundRadioButtonListBox : Window
{
public ObservableCollection<LineModel> Models
{
get;
private set;
}
public BoundRadioButtonListBox()
{
Models = new ObservableCollection<LineModel>();
List<string> descriptions = new List<string>()
{
"Option 1", "Option 2", "Option 3"
};
LineModel model = new LineModel(descriptions, 2);
Models.Add(model);
descriptions = new List<string>()
{
"Option A", "Option B", "Option C", "Option D"
};
model = new LineModel(descriptions, 1);
Models.Add(model);
InitializeComponent();
}
}
public class LineModel : DependencyObject
{
public IEnumerable<String> Descriptions
{
get;
private set;
}
public static readonly DependencyProperty SelectedOptionProperty =
DependencyProperty.Register("SelectedOption", typeof(int), typeof(LineModel));
public int SelectedOption
{
get { return (int)GetValue(SelectedOptionProperty); }
set { SetValue(SelectedOptionProperty, value); }
}
public ICommand CheckCommand
{
get;
private set;
}
public string GroupName
{
get;
private set;
}
private static int Index = 1;
public LineModel(IEnumerable<String> descriptions, int selected)
{
GroupName = String.Format("Group{0}", Index++);
Descriptions = descriptions;
SelectedOption = selected;
CheckCommand = new RelayCommand((index) => SelectedOption = ((int)index));
}
}
所有这一切都应该非常明确。 LineModel
类代表您在问题中描述的模型。因此,它具有一系列字符串描述以及SelectedOption
属性,该属性已成为DependencyProperty
以进行自动更改通知。
接下来,两个值转换器的代码:
public class ItemContainerToIndexConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 2 &&
values[0] is ItemsControl &&
values[1] is string)
{
ItemsControl control = values[0] as ItemsControl;
ContentPresenter item = control.ItemContainerGenerator.ContainerFromItem(values[1]) as ContentPresenter;
return control.ItemContainerGenerator.IndexFromContainer(item);
}
return -1;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
public class IndexMatchToBoolConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 2 &&
values[0] is int &&
values[1] is int)
{
return (int)values[0] == (int)values[1];
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
索引匹配转换器非常简单 - 只比较两个索引并返回true或false。索引转换器的容器有点复杂,并且依赖于一些ItemContainerGenerator
方法。
现在,完成的结果,100%数据绑定:
alt text http://img210.imageshack.us/img210/2156/boundradiobuttons.png
即时生成单选按钮,检查每个单选按钮会导致模型上的SelectedOption
属性更新。