2009-04-30 103 views
13

我有一個數據對象中的選項列表,我希望使單選按鈕列表等同於允許用戶選擇其中的一個,並且只能選擇其中一個。功能類似於數據綁定組合框,但採用單選按鈕格式。WPF中的數據綁定單選按鈕列表

傻了我,我以爲這是內置的,但沒有。你怎麼做呢?

回答

23

基本上,在回顧了谷歌搜索結果之後,我開始從an MSDN discussion thread where Dr. WPF provided an answer獲取信息,該信息討論了對ListBox進行樣式設計以使其看起來正確。但是,當列表框被禁用時,背景是一種令人討厭的顏色,在我讀到the MSDN example of the ListBox ControlTemplate之前,我無法擺脫我的生活,直到我看到踢着我的背景屁股的祕密邊框元素。

所以,在這裏最後的答案是這樣的風格:

<Style x:Key="RadioButtonList" TargetType="{x:Type ListBox}"> 
    <!-- ControlTemplate taken from MSDN http://msdn.microsoft.com/en-us/library/ms754242.aspx --> 
    <Setter Property="SnapsToDevicePixels" Value="true"/> 
    <Setter Property="OverridesDefaultStyle" Value="true"/> 
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/> 
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/> 
    <Setter Property="ScrollViewer.CanContentScroll" Value="true"/> 
    <Setter Property="MinWidth" Value="120"/> 
    <Setter Property="MinHeight" Value="95"/> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="ListBox"> 
       <Border 
     Name="Border" 
     Background="Transparent" 
     BorderBrush="Transparent" 
     BorderThickness="0" 
     CornerRadius="2"> 
        <ScrollViewer 
     Margin="0" 
     Focusable="false"> 
         <StackPanel Margin="2" IsItemsHost="True" /> 
        </ScrollViewer> 
       </Border> 
       <ControlTemplate.Triggers> 
        <Trigger Property="IsEnabled" Value="false"> 
         <Setter TargetName="Border" Property="Background" 
       Value="Transparent" /> 
         <Setter TargetName="Border" Property="BorderBrush" 
       Value="Transparent" /> 
        </Trigger> 
        <Trigger Property="IsGrouping" Value="true"> 
         <Setter Property="ScrollViewer.CanContentScroll" Value="false"/> 
        </Trigger> 
       </ControlTemplate.Triggers> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
    <Setter Property="ItemContainerStyle"> 
     <Setter.Value> 
      <Style TargetType="{x:Type ListBoxItem}" > 
       <Setter Property="Margin" Value="2" /> 
       <Setter Property="Template"> 
        <Setter.Value> 
         <ControlTemplate TargetType="{x:Type ListBoxItem}"> 
          <Border Name="theBorder" Background="Transparent"> 
           <RadioButton Focusable="False" 
        IsHitTestVisible="False" 
        IsChecked="{TemplateBinding IsSelected}"> 
            <ContentPresenter /> 
           </RadioButton> 
          </Border> 
         </ControlTemplate> 
        </Setter.Value> 
       </Setter> 
      </Style> 
     </Setter.Value> 
    </Setter> 
</Style> 

這提供了控件模板和樣式,列表框和項目。它變得像這樣使用:

  <ListBox Grid.Column="1" Grid.Row="0" x:Name="TurnChargeBasedOnSelector" Background="Transparent" 
      IsEnabled="{Binding Path=IsEditing}" 
      Style="{StaticResource RadioButtonList}" 
      ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MainForm}}, Path=DataContext.RampTurnsBasedOnList}" 
      DisplayMemberPath="Description" SelectedValuePath="RampTurnsBasedOnID" 
      SelectedValue="{Binding Path=RampTurnsBasedOnID, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" 

            /> 

我越花時間與WPF,我認爲它使平凡的出奇的困難和瘋狂難以瑣碎的更多。請享用。 -Scott

+6

+1對WPF的評論使得微不足道的問題變得困難,反之亦然。我認爲我們在這裏遇到了一些嚴重的早期採用者問題。來自另一場討論的這篇文章有一個很好的解決方案來綁定到枚舉。它與這個解決方案很好地發揮: 2009-07-07 18:47:32

+0

我經常發現它更容易使用當我需要一個重複的模板時,一個ItemsControl比ListBox。 – xr280xr 2013-03-21 18:34:06

+2

@PaulPrewett nope - 那些問題仍然存在;-) – 2015-04-19 19:02:32

6

我已通過ValueConverter完成此操作,該操作將enum轉換爲bool。通過傳遞單選按鈕表示的枚舉值作爲ConverterParameter,轉換器將返回是否應該檢查此單選按鈕。

