C# 在问卷样式DataGridView的多个复选框列中仅选择一个复选框
我创建了一个应用程序,它显示一个带有一系列问题的C# 在问卷样式DataGridView的多个复选框列中仅选择一个复选框,c#,.net,winforms,datagridview,datagridviewcolumn,C#,.net,Winforms,Datagridview,Datagridviewcolumn,我创建了一个应用程序,它显示一个带有一系列问题的DataGridView。 dgv结构包括一个字符串列用于问题文本,三个bool/checkbox列用于答案(是、否、不适用)。 每个问题都显示在自己的行上 我希望我的程序只允许用户在每行上选择“是”、“否”或“不适用” 我想当一个选项被选中时,我需要取消选中其他复选框选项,但我不太确定如何做到这一点 我已经设置了CellValueChanged和CellContentClick事件,但我不确定实现所需功能所需的代码 DataGridView绑定到
DataGridView
。
dgv结构包括一个字符串列用于问题文本,三个bool/checkbox列用于答案(是、否、不适用)。
每个问题都显示在自己的行上
我希望我的程序只允许用户在每行上选择“是”、“否”或“不适用”
我想当一个选项被选中时,我需要取消选中其他复选框选项,但我不太确定如何做到这一点
我已经设置了CellValueChanged
和CellContentClick
事件,但我不确定实现所需功能所需的代码
DataGridView绑定到DataTable
我目前掌握的代码:
private void dgvQuestions_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
int columnIndex = e.ColumnIndex;
int rowIndex = e.RowIndex;
DataGridViewCheckBoxCell chkYes = dgvQuestions.Rows[rowIndex].Cells[2] as DataGridViewCheckBoxCell;
DataGridViewCheckBoxCell chkNo = dgvQuestions.Rows[rowIndex].Cells[3] as DataGridViewCheckBoxCell;
DataGridViewCheckBoxCell chkNA = dgvQuestions.Rows[rowIndex].Cells[4] as DataGridViewCheckBoxCell;
if (Convert.ToBoolean(chkYes.Value) == true)
{
}
if (Convert.ToBoolean(chkNo.Value) == true)
{
}
if (Convert.ToBoolean(chkNA.Value) == true)
{
}
}
private void dgvQuestions_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
dgvQuestions.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
我希望这个示例对使DataGridView变得简单和强大有用;这篇文章的原意是“感谢您的帮助” 是否表现出你想要的行为?对我来说,使用BindingList作为DataGridView的数据源是可行的。然后,使用复选框更改时发生的“CellDirty”事件,您可以使它们看起来像一个热单选按钮,并回答您的问题:“从多个复选框项中仅选择一个复选框” 下面是一个代表问卷中一行项目的类的示例
class QuestionaireItem
{
public string Question { get; internal set; } = "Question " + _count++;
public bool Yes { get; set; }
public bool No { get; set; }
public bool Maybe { get; set; } // OOPS! I should have said "NA"
static int _count = 1;
}
将此类绑定到DataGridView时,列将自动填充一个名为“Question”的列(该列是只读的(因为“set”标记为internal),以及三个复选框列,它们的值可以更改(因为get和set都是公共的)。
采用这种方法适用于任何类T,并且几乎完成了使用DataGridView的所有繁重工作
下面是如何处理CellDirty事件,使三个复选框(我将它们命名为Yes、No和Maybe)像单选按钮一样工作:
private void DataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
// The cell will be considered "dirty" or modified so Commit first.
dataGridView1.EndEdit(DataGridViewDataErrorContexts.Commit);
// Get the QuestionaireItem that is bound to the row
QuestionaireItem item = (QuestionaireItem)
dataGridView1
.Rows[dataGridView1.CurrentCell.RowIndex]
.DataBoundItem;
// Now see which column changed:
switch (dataGridView1.Columns[dataGridView1.CurrentCell.ColumnIndex].Name)
{
case "Yes":
item.No = false; // i.e. "unchecked"
item.Maybe = false;
break;
case "No":
item.Yes = false;
item.Maybe = false;
break;
case "Maybe":
item.Yes = false;
item.No = false;
break;
}
dataGridView1.Refresh(); // Update the binding list to the display
}
一旦MainForm有了它的窗口句柄,绑定本身就很简单了。我们可以为此重写OnHandleCreated。在这里,绑定过程将正常工作,我们还可以设置列的显示宽度。这显示了如何初始化dataGridView1。我在其中添加了注释来解释发生了什么:
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (!DesignMode) // We only want this behavior at runtime
{
// Create the binding list
BindingList<QuestionaireItem> testdata = new BindingList<QuestionaireItem>();
// And add 5 example items to it
for (int i = 0; i < 5; i++) testdata.Add(new QuestionaireItem());
// Now make this list the DataSource of the DGV.
dataGridView1.DataSource = testdata;
// This just formats the column widths a little bit
dataGridView1.Columns["Question"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridView1.Columns["Maybe"].Width =
dataGridView1.Columns["Yes"].Width =
dataGridView1.Columns["No"].Width = 40;
// And this subscribes to the event (one of them anyway...)
// that will fire when the checkbox is changed
dataGridView1.CurrentCellDirtyStateChanged += DataGridView1_CurrentCellDirtyStateChanged;
}
}
受保护的重写无效OnHandleCreated(EventArgs e)
{
碱基。根据HandleCreated(e);
if(!DesignMode)//我们只需要在运行时执行此行为
{
//创建绑定列表
BindingList testdata=新建BindingList();
//并向其中添加5个示例项
对于(int i=0;i<5;i++)testdata.Add(new QuestionaireItem());
//现在,将此列表作为DGV的数据源。
dataGridView1.DataSource=testdata;
//这只是将列宽格式化一点
dataGridView1.Columns[“Question”].AutoSizeMode=DataGridViewAutoSizeColumnMode.Fill;
dataGridView1.列[“可能”].宽度=
dataGridView1.列[“是”].宽度=
dataGridView1.列[“否”]。宽度=40;
//这是对事件的订阅(无论如何,其中一个…)
//当复选框更改时将激发
dataGridView1.CurrentCellDirtyStateChanged+=dataGridView1\u CurrentCellDirtyStateChanged;
}
}
此示例来自GitHub。您似乎已正确设置了
CellContentClick
,但是,如果网格中有其他列,则检查以确保单击内容的单元格实际上是复选框单元格之一可能会有所帮助。否则,代码可能会不必要地设置单元格值伊莉
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e) {
string colName = dataGridView1.Columns[e.ColumnIndex].Name;
if (colName == "Yes" || colName == "No" || colName == "N/A") {
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
在CellValueChanged
事件中,代码应再次只检查复选框值。此外,我假设必须至少检查一(1)个单元格。例如,如果最初选中了“N/A”单元格,则用户“取消选中”如果选中该单元格,则该行将不会选中任何复选框。这是代码中的最后一个复选框,如果用户“取消选中”了“N/A”单元格,并且所有复选框都处于“未选中”状态,则代码将“选中”该“N/A”单元格。此外,“关闭”也很重要在我们更改CellValueChanged
事件中的任何复选框值以避免重入之前,CellValueChanged
事件
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
if (e.RowIndex >= 0 && e.ColumnIndex >= 0) {
string colName = dataGridView1.Columns[e.ColumnIndex].Name;
bool checkValue;
dataGridView1.CellValueChanged -= new DataGridViewCellEventHandler(this.dataGridView1_CellValueChanged);
switch (colName) {
case "Yes":
checkValue = (bool)dataGridView1.Rows[e.RowIndex].Cells["Yes"].Value;
if (checkValue == true) {
dataGridView1.Rows[e.RowIndex].Cells["No"].Value = false;
dataGridView1.Rows[e.RowIndex].Cells["N/A"].Value = false;
}
else {
dataGridView1.Rows[e.RowIndex].Cells["N/A"].Value = true;
}
break;
case "No":
checkValue = (bool)dataGridView1.Rows[e.RowIndex].Cells["No"].Value;
if (checkValue == true) {
dataGridView1.Rows[e.RowIndex].Cells["Yes"].Value = false;
dataGridView1.Rows[e.RowIndex].Cells["N/A"].Value = false;
}
else {
dataGridView1.Rows[e.RowIndex].Cells["N/A"].Value = true;
}
break;
case "N/A":
checkValue = (bool)dataGridView1.Rows[e.RowIndex].Cells["N/A"].Value;
if (checkValue == true) {
dataGridView1.Rows[e.RowIndex].Cells["Yes"].Value = false;
dataGridView1.Rows[e.RowIndex].Cells["No"].Value = false;
}
else {
if ((bool)dataGridView1.Rows[e.RowIndex].Cells["Yes"].Value == false &&
(bool)dataGridView1.Rows[e.RowIndex].Cells["No"].Value == false) {
dataGridView1.Rows[e.RowIndex].Cells["N/A"].Value = true;
}
}
break;
default:
break;
}
dataGridView1.CellValueChanged += new DataGridViewCellEventHandler(this.dataGridView1_CellValueChanged);
}
下面是一个简单的示例,有三列“是”、“否”和“不适用”复选框列
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
dataGridView1.DataSource = GetTable();
}
private DataTable GetTable() {
DataTable dt = new DataTable();
dt.Columns.Add("Yes", typeof(bool));
dt.Columns.Add("No", typeof(bool));
dt.Columns.Add("N/A", typeof(bool));
for (int i = 0; i < 10; i++) {
dt.Rows.Add(false, false, true);
}
return dt;
}
public Form1(){
初始化组件();
}
私有void Form1\u加载(对象发送方、事件参数e){
dataGridView1.DataSource=GetTable();
}
私有数据表GetTable(){
DataTable dt=新的DataTable();
添加(“是”,类型(bool));
添加(“否”,类型(bool));
dt.列。添加(“不适用”,类型(bool));
对于(int i=0;i<10;i++){
dt.Rows.Add(false、false、true);
}
返回dt;
}
希望这有帮助。I.Btw,一个与DataGridView相关的问题要求您指定数据源类型。@Jimi,恕我直言,我不同意,因为BindingList是泛型的,可以用作DataGridView的数据源(此时类T的所有公共属性都是可见的,并且会自动填充到DataGridView中)。因此,我的观点是,这些问题可以以通用形式回答,就像为任何T类工作一样。@IVSoftware是的,好吧,绑定列表可以做它能做的事情。DataTable可以做其他事情。DataTable绑定到BindingSource,更多。我的观点是:使用DataTable(例如),所需要的只是一个经过校准的表达式(即使是计算列)。使用BindingList和Class对象:类是否实现了
INotifyPropertyChannged
?和/或,该逻辑是否可以在类对象本身中实现,因此单个选项实际上是在内部触发的,而您根本不必担心UI层