在WinForms中对一组radiobutton进行数据绑定的最佳方法

我目前正在研究一些现有的Windows窗体的数据绑定,并且我遇到了一个问题,想出了在一个组框内数据绑定一组radiobutton控件的正确方法。

我的业务对象有一个整数属性,我想对4个radiobuttons进行数据绑定(其中每个都代表值0 – 3)。

我目前正在绑定一个presenter对象,它作为表单和业务对象之间的绑定器,我现在的方式是拥有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 } 

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

这对我来说似乎不对,所以任何建议都非常感谢。

下面是一个通用的RadioGroupBox实现,本着ArielBH的建议(一些代码借用了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() .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”属性(绑定在初始化单选按钮之前执行,因此在第一次分配时它们的标记为null)。 所以就这样绑定自己:

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

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

我知道这篇文章很老但是我在寻找这个问题的答案时遇到了这个post并且它没有解决我的问题。 我最后在一分钟之前随机关闭了一个灯泡并希望分享我的解决方案。

我在一个combobox中有三个单选按钮。 我正在使用自定义类对象的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); 

我希望这可以帮助别人!!

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

使用Enum作为按钮的Tag属性中的字符串,我使用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(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(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; b.Parse += ParseSelectedRadioButton; rb.DataBindings.Add(b); } 

我开始解决同样的问题。

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

以下类将所有单选按钮保留在列表中并查找枚举:

 class RadioButtonBinding : ILookup { private Type enumType; private List 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(); 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 this[Enum key] { get { return this.radioButtons.FindAll(a => { return a.Text == key.ToString(); }); } } IEnumerator> IEnumerable>.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); } 

将单选按钮的标记名称设置为表示该值的值。

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

要保存已选中的单选按钮:

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

要加载已选中的单选按钮:

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

田田!

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

 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()) { // 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()) { // 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()) { // 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()) { // 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()) { // 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()) { // 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()); } } 

我的方法是在将每个单选按钮绑定到布尔属性之前将其放入自己的面板中:

  public static Binding Bind(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); } 

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

 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之后的变量可能最终处于不确定状态,并可能抛出另一个可能会或可能不会处理的exception。

更好的方法如下

  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的所有后代必须首先出现)。 通过使用entity framework类而不是Exception基类,可以获得entity framework错误的特定实体状态信息。 这有助于调试或为用户提供更清晰的运行时消息。

当我设置try-catch块时,我喜欢将其视为代码顶部的“层”。 我通过程序,向用户显示exception的流程,以及为了使程序能够在没有可以级联到其他错误的不确定状态的对象的情况下继续正常工作而需要进行清理。