2009-08-23 146 views
44

什麼是一組3個單選按鈕綁定到值1,2,或3 int類型的屬性的最簡單的方法?簡單的WPF RadioButton綁定?

+0

看一看這個博客條目:> [WPF單選按鈕和數據綁定(http://geekswithblogs.net /claraoscura/archive/2008/10/17/125901.aspx) – Nifle 2009-08-23 07:40:26

+0

請看解決方案[在WPF中的RadioButton的Binding IsChecked屬性](http://pstaev.blogspot.com/2008/10/binding-ischecked-property -of.html),它就像一個魅力。原來的問題已被修復爲WPF 4.0! – 2009-11-06 15:43:14

+1

一個更好,更通用的解決方案可以在這個答案中找到:http://stackoverflow.com/a/406798/414306 – 2012-12-28 12:54:34

回答

31

其實,利用轉換器一樣,打破兩正如我上面所說的,你也不能在枚舉中使用它。更好的方式來做到這一點是針對一個ListBox簡單的款式,就像這樣:

注:相反的是DrWPF.com在他們的例子說明,做把ContentPresenter的單選按鈕內,否則如果添加一個包含按鈕或其他內容的項目,您將無法設置焦點或與其進行交互。這項技術解決了這個問題。此外,您需要處理文本的灰色以及刪除標籤上的邊距,否則將無法正確呈現。這種風格也適合你。

<Style x:Key="RadioButtonListItem" TargetType="{x:Type ListBoxItem}" > 

    <Setter Property="Template"> 
     <Setter.Value> 

      <ControlTemplate TargetType="ListBoxItem"> 

       <DockPanel LastChildFill="True" Background="{TemplateBinding Background}" HorizontalAlignment="Stretch" VerticalAlignment="Center" > 

        <RadioButton IsChecked="{TemplateBinding IsSelected}" Focusable="False" IsHitTestVisible="False" VerticalAlignment="Center" Margin="0,0,4,0" /> 

        <ContentPresenter 
         Content    = "{TemplateBinding ContentControl.Content}" 
         ContentTemplate  = "{TemplateBinding ContentControl.ContentTemplate}" 
         ContentStringFormat = "{TemplateBinding ContentControl.ContentStringFormat}" 
         HorizontalAlignment = "{TemplateBinding Control.HorizontalContentAlignment}" 
         VerticalAlignment = "{TemplateBinding Control.VerticalContentAlignment}" 
         SnapsToDevicePixels = "{TemplateBinding UIElement.SnapsToDevicePixels}" /> 

       </DockPanel> 

      </ControlTemplate> 

     </Setter.Value> 

    </Setter> 

</Style> 

<Style x:Key="RadioButtonList" TargetType="ListBox"> 

    <Style.Resources> 
     <Style TargetType="Label"> 
      <Setter Property="Padding" Value="0" /> 
     </Style> 
    </Style.Resources> 

    <Setter Property="BorderThickness" Value="0" /> 
    <Setter Property="Background"  Value="Transparent" /> 

    <Setter Property="ItemContainerStyle" Value="{StaticResource RadioButtonListItem}" /> 

    <Setter Property="Control.Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type ListBox}"> 
       <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" /> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 

    <Style.Triggers> 
     <Trigger Property="IsEnabled" Value="False"> 
      <Setter Property="TextBlock.Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" /> 
     </Trigger> 
    </Style.Triggers> 

</Style> 

<Style x:Key="HorizontalRadioButtonList" BasedOn="{StaticResource RadioButtonList}" TargetType="ListBox"> 
    <Setter Property="ItemsPanel"> 
     <Setter.Value> 
      <ItemsPanelTemplate> 
       <VirtualizingStackPanel Background="Transparent" Orientation="Horizontal" /> 
      </ItemsPanelTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

你現在有單選按鈕的外觀和感覺,但你可以做雙向綁定,你可以使用一個枚舉。以下是如何...

<ListBox Style="{StaticResource RadioButtonList}" 
    SelectedValue="{Binding SomeVal}" 
    SelectedValuePath="Tag"> 

    <ListBoxItem Tag="{x:Static l:MyEnum.SomeOption}"  >Some option</ListBoxItem> 
    <ListBoxItem Tag="{x:Static l:MyEnum.SomeOtherOption}">Some other option</ListBoxItem> 
    <ListBoxItem Tag="{x:Static l:MyEnum.YetAnother}"  >Yet another option</ListBoxItem> 

</ListBox> 

而且,因爲我們明確地分離出來是tragets的ListBoxItem中,而不是把它內嵌的風格,再次作爲其他例子已經表明,你現在可以創建一個新的風格斷它根據每個項目(如間距)定製事物。 (如果你只是嘗試目標ListBoxItem的配合鍵樣式將覆蓋通用的控制目標,這是行不通的。)

這裏的推杆6保證金每個項目的上面和下面的一個例子。 (請注意,您如何有通過ItemContainerStyle屬性明確應用的樣式,而不是簡單地爲理由ListBox的資源區段定位ListBoxItem的上述說明。)

<Window.Resources> 
    <Style x:Key="SpacedRadioButtonListItem" TargetType="ListBoxItem" BasedOn="{StaticResource RadioButtonListItem}"> 
     <Setter Property="Margin" Value="0,6" /> 
    </Style> 
</Window.Resources> 

<ListBox Style="{StaticResource RadioButtonList}" 
    ItemContainerStyle="{StaticResource SpacedRadioButtonListItem}" 
    SelectedValue="{Binding SomeVal}" 
    SelectedValuePath="Tag"> 

    <ListBoxItem Tag="{x:Static l:MyEnum.SomeOption}"  >Some option</ListBoxItem> 
    <ListBoxItem Tag="{x:Static l:MyEnum.SomeOtherOption}">Some other option</ListBoxItem> 
    <ListBoxItem Tag="{x:Static l:MyEnum.YetAnother}"  >Ter another option</ListBoxItem> 

</ListBox> 

希望這會有所幫助,當然,如果你喜歡這個,請標記爲已接受或投票給我! :)

+32

+1,但如果這是'將3個單選按鈕綁定到值爲1,2或3的int類型屬性的最簡單方法,那麼它對WPF來說並不是很好。它真的超越了我,爲什麼他們使事情變得簡單,讓事情變得如此頭痛難以且不必要的複雜 – 2012-04-30 13:36:44

+1

這實際上不僅僅是因爲它設計了一個列表框,而是一個單一的控件,所以綁定的工作原理如此簡單(一旦你獲得通過瘋狂的設置。)但是,我同意RadioButton類肯定有一些綁定的缺點。當然,你可以用代碼隱藏的方式做得更簡單,但是在這裏這種方式,你只需要做一次樣式等,然後稍後使用它就容易多了。 – MarqueIV 2012-04-30 15:43:54

+2

OMG,簡單嗎?儘管對你的回答和努力給予了應有的尊重,但稱這種「簡單的風格」卻相當牽強,不是嗎? WPF和Winforms的核心思想和感知優勢不是更好的數據綁定?在WPF投放市場多年之後,一個簡單的單選按鈕(和單選按鈕分組)如何破壞綁定是我無法理解的。對MS感到羞恥。 – 2015-03-18 10:49:23

67

我想出了一個簡單的解決方案。

我有一個model.cs類:

private int _isSuccess; 
public int IsSuccess { get { return _isSuccess; } set { _isSuccess = value; } } 

我Window1.xaml.cs用的DataContext設置爲model.cs文件。 XAML中包含了單選按鈕:

<RadioButton IsChecked="{Binding Path=IsSuccess, Converter={StaticResource radioBoolToIntConverter}, ConverterParameter=1}" Content="one" /> 
<RadioButton IsChecked="{Binding Path=IsSuccess, Converter={StaticResource radioBoolToIntConverter}, ConverterParameter=2}" Content="two" /> 
<RadioButton IsChecked="{Binding Path=IsSuccess, Converter={StaticResource radioBoolToIntConverter}, ConverterParameter=3}" Content="three" /> 

這裏是我的轉換器:

public class RadioBoolToIntConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     int integer = (int)value; 
     if (integer==int.Parse(parameter.ToString())) 
      return true; 
     else 
      return false; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     return parameter; 
    } 
} 

