【问题标题】:MVVM: Binding radio buttons to a view model?MVVM:将单选按钮绑定到视图模型?
【发布时间】:2011-01-18 02:10:43
【问题描述】:

编辑:问题已在 .NET 4.0 中修复。

我一直在尝试使用IsChecked 按钮将一组单选按钮绑定到视图模型。在查看其他帖子后,IsChecked 属性似乎根本不起作用。我整理了一个简短的演示来重现该问题,我将其包含在下面。

这是我的问题:是否有使用 MVVM 绑定单选按钮的简单可靠的方法?谢谢。

其他信息:IsChecked 属性不起作用有两个原因:

  1. 当一个按钮被选中时,组中其他按钮的 IsChecked 属性不会设置为 false

  2. 当一个按钮被选中时,它自己的 IsChecked 属性在第一次被选中后不会被设置。我猜该绑定在第一次点击时会被 WPF 丢弃。

演示项目:这是重现问题的简单演示的代码和标记。创建一个 WPF 项目并将 Window1.xaml 中的标记替换为以下内容:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
    <StackPanel>
        <RadioButton Content="Button A" IsChecked="{Binding Path=ButtonAIsChecked, Mode=TwoWay}" />
        <RadioButton Content="Button B" IsChecked="{Binding Path=ButtonBIsChecked, Mode=TwoWay}" />
    </StackPanel>
</Window>

将 Window1.xaml.cs 中的代码替换为以下代码(hack),该代码设置视图模型:

using System.Windows;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.DataContext = new Window1ViewModel();
        }
    }
}

现在将以下代码作为Window1ViewModel.cs添加到项目中:

using System.Windows;

namespace WpfApplication1
{
    public class Window1ViewModel
    {
        private bool p_ButtonAIsChecked;

        /// <summary>
        /// Summary
        /// </summary>
        public bool ButtonAIsChecked
        {
            get { return p_ButtonAIsChecked; }
            set
            {
                p_ButtonAIsChecked = value;
                MessageBox.Show(string.Format("Button A is checked: {0}", value));
            }
        }

        private bool p_ButtonBIsChecked;

        /// <summary>
        /// Summary
        /// </summary>
        public bool ButtonBIsChecked
        {
            get { return p_ButtonBIsChecked; }
            set
            {
                p_ButtonBIsChecked = value;
                MessageBox.Show(string.Format("Button B is checked: {0}", value));
            }
        }

    }
}

要重现此问题,请运行应用程序并单击按钮 A。将出现一个消息框,说明按钮 A 的 IsChecked 属性已设置为 true。现在选择按钮 B。会出现另一个消息框,说按钮 B 的 IsChecked 属性已设置为 true,但没有消息框表明按钮 A 的 IsChecked 属性已设置为false--该属性没有改变。

现在再次单击按钮 A。该按钮将在窗口中被选中,但不会出现消息框——IsChecked 属性未更改。最后,再次单击按钮 B——同样的结果。在第一次单击按钮后,任何一个按钮的 IsChecked 属性都不会更新。

【问题讨论】:

  • +1 只是因为您发布了所有代码来重现它。
  • 谢谢——我错过了。很有帮助。
  • 您能否在您的问题上加一个粗体字,即您的问题只是目标框架 3.5 的问题。该问题已在 .NET 4.0 中修复,您的演示项目在 .NET 4 中按预期运行。我刚刚在两个平台上测试了您的演示项目,您的问题只能在 .NET 3.5 中重现(另请参阅下面的 RandomEngy 的回答谁确认这个)。

标签: c# wpf mvvm binding radio-button


【解决方案1】:

不确定是否存在任何 IsChecked 错误,您可以对视图模型进行一种可能的重构:视图具有许多由一系列 RadioButtons 表示的互斥状态,在任何给定时间只能选择其中一个。在视图模型中,只有 1 个属性(例如枚举)代表可能的状态:stateA、stateB 等这样您就不需要所有单独的 ButtonAIsChecked 等

【讨论】:

  • 谢谢,但这并不能真正解决问题。即使进行了重构,您仍然会遇到第二次单击按钮失败的问题。
【解决方案2】:

如果您从 Jason 的建议开始,那么问题就变成了列表中的单个绑定选择,该列表很好地转换为 ListBox。此时,将样式应用于ListBox 控件以使其显示为RadioButton 列表是微不足道的。

<ListBox ItemsSource="{Binding ...}" SelectedItem="{Binding ...}">
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <RadioButton Content="{TemplateBinding Content}"
                                     IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