<Window.Resources> 
    <Converters:EnumConverter x:Key="EnumConverter" /> 
</Window.Resources> 

<RadioButton IsChecked="{Binding Path=MyEnum, Mode=TwoWay, 
           Converter={StaticResource EnumConverter}, 
           ConverterParameter=Enum1}"} 
      Content="Enum 1" /> 
<RadioButton IsChecked="{Binding Path=MyEnum, Mode=TwoWay, 
           Converter={StaticResource EnumConverter}, 
           ConverterParameter=Enum2}"} 
      Content="Enum 2" /> 

EnumConverter定義如下:

public class EnumConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      if (targetType.IsAssignableFrom(typeof(Boolean)) && targetType.IsAssignableFrom(typeof(String))) 
       throw new ArgumentException("EnumConverter can only convert to boolean or string."); 
      if (targetType == typeof(String)) 
       return value.ToString(); 

      return String.Compare(value.ToString(), (String)parameter, StringComparison.InvariantCultureIgnoreCase) == 0; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
     { 
      if (targetType.IsAssignableFrom(typeof(Boolean)) && targetType.IsAssignableFrom(typeof(String))) 
       throw new ArgumentException("EnumConverter can only convert back value from a string or a boolean."); 
      if (!targetType.IsEnum) 
       throw new ArgumentException("EnumConverter can only convert value to an Enum Type."); 

      if (value.GetType() == typeof(String)) 
      { 
       return Enum.Parse(targetType, (String)value, true); 
      } 

      //We have a boolean, as for binding to a checkbox. we use parameter 
      if ((Boolean)value) 
       return Enum.Parse(targetType, (String)parameter, true); 

      return null; 
     } 
    } 

請注意,我不向數據綁定枚舉的列表來生成單選按鈕,我已經由手工完成它們。如果您想通過綁定填充單選按鈕列表,我認爲您需要將IsChecked綁定綁定到MultiBinding,綁定到當前值和收音機的枚舉值,因爲您不能在ConverterParameter上使用綁定。

+0

最後一句是痛苦的真實:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/88a22766-5e6f-4a16-98a6 -1ab39877dd09/ – jan 2011-08-12 08:49:04

10

綁定列表框與有一個屬性名稱的對象列表的ListBox的ItemsSource時(這可以改變)

<ListBox Name="RadioButtonList"> 
    <ListBox.ItemTemplate > 
     <DataTemplate > 
      <RadioButton GroupName="radioList" Tag="{Binding}" Content="{Binding Name}"/> 
     </DataTemplate> 
                </ListBox.ItemTemplate> 
               </ListBox> 

重要組名=「單選列表」

1

我被騙了:

我的解決辦法是綁定列表框programaticly,因爲這是所有似乎爲我工作:

  if (mUdData.Telephony.PhoneLst != null) 
      { 
       lbPhone.ItemsSource = mUdData.Telephony.PhoneLst; 
       lbPhone.SelectedValuePath = "ID"; 
       lbPhone.SelectedValue = mUdData.Telephony.PrimaryFaxID; 
      } 

的XAML看起來是這樣的:

     <ListBox.ItemTemplate > 

         <DataTemplate > 
          <Grid> 
           <Grid.RowDefinitions> 
            <RowDefinition></RowDefinition> 
           </Grid.RowDefinitions> 
           <Grid.ColumnDefinitions> 
            <ColumnDefinition Width="Auto"></ColumnDefinition> 
            <ColumnDefinition Width="Auto"></ColumnDefinition> 
           </Grid.ColumnDefinitions> 

           <RadioButton 
            IsChecked="{Binding Path=PrimaryPhoneID}" 
            GroupName="Phone" 
            x:Name="rbPhone" 
            Content="{Binding Path=PrimaryPhoneID}" 
            Checked="rbPhone_Checked"/> 

           <CheckBox Grid.Column="2" IsEnabled="False" IsChecked="{Binding Path=Active}" Content="{Binding Path=Number}" ></CheckBox> 

          </Grid> 
         </DataTemplate> 
        </ListBox.ItemTemplate> 

而且在我的事件,因爲它選擇了看起來像這樣讀的單選按鈕的值:

private void rbPhone_Checked(object sender, RoutedEventArgs e) 
    { 
     DataRowView dvFromControl = null; 
     dvFromControl = (DataRowView)((RadioButton)sender).DataContext; 

     BindData.Telephony.PrimaryPhoneID = (int)dvFromControl["ID"]; 

    } 

希望幫助別人。

2

我從Jon Benson's blog entry中獲得靈感,但修改了他的解決方案以使用具有description屬性的枚舉。因此,解決方案的關鍵部分變成了:

枚舉帶有介紹

public enum AgeRange { 
    [Description("0 - 18 years")] 
    Youth, 
    [Description("18 - 65 years")] 
    Adult, 
    [Description("65+ years")] 
    Senior, 
} 

代碼閱讀說明和返回鍵/值對的結合。

public static class EnumHelper 
{ 
    public static string ToDescriptionString(this Enum val) 
    { 
     var attribute = 
      (DescriptionAttribute) 
      val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false). 
       SingleOrDefault(); 
     return attribute == default(DescriptionAttribute) ? val.ToString() : attribute.Description; 
    } 

    public static List<KeyValuePair<string,string>> GetEnumValueDescriptionPairs(Type enumType) 
    { 
     return Enum.GetValues(enumType) 
      .Cast<Enum>() 
      .Select(e => new KeyValuePair<string, string>(e.ToString(), e.ToDescriptionString())) 
      .ToList(); 
    } 
} 

在XAML你的對象數據提供商

<ObjectDataProvider 
    ObjectType="{x:Type local:EnumHelper}" 
    MethodName="GetEnumValueDescriptionPairs" 
    x:Key="AgeRanges"> 
    <ObjectDataProvider.MethodParameters> 
     <x:Type TypeName="local:AgeRange" /> 
    </ObjectDataProvider.MethodParameters> 
</ObjectDataProvider> 

你在XAML列表框

<ListBox 
    ItemsSource="{Binding Source={StaticResource AgeRanges}}" 
    SelectedValue="{Binding SelectedAgeRange}" 
    SelectedValuePath="Key"> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <RadioButton 
       IsChecked="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}" 
       Content="{Binding Value}" /> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 

的性質(例如,在您的視圖模型)所綁定到

public class YourViewModel : INotifyPropertyChanged 
{ 
    private AgeRange _selectedAgeRange; 
    public AgeRange SelectedAgeRange 
    { 
    get { return _selectedAgeRange; } 
    set 
    { 
     if (value != _selectedAgeRange) 
     { 
     _selectedAgeRange = value; 
     OnPropertyChanged("SelectedAgeRange"); 
     } 
    } 
    } 
} 
4

對不起,我想將這個迴應斯科特·O公司的崗位作爲他的訊息發表評論,但我還沒有信譽做到這一點。我真的很喜歡他的答案,因爲它是一個只風格的解決方案,因此不需要添加任何代碼隱藏或創建一個自定義的控制等

不過,我也有一個問題,當我後來去試試使用ListBoxItems中的控件。當我使用這種風格,我無法對焦任何包含的控件由於這一行:

<RadioButton Focusable="False" IsHitTestVisible="False" IsChecked="{TemplateBinding IsSelected}">

的單選按鈕,需要關閉調焦和IsHitTestVisible的結合器isChecked正常工作。爲了解決這個問題,我將IsChecked從TemplateBinding更改爲常規綁定,這使我可以將其設置爲雙向綁定。拆除違規設置給了我這一行:

<RadioButton IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected, Mode=TwoWay}">

現在可以讓我集中包含在ListBoxItems任何控件的預期。

希望這會有所幫助。

3

超簡單,MVVM友好,利用DataTemplates的類型。 XAML:

<Window x:Class="WpfApplication1.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:WpfApplication1" 
    Title="MainWindow" Height="350" Width="525"> 

<Window.Resources> 
    <DataTemplate DataType="{x:Type local:Option}"> 
     <RadioButton Focusable="False" 
       IsHitTestVisible="False" 
       Content="{Binding Display}" 
       IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"> 
     </RadioButton> 
    </DataTemplate> 
</Window.Resources> 

<Grid> 
    <ListBox ItemsSource="{Binding Options}" SelectedItem="{Binding SelectedOption}"/> 
</Grid> 

視圖模型等:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     this.DataContext = new Vm(); 
    } 
} 

public class Vm 
{ 
    public Option[] Options { get { return new Option[] { 
     new Option() { Display = "A" }, 
     new Option() { Display = "B" }, 
     new Option() { Display = "C" } }; } } 
    public Option SelectedOption { get; set; } 
} 

public class Option 
{ 
    public string Display { get; set; } 
} 

如果你換你的選擇爲特定類型(或有可能是已經)。您可以爲該類型設置一個DataTemplate,WPF將自動使用它。 (在ListBox資源中定義DataTemplate以限制DataTemplate的應用範圍)。

如果需要,還可以使用DataTemplate中的組名稱來設置組。

這比改變控件模板簡單得多,但它確實意味着你在選定的項目上會出現一條藍線。 (再一次,沒有什麼樣式不能修復)。

當你知道如何WPF很簡單。