當然,在窗口1的資源:

<Window.Resources> 
    <local:RadioBoolToIntConverter x:Key="radioBoolToIntConverter" /> 
</Window.Resources> 
+0

這個轉換器的問題是它不適用於枚舉作爲參數(你的ToString()調用死亡。)看到我的答案在下面。 – MarqueIV 2010-11-07 21:43:38

+1

你只需要改變你的轉換器來解析枚舉 – 2012-04-12 21:05:52

+0

再一次,這不適用於雙向綁定。 'Convert Back'調用會嘗試讓多個事物設置不同的綁定值,在這種情況下,當您嘗試將屬性IsSuccess設置爲一個值時遇到競爭條件,而另一個嘗試將其設置爲不同的值。這是這種方法的問題。當然,這是更少的代碼,但你仍然有單獨的控件都試圖改變一個屬性。 (記住,當你選擇一個單選按鈕時,它會取消選擇另一個單選按鈕,因此會有兩個更改。) – MarqueIV 2012-07-14 00:38:20

14

我知道它的方式方法姍姍來遲,但我有一個替代的解決方案,這是更輕,更簡單。從System.Windows.Controls.RadioButton派生類並聲明兩個依賴項屬性RadioValueRadioBinding。然後在類代碼,重寫OnChecked並設置RadioBinding屬性值的RadioValue屬性值。在另一方向,陷阱使用回調改變爲RadioBinding屬性,並且如果新的值等於RadioValue屬性的值,其IsChecked屬性設置爲true