【讨论】:

  • 列表框是建议的最佳解决方案。但请参阅 stackoverflow.com/questions/883246/mvvm-radiobuttons 了解另一种解决方案。
  • Jaime Rodriguez 有一个非常简单的解决方案——将每个 RadioButton 的 GroupName 设置为不同的值。将每个按钮放在不同的组中可以防止在选择按钮时绑定被清除。请参阅我在之前的评论中提到的线程。在我的应用中,列表框解决方案效果最好,因此它是选定的答案。
  • 是的,有很多不同的方法可以解决这个问题。我还曾经使用一种依赖于转换器的方法,该转换器将为未选中的按钮返回 Binding.DoNothing 以避免破坏绑定,但最终更频繁地使用它,因为它的代码更少。良好的对话开始!
  • 当我第一次意识到我的问题时,我很担心,但这篇文章让一切变得更好。
  • DisplayMember 好像没有效果,它显示对象。
【解决方案3】:

一种解决方案是更新属性设置器中单选按钮的 ViewModel。当 Button A 设置为 True 时,将 Button B 设置为 false。

绑定到 DataContext 中的对象时的另一个重要因素是该对象应实现 INotifyPropertyChanged。当任何绑定属性更改时,应触发事件并包含更改的属性的名称。 (为简洁起见,示例中省略了空值检查。)

public class ViewModel  : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool _ButtonAChecked = true;
    public bool ButtonAChecked
    {
        get { return _ButtonAChecked; }
        set 
        { 
            _ButtonAChecked = value;
            PropertyChanged(this, new PropertyChangedEventArgs("ButtonAChecked"));
            if (value) ButtonBChecked = false;
        }
    }

    protected bool _ButtonBChecked;
    public bool ButtonBChecked
    {
        get { return _ButtonBChecked; }
        set 
        { 
            _ButtonBChecked = value; 
            PropertyChanged(this, new PropertyChangedEventArgs("ButtonBChecked"));
            if (value) ButtonAChecked = false;
        }
    }
}

编辑:

问题是,当第一次单击按钮 B 时,IsChecked 值会发生变化,并且绑定会通过,但按钮 A 不会将其未选中状态提供给 ButtonAChecked 属性。通过手动更新代码,ButtonAChecked 属性设置器将在下次单击按钮 A 时被调用。

【讨论】:

  • 我不认为这个提议的解决方案解决了在选择按钮时绑定被丢弃的问题。
  • 当我在设置器上使用断点对其进行测试时,每当我在两个单选按钮之间来回切换时,设置器都会受到影响。绑定似乎没有被破坏。
【解决方案4】:

为了任何研究这个问题的人的利益,这是我最终实施的解决方案。它建立在 John Bowen 的答案之上,我将其选为该问题的最佳解决方案。

首先,我为一个包含单选按钮的透明列表框创建了一个样式。然后,我创建了要放入列表框中的按钮——我的按钮是固定的,而不是作为数据读入应用程序,所以我将它们硬编码到标记中。

我在视图模型中使用名为ListButtons 的枚举来表示列表框中的按钮,并且我使用每个按钮的Tag 属性来传递枚举值的字符串值以用于该按钮。 ListBox.SelectedValuePath 属性允许我将Tag 属性指定为所选值的源,我使用SelectedValue 属性将其绑定到视图模型。我以为我需要一个值转换器来在字符串和它的枚举值之间进行转换,但是 WPF 的内置转换器可以毫无问题地处理转换。

这是 Window1.xaml 的完整标记:

<Window x:Class="RadioButtonMvvmDemo.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">

    <!-- Resources -->
    <Window.Resources>
        <Style x:Key="RadioButtonList" TargetType="{x:Type ListBox}">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="ItemContainerStyle">
                <Setter.Value>
                    <Style TargetType="{x:Type ListBoxItem}" >
                        <Setter Property="Margin" Value="5" />
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                    <Border BorderThickness="0" Background="Transparent">
                                        <RadioButton 
                                            Focusable="False"
                                            IsHitTestVisible="False"
                                            IsChecked="{TemplateBinding IsSelected}">
                                            <ContentPresenter />
                                        </RadioButton>
                                    </Border>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </Setter.Value>
            </Setter>
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBox}">
                        <Border BorderThickness="0" Padding="0" BorderBrush="Transparent" Background="Transparent" Name="Bd" SnapsToDevicePixels="True">
                            <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <!-- Layout -->
    <Grid>
        <!-- Note that we use SelectedValue, instead of SelectedItem. This allows us 
        to specify the property to take the value from, using SelectedValuePath. -->

        <ListBox Style="{StaticResource RadioButtonList}" SelectedValuePath="Tag" SelectedValue="{Binding Path=SelectedButton}">
            <ListBoxItem Tag="ButtonA">Button A</ListBoxItem>
            <ListBoxItem Tag="ButtonB">Button B</ListBoxItem>
        </ListBox>
    </Grid>
