這是一個需要一個有趣的項目有時會發生一些黑客攻擊。但我主要通過多重綁定和一對值轉換器來管理它。此示例涵蓋了您請求的每個功能,並已封裝爲單個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;
}
}
的折射率匹配轉換器非常簡單 - 它只是比較兩個指數和返回真或假。容器索引轉換器有點複雜,並且依賴於幾種方法。
現在,最終結果了,100%的數據綁定:
alt text http://img210.imageshack.us/img210/2156/boundradiobuttons.png
的單選按鈕被實時生成和檢查在SelectedOption
財產的每個單選按鈕,結果被你的模型更新。