2010-04-14 67 views
0

我在水平線上有一個帶單選按鈕的列表框。單選按鈕的數量是可選的。每個單選按鈕的文本取自模型列表。選擇哪個單選按鈕將由屬性SelectedOption決定。如果沒有選擇,它應該設置爲-1。 問題是我希望除了模型提供的選擇外,我還希望有一個選擇「不知道」將SelectedOption置於-1。我如何編寫我的ListBox的XAML來獲得這個?使用XAML在列表框中添加額外的行

我也想「不知道」有另一種背景顏色和邊距。

型號:

  • IEnumerable<String> Descriptions - 描述文本的可用選項,除了 「不知道」
  • Int SelectedOption - 選擇說明的指數。 -1如果 「不知道」 被選中

例子:

--------------------------------------------------------- 
|() Option1() Option2() Option3  () Don’t know | 
--------------------------------------------------------- 

()是一個單選按鈕
() Don’t know有另一種背景顏色

回答

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; 
    } 
} 

的折射率匹配轉換器非常簡單 - 它只是比較兩個指數和返回真或假。容器索引轉換器有點複雜,並且依賴於幾種方法。

現在,最終結果了,100%的數據綁定:

alt text http://img210.imageshack.us/img210/2156/boundradiobuttons.png

的單選按鈕被實時生成和檢查在SelectedOption財產的每個單選按鈕,結果被你的模型更新。