</Window>

视图模型有一个属性 SelectedButton,它使用 ListButtons 枚举来显示选择了哪个按钮。该属性调用了我用于视图模型的基类中的一个事件,该事件引发了PropertyChanged 事件:

namespace RadioButtonMvvmDemo
{
    public enum ListButtons {ButtonA, ButtonB}

    public class Window1ViewModel : ViewModelBase
    {
        private ListButtons p_SelectedButton;

        public Window1ViewModel()
        {
            SelectedButton = ListButtons.ButtonB;
        }

        /// <summary>
        /// The button selected by the user.
        /// </summary>
        public ListButtons SelectedButton
        {
            get { return p_SelectedButton; }

            set
            {
                p_SelectedButton = value;
                base.RaisePropertyChangedEvent("SelectedButton");
            }
        }

    }
} 

在我的生产应用程序中,SelectedButton 设置器将调用一个服务类方法,该方法将在选择按钮时执行所需的操作。

为了完整起见,这里是基类:

using System.ComponentModel;

namespace RadioButtonMvvmDemo
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Protected Methods

        /// <summary>
        /// Raises the PropertyChanged event.
        /// </summary>
        /// <param name="propertyName">The name of the changed property.</param>
        protected void RaisePropertyChangedEvent(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
                PropertyChanged(this, e);
            }
        }

        #endregion
    }
}

希望有帮助!

