【问题标题】:Best way to databind a group of radiobuttons in WinForms在 WinForms 中对一组单选按钮进行数据绑定的最佳方法
【发布时间】:2010-10-15 01:53:27
【问题描述】:

我目前正在对我现有的一些 Windows 窗体进行数据绑定,但我遇到了一个问题,我无法确定对组框中的一组单选按钮控件进行数据绑定的正确方法。

我的业务对象有一个整数属性,我想针对 4 个单选按钮进行数据绑定(其中每个单选按钮代表值 0 - 3)。

我目前正在绑定一个演示者对象,该对象充当表单和业务对象之间的绑定器,我现在所做的方式是拥有 4 个单独的属性,每个属性都绑定到这些值中的每一个(我一定要使用 INotifyPropertyChanged,但这里不包括):

Private int _propValue;

Public bool PropIsValue0 
{ 
  get { return _propValue == 0; }
  set
  {
    if (value) 
      _propValue = 0;
  }
}

Public bool PropIsValue1 { // As above, but with value == 1 }
Public bool PropIsValue2 { // As above, but with value == 2 }
Public bool PropIsValue3 { // As above, but with value == 3 }

然后我将每个单选按钮绑定到它们各自的属性,如上所述。

这对我来说似乎不对,因此非常感谢任何建议。

【问题讨论】:

    标签: c# winforms data-binding radio-button


    【解决方案1】:

    我想我会使用我自己的 GroupBox。我会将 CustomGroupBox 绑定到您的模型并从绑定值设置正确的 RadioButton(使用标签或名称属性)。

    【讨论】:

    • 听起来好多了。感谢您的提示
    【解决方案2】:

    以下是本着 ArielBH 建议的精神的通用 RadioGroupBox 实现(一些代码借用自 Jay Andrew Allen 的 RadioPanel)。只需将 RadioButtons 添加到其中,将它们的标签设置为不同的整数并绑定到 'Selected' 属性。

    public class RadioGroupBox : GroupBox
    {
        public event EventHandler SelectedChanged = delegate { };
    
        int _selected;
        public int Selected
        {
            get
            {
                return _selected;
            }
            set
            {
                int val = 0;
                var radioButton = this.Controls.OfType<RadioButton>()
                    .FirstOrDefault(radio =>
                        radio.Tag != null 
                       && int.TryParse(radio.Tag.ToString(), out val) && val == value);
    
                if (radioButton != null)
                {
                    radioButton.Checked = true;
                    _selected = val;
                }
            }
        }
    
        protected override void OnControlAdded(ControlEventArgs e)
        {
            base.OnControlAdded(e);
    
            var radioButton = e.Control as RadioButton;
            if (radioButton != null)
                radioButton.CheckedChanged += radioButton_CheckedChanged;
        }
    
        void radioButton_CheckedChanged(object sender, EventArgs e)
        {
            var radio = (RadioButton)sender;
            int val = 0;
            if (radio.Checked && radio.Tag != null 
                 && int.TryParse(radio.Tag.ToString(), out val))
            {
                _selected = val;
                SelectedChanged(this, new EventArgs());
            }
        }
    }
    

    请注意,由于 InitializeComponent 中的初始化顺序问题,您无法通过设计器绑定到“Selected”属性(绑定是在初始化单选按钮之前执行的,因此它们的标记在第一次分配中为空)。所以就这样绑定自己吧:

        public Form1()
        {
            InitializeComponent();
            //Assuming selected1 and selected2 are defined as integer application settings
            radioGroup1.DataBindings.Add("Selected", Properties.Settings.Default, "selected1");
            radioGroup2.DataBindings.Add("Selected", Properties.Settings.Default, "selected2");
        }
    

    【讨论】:

    • 太棒了,谢谢!无论如何,我没有通过设计师绑定,所以这是完美的。我正在使用 StrongBind (code.google.com/p/strongbind) 来绑定我的控件
    • 很高兴能提供帮助 :) 感谢您的提醒,我会检查 StrongBind,看起来很有趣
    【解决方案3】:

    这是我将单选按钮列表绑定到枚举的方法。

    在按钮的 Tag 属性中使用 Enum 作为字符串,我使用 Binding.FormatBinding.Parse 事件来决定应该检查哪个按钮。

    public enum OptionEnum
    {
       Option1 = 0,
       Option2
    }
    
    OptionEnum _rbEnum = OptionEnum.Option1;
    OptionEnum PropertyRBEnum
    {
        get { return _rbEnum; }
        set
        {
            _rbEnum = value;
            RaisePropertyChanged("PropertyRBEnum");
        }
    }
    
    public static void FormatSelectedEnum<T>(object sender, ConvertEventArgs args) where T : struct
    {
        Binding binding = (sender as Binding);
        if (binding == null) return;
    
        Control button = binding.Control;
    
        if (button == null || args.DesiredType != typeof(Boolean)) return;
    
        T value = (T)args.Value;
        T controlValue;
    
        if (Enum.TryParse(button.Tag.ToString(), out controlValue))
        {
            args.Value = value.Equals(controlValue);
        }
        else
        {
            Exception ex = new Exception("String not found in Enum");
            ex.Data.Add("Tag", button.Tag);
    
            throw ex;
        }
    }
    
    public static void ParseSelectedEnum<T>(object sender, ConvertEventArgs args) where T : struct
    {
        Binding binding = (sender as Binding);
        if (binding == null) return;
    
        Control button = binding.Control;
        bool value = (bool)args.Value;
    
        if (button == null || value != true) return;
    
        T controlValue;
    
        if (Enum.TryParse(button.Tag.ToString(), out controlValue))
        {
            args.Value = controlValue;
        }
        else
        {
            Exception ex = new Exception("String not found in Enum");
            ex.Data.Add("Tag", button.Tag);
    
            throw ex;
        }
    }
    

    然后像这样设置你的数据绑定:

    radioButton1.Tag = "Option1";
    radioButton2.Tag = "Option2";
    
    foreach (RadioButtonUx rb in new RadioButtonUx[] { radioButton1, radioButton2 })
    {
        Binding b = new Binding("Checked", this, "PropertyRBEnum");
        b.Format += FormatSelectedRadioButton<OptionEnum>;
        b.Parse += ParseSelectedRadioButton<OptionEnum>;
    
        rb.DataBindings.Add(b);
    }
    

    【讨论】:

      【解决方案4】:

      我开始解决同样的问题。

      我使用了 RadioButtonBinding 类,它封装了数据源中有关枚举的所有单选按钮。

      下面这个类将所有单选按钮保存在一个列表中并查找枚举:

      class RadioButtonBinding : ILookup<System.Enum, System.Windows.Forms.RadioButton>
      {
          private Type enumType;
          private List<System.Windows.Forms.RadioButton> radioButtons;
          private System.Windows.Forms.BindingSource bindingSource;
          private string propertyName;
      
          public RadioButtonBinding(Type myEnum, System.Windows.Forms.BindingSource bs, string propertyName)
          {
              this.enumType = myEnum;
              this.radioButtons = new List<System.Windows.Forms.RadioButton>();
              foreach (string name in System.Enum.GetNames(this.enumType))
              {
                  System.Windows.Forms.RadioButton rb = new System.Windows.Forms.RadioButton();
                  rb.Text = name;
                  this.radioButtons.Add(rb);
                  rb.CheckedChanged += new EventHandler(rb_CheckedChanged);
              }
              this.bindingSource = bs;
              this.propertyName = propertyName;
              this.bindingSource.DataSourceChanged += new EventHandler(bindingSource_DataSourceChanged);
          }
      
          void bindingSource_DataSourceChanged(object sender, EventArgs e)
          {
              object obj = this.bindingSource.Current;
              System.Enum item = obj.GetType().GetProperty(propertyName).GetValue(obj, new object[] { }) as System.Enum;
              foreach (System.Enum value in System.Enum.GetValues(this.enumType))
              {
                  if (this.Contains(value))
                  {
                      System.Windows.Forms.RadioButton rb = this[value].First();
                      if (value.Equals(item))
                      {
                          rb.Checked = true;
                      }
                      else
                      {
                          rb.Checked = false;
                      }
                  }
              }
          }
      
          void rb_CheckedChanged(object sender, EventArgs e)
          {
              System.Windows.Forms.RadioButton rb = sender as System.Windows.Forms.RadioButton;
              System.Enum val = null;
              try
              {
                  val = System.Enum.Parse(this.enumType, rb.Text) as System.Enum;
              }
              catch(Exception ex)
              {
                  // cannot occurred if code is safe
                  System.Windows.Forms.MessageBox.Show("No enum value for this radio button : " + ex.ToString());
              }
              object obj = this.bindingSource.Current;
              obj.GetType().GetProperty(propertyName).SetValue(obj, val, new object[] { });
              this.bindingSource.CurrencyManager.Refresh();
          }
      
          public int Count
          {
              get
              {
                  return System.Enum.GetNames(this.enumType).Count();
              }
          }
      
          System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
          {
              return this.radioButtons.GetEnumerator();
          }
      
          public bool Contains(Enum key)
          {
              return System.Enum.GetNames(this.enumType).Contains(key.ToString());
          }
      
          public IEnumerable<System.Windows.Forms.RadioButton> this[Enum key]
          {
              get
              {
                  return this.radioButtons.FindAll(a => { return a.Text == key.ToString(); });
              }
          }
      
          IEnumerator<IGrouping<Enum, System.Windows.Forms.RadioButton>> IEnumerable<IGrouping<Enum, System.Windows.Forms.RadioButton>>.GetEnumerator()
          {
              throw new NotImplementedException();
          }
      
          public void AddControlsIntoGroupBox(System.Windows.Forms.GroupBox gb)
          {
              System.Windows.Forms.FlowLayoutPanel panel = new System.Windows.Forms.FlowLayoutPanel();
              panel.Dock = System.Windows.Forms.DockStyle.Fill;
              panel.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
              foreach (System.Windows.Forms.RadioButton rb in this.radioButtons)
              {
                  panel.Controls.Add(rb);
              }
              gb.Controls.Add(panel);
          }
      }
      

      您正在通过在表单的构造函数中添加该代码来将该类用于表单:

          public PageView()
          {
              InitializeComponent();
              RadioButtonBinding rbWidth = new RadioButtonBinding(typeof(Library.EnumConstraint), this.pageBindingSource, "ConstraintWidth");
              rbWidth.AddControlsIntoGroupBox(this.groupBox1);
              RadioButtonBinding rbHeight = new RadioButtonBinding(typeof(Library.EnumConstraint), this.pageBindingSource, "ConstraintHeight");
              rbHeight.AddControlsIntoGroupBox(this.groupBox3);
              this.pageBindingSource.CurrentItemChanged += new EventHandler(pageBindingSource_CurrentItemChanged);
          }
      

      【讨论】:

        【解决方案5】:

        我知道这篇文章很旧,但在寻找同样问题的答案时,我遇到了这篇文章,但它并没有解决我的问题。就在一分钟前,我最终有一个灯泡随机熄灭,并想分享我的解决方案。

        我在一个组框中有三个单选按钮。我正在使用自定义类对象的 List 作为数据源。

        类对象:

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        
        namespace BAL
        {
        class ProductItem
        {
        
            // Global Variable to store the value of which radio button should be checked
            private int glbTaxStatus;
            // Public variable to set initial value passed from 
            // database query and get value to save to database
            public int TaxStatus
            {
                get { return glbTaxStatus; }
                set { glbTaxStatus = value; }
            }
        
            // Get/Set for 1st Radio button
            public bool Resale
            {
                // If the Global Variable = 1 return true, else return false
                get
                {
                    if (glbTaxStatus.Equals(1))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
        
                // If the value being passed in = 1 set the Global Variable = 1, else do nothing
                set
                {
                    if (value.Equals(true))
                    {
                        glbTaxStatus = 1;
                    }
                }
            }
        
            // Get/Set for 2nd Radio button
            public bool NeverTax
            {
                // If the Global Variable = 2 return true, else return false
                get
                {
                    if (glbTaxStatus.Equals(2))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
        
                // If the value being passed in = 2 set the Global Variable = 2, else do nothing
                set
                {
                    if (value.Equals(true))
                    {
                        glbTaxStatus = 2;
                    }
                }
            }
        
            // Get/Set for 3rd Radio button
            public bool AlwaysTax
            {
                // If the Global Variable = 3 return true, else return false
                get
                {
                    if (glbTaxStatus.Equals(3))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
        
                // If the value being passed in = 3 set the Global Variable = 3, else do nothing
                set
                {
                    if (value.Equals(true))
                    {
                        glbTaxStatus = 3;
                    }
                }
            }
        
        // More code ...
        

        三个单独的公共变量,get/set 访问同一个全局变量。

        在后面的代码中,我有一个在 Page_Load() 设置所有控件数据绑定期间调用的函数。我为每个单选按钮添加了自己的数据。

        radResale.DataBindings.Add("Checked", glbProductList, "Resale", true, DataSourceUpdateMode.OnPropertyChanged, false);
        radNeverTax.DataBindings.Add("Checked", glbProductList, "NeverTax", true, DataSourceUpdateMode.OnPropertyChanged, false);
        radAlwaysTax.DataBindings.Add("Checked", glbProductList, "Always", true, DataSourceUpdateMode.OnPropertyChanged, false);
        

        我希望这对某人有所帮助!

        【讨论】:

        • 感谢@paul - 在我使用您的绑定选项之前,我看到了非常奇怪的行为(单击按钮 C,属性 B 的设置器以 value==true 命中)。显然,为此需要formattingEnabled=true 和nullValue=false 参数。不知道为什么,但它确实有效,所以我很高兴 50%!
        • 是的,这很有帮助,因为我永远不会猜到formattingEnabled 参数需要为“true”,而nullValue 参数需要为“false”。在 RadioButton 上?!?!不管怎样,它奏效了。谢谢。
        【解决方案6】:

        将单选按钮的标签名称设置为代表值的名称。

        创建一个字符串设置,例如 OptionDuplicateFiles,并为其指定默认单选按钮的标签名称的默认值。

        保存选中的单选按钮:

        Settings.Default.OptionDuplicateFiles = gbxDuplicateFiles.Controls
           .OfType<RadioButton>()
           .Where(b => b.Checked)
           .Select(b => b.Tag)
           .First()
           .ToString();
        

        加载选中的单选按钮:

        (gbxDuplicateFiles.Controls
           .OfType<RadioButton>()
           .Where(b => b.Tag.ToString() == Settings.Default.OptionDuplicateFiles)
           .First())
           .Checked = true;
        

        多田!

        【讨论】:

          【解决方案7】:

          我喜欢 RadioButtonGroupBox 的想法,但我决定创建一个自支持的版本。 没有理由为 Tag 属性添加值或引入新的值属性。 任何分配的单选按钮仍然是 RadioButtonGroupBox 的成员,并且单选按钮的顺序是在开发过程中定义的。 所以,我修改了代码。 现在我可以通过索引位置、控件名称和文本来获取和设置选定的单选按钮。 BTW Text 仅在您为每个单选按钮分配的 Text 不同时才可用。

          public class RadioButtonGroupBox : GroupBox
          {
              public event EventHandler SelectedChanged = delegate { };
          
              int _nIndexPosCheckRadioButton = -1;
              int _selected;
              public int Selected
              {
                  get
                  {
                      return _selected;
                  }
              }
          
          
              public int CheckedRadioButtonIndexPos
              {
                  set
                  {
                      int nPosInList = -1;
                      foreach (RadioButton item in this.Controls.OfType<RadioButton>())
                      {
                          // There are RadioButtonItems in the list...
                          nPosInList++;
          
                          // Set the RB that should be checked
                          if (nPosInList == value)
                          {
                              item.Checked = true;
                              // We can stop with the loop
                              break;
                          }
                      }
                      _nIndexPosCheckRadioButton = nPosInList;
                  }
                  get
                  {
                      int nPosInList = -1;
                      int nPosCheckeItemInList = -1;
          
                      foreach (RadioButton item in this.Controls.OfType<RadioButton>())
                      {
                          // There are RadioButtonItems in the list...
                          nPosInList++;
          
                          // Find the RB that is checked
                          if (item.Checked)
                          {
                              nPosCheckeItemInList = nPosInList;
                              // We can stop with the loop
                              break;
                          }
                      }
                      _nIndexPosCheckRadioButton = nPosCheckeItemInList;
                      return _nIndexPosCheckRadioButton;
                  }
              }
          
              public string CheckedRadioButtonByText
              {
                  set
                  {
                      int nPosInList = -1;
                      foreach (RadioButton item in this.Controls.OfType<RadioButton>())
                      {
                          // There are RadioButtonItems in the list...
                          nPosInList++;
          
                          // Set the RB that should be checked
                          if (item.Text == value)
                          {
                              item.Checked = true;
                              // We can stop with the loop
                              break;
                          }
                      }
                      _nIndexPosCheckRadioButton = nPosInList;
                  }
                  get
                  {
                      string cByTextValue = "__UNDEFINED__";
                      int nPosInList = -1;
                      int nPosCheckeItemInList = -1;
          
                      foreach (RadioButton item in this.Controls.OfType<RadioButton>())
                      {
                          // There are RadioButtonItems in the list...
                          nPosInList++;
          
                          // Find the RB that is checked
                          if (item.Checked)
                          {
                              cByTextValue = item.Text;
                              nPosCheckeItemInList = nPosInList;
                              // We can stop with the loop
                              break;
                          }
                      }
                      _nIndexPosCheckRadioButton = nPosCheckeItemInList;
                      return cByTextValue;
                  }
              }
          
              public string CheckedRadioButtonByName
              {
                  set
                  {
                      int nPosInList = -1;
                      foreach (RadioButton item in this.Controls.OfType<RadioButton>())
                      {
                          // There are RadioButtonItems in the list...
                          nPosInList++;
          
                          // Set the RB that should be checked
                          if (item.Name == value)
                          {
                              item.Checked = true;
                              // We can stop with the loop
                              break;
                          }
                      }
                      _nIndexPosCheckRadioButton = nPosInList;
                  }
                  get
                  {
                      String cByNameValue = "__UNDEFINED__";
                      int nPosInList = -1;
                      int nPosCheckeItemInList = -1;
          
                      foreach (RadioButton item in this.Controls.OfType<RadioButton>())
                      {
                          // There are RadioButtonItems in the list...
                          nPosInList++;
          
                          // Find the RB that is checked
                          if (item.Checked)
                          {
                              cByNameValue = item.Name;
                              nPosCheckeItemInList = nPosInList;
                              // We can stop with the loop
                              break;
                          }
                      }
                      _nIndexPosCheckRadioButton = nPosCheckeItemInList;
                      return cByNameValue;
                  }
              }
          
          
              protected override void OnControlAdded(ControlEventArgs e)
              {
                  base.OnControlAdded(e);
          
                  var radioButton = e.Control as RadioButton;
                  if (radioButton != null)
                      radioButton.CheckedChanged += radioButton_CheckedChanged;
              }
          
          
              void radioButton_CheckedChanged(object sender, EventArgs e)
              {
                  _selected = CheckedRadioButtonIndexPos;
                  SelectedChanged(this, new EventArgs());
              }
          
          }
          

          【讨论】:

            【解决方案8】:

            我的方法是将每个单选按钮放入自己的面板中,然后再将它们绑定到布尔属性:

                public static Binding Bind<TObject>(this RadioButton control, object dataSource, string dataMember)
                {
                    // Put the radio button into its own panel
                    Panel panel = new Panel();
                    control.Parent.Controls.Add(panel);
                    panel.Location = control.Location;
                    panel.Size = control.Size;
                    panel.Controls.Add(control);
                    control.Location = new Point(0, 0);
            
                    // Do the actual data binding
                    return control.DataBindings.Add("Checked", dataSource, dataMember);
                }
            

            【讨论】:

              【解决方案9】:

              我想对代码块进行一些观察,这可能对阅读这些帖子的人有所帮助。由于其结构,以下代码可能无法始终按预期工作。

              try
                  {
                    val = System.Enum.Parse(this.enumType, rb.Text) as System.Enum;
                  }
                  catch(Exception ex)
                  {
                    // cannot occurred if code is safe
                    System.Windows.Forms.MessageBox.Show("No enum value for this radio button : " + ex.ToString());
                  }
                  object obj = this.bindingSource.Current;
                  obj.GetType().GetProperty(propertyName).SetValue(obj, val, new object[] {
                  }
                );
                this.bindingSource.CurrencyManager.Refresh();
              

              如果try块发生错误,catch块将被执行。代码将在 catch 块之后继续执行。由于没有处理绑定源,catch 后面的变量最终可能处于不确定状态,并可能引发另一个可能会或可能不会被处理的异常。

              更好的方法如下

               try
                      {
                          val = System.Enum.Parse(this.enumType, rb.Text) as System.Enum;
              
                      object obj = this.bindingSource.Current;
                      obj.GetType().GetProperty(propertyName).SetValue(obj, val, new object[] { });
                      this.bindingSource.CurrencyManager.Refresh();
                      }
                      catch(EntityException ex)
                      {
                          // handle error
                      }
                      catch(Exception ex)
                      {
                          // cannot occurred if code is safe
                          System.Windows.Forms.MessageBox.Show("No enum value for this radio button : " + ex.ToString());
                      }
              

              这允许处理枚举值错误以及可能发生的其他错误。但是,在 Exception 块之前使用 EntityException 或其变体(所有 Exception 的后代都必须先出现)。可以通过使用实体框架类而不是 Exception 基类来获取实体框架错误的特定实体状态信息。这有助于调试或为用户提供更清晰的运行时消息。

              当我设置 try-catch 块时,我喜欢将其视为代码顶部的“层”。我决定了异常在整个程序中的流动、它们向用户的显示,以及需要什么清理才能让程序继续正常工作,而对象不会处于不确定状态,可能会级联到其他错误。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2010-10-13
                • 2010-09-06
                • 1970-01-01
                • 2020-05-02
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多