在 WinForms 中对一组单选按钮进行数据绑定的最佳方法
Posted
技术标签:
【中文标题】在 WinForms 中对一组单选按钮进行数据绑定的最佳方法【英文标题】:Best way to databind a group of radiobuttons in 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
然后我将每个单选按钮绑定到它们各自的属性,如上所述。
这对我来说似乎不对,因此非常感谢任何建议。
【问题讨论】:
【参考方案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.Format 和 Binding.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 块时,我喜欢将其视为代码顶部的“层”。我决定了异常在整个程序中的流动、它们向用户的显示,以及需要什么清理才能让程序继续正常工作,而对象不会处于不确定状态,可能会级联到其他错误。
【讨论】:
以上是关于在 WinForms 中对一组单选按钮进行数据绑定的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章