【讨论】:

    【解决方案5】:

    看起来他们在 .NET 4 中修复了与 IsChecked 属性的绑定。在 VS2008 中损坏的项目在 VS2010 中有效。

    【讨论】:

    • 确认!问题中的演示项目按预期在 .NET 4 中运行。
    • 我也可以确认!
    【解决方案6】:

    这是另一种方法

    查看:

    <StackPanel Margin="90,328,965,389" Orientation="Horizontal">
            <RadioButton Content="Mr" Command="{Binding TitleCommand, Mode=TwoWay}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Mode=Self}, Mode=TwoWay}" GroupName="Title"/>
            <RadioButton Content="Mrs" Command="{Binding TitleCommand, Mode=TwoWay}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Mode=Self}, Mode=TwoWay}" GroupName="Title"/>
            <RadioButton Content="Ms" Command="{Binding TitleCommand, Mode=TwoWay}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Mode=Self}, Mode=TwoWay}" GroupName="Title"/>
            <RadioButton Content="Other" Command="{Binding TitleCommand, Mode=TwoWay}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Mode=Self}}" GroupName="Title"/>
            <TextBlock Text="{Binding SelectedTitle, Mode=TwoWay}"/>
        </StackPanel>
    

    视图模型:

     private string selectedTitle;
        public string SelectedTitle
        {
            get { return selectedTitle; }
            set
            {
                SetProperty(ref selectedTitle, value);
            }
        }
    
        public RelayCommand TitleCommand
        {
            get
            {
                return new RelayCommand((p) =>
                {
                    selectedTitle = (string)p;
                });
            }
        }
    

    【讨论】:

    • 这适用于绑定TextBlock 的文本,但无助于绑定RadioButtonIsChecked 属性。
    【解决方案7】:

    John Bowen 的answer 的一个小扩展:当值没有实现ToString() 时它不起作用。您需要什么,而不是将 RadioButton 的 Content 设置为 TemplateBinding,只需将 ContentPresenter 放入其中,如下所示:

    <ListBox ItemsSource="{Binding ...}" SelectedItem="{Binding ...}">
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
                            <RadioButton IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}">
                                <ContentPresenter/>
                            </RadioButton>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>
    

    这样,您还可以酌情使用DisplayMemberPathItemTemplate。 RadioButton 只是“包装”项目,提供选择。

    【讨论】:

      【解决方案8】:

      您必须为单选按钮添加组名称

         <StackPanel>
              <RadioButton Content="Button A" IsChecked="{Binding Path=ButtonAIsChecked, Mode=TwoWay}" GroupName="groupName" />
              <RadioButton Content="Button B" IsChecked="{Binding Path=ButtonBIsChecked, Mode=TwoWay}" GroupName="groupName" />
          </StackPanel>
      

      【讨论】:

        【解决方案9】:

        我在 VS2015 和 .NET 4.5.1 中有一个非常相似的问题

        XAML:

                        <ListView.ItemsPanel>
                            <ItemsPanelTemplate>
                                <UniformGrid Columns="6" Rows="1"/>
                            </ItemsPanelTemplate>
                        </ListView.ItemsPanel>
                        <ListView.ItemTemplate>
                            <DataTemplate >
                                <RadioButton  GroupName="callGroup" Style="{StaticResource itemListViewToggle}" Click="calls_ItemClick" Margin="1" IsChecked="{Binding Path=Selected,Mode=TwoWay}" Unchecked="callGroup_Checked"  Checked="callGroup_Checked">
        

        ....

        正如您在这段代码中看到的,我有一个列表视图,模板中的项目是属于组名的单选按钮。

        如果我将一个新项目添加到集合中,并将属性 Selected 设置为 True,它将显示为选中状态,其余按钮保持选中状态。

        我通过首先获取选中按钮并手动将其设置为 false 来解决它,但这不是它应该完成的方式。

        后面的代码:

        `....
          lstInCallList.ItemsSource = ContactCallList
          AddHandler ContactCallList.CollectionChanged, AddressOf collectionInCall_change
        .....
        Public Sub collectionInCall_change(sender As Object, e As NotifyCollectionChangedEventArgs)
            'Whenever collection change we must test if there is no selection and autoselect first.   
            If e.Action = NotifyCollectionChangedAction.Add Then
                'The solution is this, but this shouldn't be necessary
                'Dim seleccionado As RadioButton = getCheckedRB(lstInCallList)
                'If seleccionado IsNot Nothing Then
                '    seleccionado.IsChecked = False
                'End If
                DirectCast(e.NewItems(0), PhoneCall).Selected = True
        .....
        End sub
        

        `

        【讨论】:

          【解决方案10】:
          <RadioButton  IsChecked="{Binding customer.isMaleFemale}">Male</RadioButton>
              <RadioButton IsChecked="{Binding customer.isMaleFemale,Converter=      {StaticResource GenderConvertor}}">Female</RadioButton>
          

          下面是 IValueConverter 的代码

          public class GenderConvertor : IValueConverter
          {
              public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
              {
                  return !(bool)value;
              }
          
              public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
              {
                  return !(bool)value;
              }
          }
          

          这对我有用。根据单选按钮单击,甚至值都绑定在视图和视图模型上。真-->男假-->女

          【讨论】:

            【解决方案11】:

            我知道这是一个老问题,最初的问题已在 .NET 4 中解决。老实说,这有点偏离主题。

            在我想在 MVVM 中使用 RadioButtons 的大多数情况下,它是在 enum 的元素之间进行选择,这需要将 VM 空间中的 bool 属性绑定到每个按钮并使用它们来设置反映实际选择的整体 enum 属性,这很快就会变得非常乏味。所以我想出了一个可重复使用且非常易于实施且不需要 ValueConverters 的解决方案。

            View 几乎相同,但是一旦你的 enum 就位,VM 端就可以使用单个属性来完成。

            主窗口虚拟机

            using System.ComponentModel;
            
            namespace EnumSelectorTest
            {
              public class MainWindowVM : INotifyPropertyChanged
              {
                public EnumSelectorVM Selector { get; set; }
            
                private string _colorName;
                public string ColorName
                {
                  get { return _colorName; }
                  set
                  {
                    if (_colorName == value) return;
                    _colorName = value;
                    RaisePropertyChanged("ColorName");
                  }
                }
            
                public MainWindowVM()
                {
                  Selector = new EnumSelectorVM
                    (
                      typeof(MyColors),
                      MyColors.Red,
                      false,
                      val => ColorName = "The color is " + ((MyColors)val).ToString()
                    );
                }
            
                public event PropertyChangedEventHandler PropertyChanged;
                protected virtual void RaisePropertyChanged(string propertyName)
                {
                  PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
                }
              }
            }
            

            完成所有工作的类继承自 DynamicObject。从外部看,它为 enum 中的每个元素创建一个 bool 属性,前缀为“Is”、“IsRed”、“IsBlue”等,可以绑定到 from XAML。以及保存实际 enum 值的 Value 属性。

            public enum MyColors
            {
              Red,
              Magenta,
              Green,
              Cyan,
              Blue,
              Yellow
            }
            

            EnumSelectorVM

            using System;
            using System.ComponentModel;
            using System.Dynamic;
            using System.Linq;
            
            namespace EnumSelectorTest
            {
              public class EnumSelectorVM : DynamicObject, INotifyPropertyChanged
              {
                //------------------------------------------------------------------------------------------------------------------------------------------
                #region Fields
            
                private readonly Action<object> _action;
                private readonly Type _enumType;
                private readonly string[] _enumNames;
                private readonly bool _notifyAll;
            
                #endregion Fields
            
                //------------------------------------------------------------------------------------------------------------------------------------------
                #region Properties
            
                private object _value;
                public object Value
                {
                  get { return _value; }
                  set
                  {
                    if (_value == value) return;
                    _value = value;
                    RaisePropertyChanged("Value");
                    _action?.Invoke(_value);
                  }
                }
            
                #endregion Properties
            
                //------------------------------------------------------------------------------------------------------------------------------------------
                #region Constructor
            
                public EnumSelectorVM(Type enumType, object initialValue, bool notifyAll = false, Action<object> action = null)
                {
                  if (!enumType.IsEnum)
                    throw new ArgumentException("enumType must be of Type: Enum");
                  _enumType = enumType;
                  _enumNames = enumType.GetEnumNames();
                  _notifyAll = notifyAll;
                  _action = action;
            
                  //do last so notification fires and action is executed
                  Value = initialValue;
                }
            
                #endregion Constructor
            
                //------------------------------------------------------------------------------------------------------------------------------------------
                #region Methods
            
                //---------------------------------------------------------------------
                #region Public Methods
            
                public override bool TryGetMember(GetMemberBinder binder, out object result)
                {
                  string elementName;
                  if (!TryGetEnumElemntName(binder.Name, out elementName))
                  {
                    result = null;
                    return false;
                  }
                  try
                  {
                    result = Value.Equals(Enum.Parse(_enumType, elementName));
                  }
                  catch (Exception ex) when (ex is ArgumentNullException || ex is ArgumentException || ex is OverflowException)
                  {
                    result = null;
                    return false;
                  }
                  return true;
                }
            
                public override bool TrySetMember(SetMemberBinder binder, object newValue)
                {
                  if (!(newValue is bool))
                    return false;
                  string elementName;
                  if (!TryGetEnumElemntName(binder.Name, out elementName))
                    return false;
                  try
                  {
                    if((bool) newValue)
                      Value = Enum.Parse(_enumType, elementName);
                  }
                  catch (Exception ex) when (ex is ArgumentNullException || ex is ArgumentException || ex is OverflowException)
                  {
                    return false;
                  }
                  if (_notifyAll)
                    foreach (var name in _enumNames)
                      RaisePropertyChanged("Is" + name);
                  else
                    RaisePropertyChanged("Is" + elementName);
                  return true;
                }
            
                #endregion Public Methods
            
                //---------------------------------------------------------------------
                #region Private Methods
            
                private bool TryGetEnumElemntName(string bindingName, out string elementName)
                {
                  elementName = "";
                  if (bindingName.IndexOf("Is", StringComparison.Ordinal) != 0)
                    return false;
                  var name = bindingName.Remove(0, 2); // remove first 2 chars "Is"
                  if (!_enumNames.Contains(name))
                    return false;
                  elementName = name;
                  return true;
                }
            
                #endregion Private Methods
            
                #endregion Methods
            
                //------------------------------------------------------------------------------------------------------------------------------------------
                #region Events
            
                public event PropertyChangedEventHandler PropertyChanged;
                protected virtual void RaisePropertyChanged(string propertyName)
                {
                  PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
                }
            
                #endregion Events
              }
            }
            

            要响应更改,您可以订阅 NotifyPropertyChanged 事件或将匿名方法传递给构造函数,如上所述。

            最后是 MainWindow.xaml

            <Window x:Class="EnumSelectorTest.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                mc:Ignorable="d"
                Title="MainWindow" Height="350" Width="525">  
              <Grid>
                <StackPanel>
                  <RadioButton IsChecked="{Binding Selector.IsRed}">Red</RadioButton>
                  <RadioButton IsChecked="{Binding Selector.IsMagenta}">Magenta</RadioButton>
                  <RadioButton IsChecked="{Binding Selector.IsBlue}">Blue</RadioButton>
                  <RadioButton IsChecked="{Binding Selector.IsCyan}">Cyan</RadioButton>
                  <RadioButton IsChecked="{Binding Selector.IsGreen}">Green</RadioButton>
                  <RadioButton IsChecked="{Binding Selector.IsYellow}">Yellow</RadioButton>
                  <TextBlock Text="{Binding ColorName}"/>
                </StackPanel>
              </Grid>
            </Window>
            

            希望其他人觉得这很有用,因为我认为这些会在我的工具箱中。

            【讨论】:

              猜你喜欢
              • 2013-06-29
              • 2021-07-19
              • 1970-01-01
              • 2012-07-31
              • 2011-10-07
              • 2011-08-25
              • 2020-01-19
              • 1970-01-01
              相关资源
              最近更新 更多