下面的代碼:

public class MyRadioButton : RadioButton 
{ 
    public object RadioValue 
    { 
     get { return (object)GetValue(RadioValueProperty); } 
     set { SetValue(RadioValueProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for RadioValue. 
     This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty RadioValueProperty = 
     DependencyProperty.Register(
      "RadioValue", 
      typeof(object), 
      typeof(MyRadioButton), 
      new UIPropertyMetadata(null)); 

    public object RadioBinding 
    { 
     get { return (object)GetValue(RadioBindingProperty); } 
     set { SetValue(RadioBindingProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for RadioBinding. 
     This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty RadioBindingProperty = 
     DependencyProperty.Register(
      "RadioBinding", 
      typeof(object), 
      typeof(MyRadioButton), 
      new FrameworkPropertyMetadata(
       null, 
       FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
       OnRadioBindingChanged)); 

    private static void OnRadioBindingChanged(
     DependencyObject d, 
     DependencyPropertyChangedEventArgs e) 
    { 
     MyRadioButton rb = (MyRadioButton)d; 
     if (rb.RadioValue.Equals(e.NewValue)) 
      rb.SetCurrentValue(RadioButton.IsCheckedProperty, true); 
    } 

    protected override void OnChecked(RoutedEventArgs e) 
    { 
     base.OnChecked(e); 
     SetCurrentValue(RadioBindingProperty, RadioValue); 
    } 
} 

XAML用法:

<my:MyRadioButton GroupName="grp1" Content="Value 1" 
    RadioValue="val1" RadioBinding="{Binding SelectedValue}"/> 
<my:MyRadioButton GroupName="grp1" Content="Value 2" 
    RadioValue="val2" RadioBinding="{Binding SelectedValue}"/> 
<my:MyRadioButton GroupName="grp1" Content="Value 3" 
    RadioValue="val3" RadioBinding="{Binding SelectedValue}"/> 
<my:MyRadioButton GroupName="grp1" Content="Value 4" 
    RadioValue="val4" RadioBinding="{Binding SelectedValue}"/> 

希望有人認爲這有用的這段時間:)

17

我很驚訝,沒有人想出了這個類。的解決方案將其綁定到bool陣列上。它可能不是最乾淨的,但它可以很輕鬆的使用:

private bool[] _modeArray = new bool[] { true, false, false}; 
public bool[] ModeArray 
{ 
    get { return _modeArray ; } 
} 
public int SelectedMode 
{ 
    get { return Array.IndexOf(_modeArray, true); } 
} 

在XAML:

<RadioButton GroupName="Mode" IsChecked="{Binding Path=ModeArray[0], Mode=TwoWay}"/> 
<RadioButton GroupName="Mode" IsChecked="{Binding Path=ModeArray[1], Mode=TwoWay}"/> 
<RadioButton GroupName="Mode" IsChecked="{Binding Path=ModeArray[2], Mode=TwoWay}"/> 

注意:你不需要雙向綁定,如果你不想給一個默認選中。雙向綁定是這種解決方案的最大缺點。

優點:

  • 無需代碼背後
  • 無需額外的類(IValue轉換器)
  • 無需額外枚舉
  • 犯規需要的bizzare結合
  • 簡單,易於瞭解
  • 不違反MVVM(嘿,至少我希望如此)
+0

我也很驚訝。此解決方案適用於那些WPF新手,只知道如何進行綁定並希望快速綁定單選按鈕。這非常簡單直接。 +1 – QuantumHive 2014-08-26 11:53:56

+0

這是很好的,在寫出我自己的'Converter'之前,很快就會投入測試。 +1 – 2015-01-07 19:59:16

+1

只是一個小建議:從C#6開始,你不再需要支持領域了。只需使用AutoProperty:public bool [] ModeArray {get; } = new bool [] {true,false,false}; – oopbase 2016-05-12 08:08:42

0

這個例子看起來有點冗長,但其意圖應該很清楚。

它在ViewModel中使用3個布爾屬性,FlagForValue1,FlagForValue2FlagForValue3。 這三個屬性中的每一個都由稱爲_intValue的單個私人字段支持。

view(xaml)的3個單選按鈕每個綁定到視圖模型中相應的Flag屬性。這意味着顯示「值1」的單選按鈕綁定到視圖模型中的FlagForValue1布爾屬性,其他兩個相應地綁定。

當設置在視圖模型的屬性之一(例如FlagForValue1),它重要也提高屬性改變爲其他兩個屬性事件(例如FlagForValue2,和FlagForValue3),所以在UI(WPF INotifyPropertyChanged基礎設施)可以選擇/正確取消每個單選按鈕。

private int _intValue; 

    public bool FlagForValue1 
    { 
     get 
     { 
      return (_intValue == 1) ? true : false; 
     } 
     set 
     { 
      _intValue = 1; 
      RaisePropertyChanged("FlagForValue1"); 
      RaisePropertyChanged("FlagForValue2"); 
      RaisePropertyChanged("FlagForValue3"); 
     } 
    } 

    public bool FlagForValue2 
    { 
     get 
     { 
      return (_intValue == 2) ? true : false; 
     } 
     set 
     { 
      _intValue = 2; 
      RaisePropertyChanged("FlagForValue1"); 
      RaisePropertyChanged("FlagForValue2"); 
      RaisePropertyChanged("FlagForValue3"); 
     } 
    } 

    public bool FlagForValue3 
    { 
     get 
     { 
      return (_intValue == 3) ? true : false; 
     } 
     set 
     { 
      _intValue = 3; 
      RaisePropertyChanged("FlagForValue1"); 
      RaisePropertyChanged("FlagForValue2"); 
      RaisePropertyChanged("FlagForValue3"); 
     } 
    } 

的XAML看起來是這樣的:

   <RadioButton GroupName="Search" IsChecked="{Binding Path=FlagForValue1, Mode=TwoWay}" 
          >Value 1</RadioButton> 

       <RadioButton GroupName="Search" IsChecked="{Binding Path=FlagForValue2, Mode=TwoWay}" 
          >Value 2</RadioButton> 

       <RadioButton GroupName="Search" IsChecked="{Binding Path=FlagForValue3, Mode=TwoWay}" 
          >Value 3</RadioButton> 
+0

請添加一些解釋 – 2015-07-23 07:45:01

+0

醜陋像罪,但基本上我也是這麼做的。 – 2016-10-02 05:49:34

1

有時可以解決它在這樣的模式: 假設你有3個布爾屬性OptionA,OptionB,OptionC中。

XAML:

<RadioButton IsChecked="{Binding OptionA}"/> 
<RadioButton IsChecked="{Binding OptionB}"/> 
<RadioButton IsChecked="{Binding OptionC}"/> 

CODE:

private bool _optionA; 
public bool OptionA 
{ 
    get { return _optionA; } 
    set 
    { 
     _optionA = value; 
     if(_optionA) 
     { 
      this.OptionB= false; 
      this.OptionC = false; 
     } 
    } 
} 

private bool _optionB; 
public bool OptionB 
{ 
    get { return _optionB; } 
    set 
    { 
     _optionB = value; 
     if(_optionB) 
     { 
      this.OptionA= false; 
      this.OptionC = false; 
     } 
    } 
} 

private bool _optionC; 
public bool OptionC 
{ 
    get { return _optionC; } 
    set 
    { 
     _optionC = value; 
     if(_optionC) 
     { 
      this.OptionA= false; 
      this.OptionB = false; 
     } 
    } 
} 

你的想法。 不是最乾淨的事情,但容易。

0

我想出瞭解決方案,使用Binding.DoNothing從轉換器返回不會破壞雙向綁定。

public class EnumToCheckedConverter : IValueConverter 
{ 
    public Type Type { get; set; } 

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (value != null && value.GetType() == Type) 
     { 
      try 
      { 
       var parameterFlag = Enum.Parse(Type, parameter as string); 

       if (Equals(parameterFlag, value)) 
       { 
        return true; 
       } 
      } 
      catch (ArgumentNullException) 
      { 
       return false; 
      } 
      catch (ArgumentException) 
      { 
       throw new NotSupportedException(); 
      } 

      return false; 
     } 
     else if (value == null) 
     { 
      return false; 
     } 

     throw new NotSupportedException(); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (value != null && value is bool check) 
     { 
      if (check) 
      { 
       try 
       { 
        return Enum.Parse(Type, parameter as string); 
       } 
       catch(ArgumentNullException) 
       { 
        return Binding.DoNothing; 
       } 
       catch(ArgumentException) 
       { 
        return Binding.DoNothing; 
       } 
      } 

      return Binding.DoNothing; 
     } 

     throw new NotSupportedException(); 
    } 
} 

用法:

<converters:EnumToCheckedConverter x:Key="SourceConverter" Type="{x:Type monitor:VariableValueSource}" /> 

單選按鈕綁定:

<RadioButton GroupName="ValueSource" 
      IsChecked="{Binding Source, Converter={StaticResource SourceConverter}, ConverterParameter=Function}">Function</RadioButton>