【问题标题】:Select only one checkbox from multiple checkbox columns in questionnaire style DataGridView从问卷样式DataGridView的多个复选框列中仅选择一个复选框
【发布时间】:2020-06-02 10:55:38
【问题描述】:

我创建了一个应用程序,它显示一个带有一系列问题的DataGridView。 dgv 结构由一个用于问题文本的字符串列和三个用于答案的布尔/复选框列组成(是、否、N/A)。 每个问题都显示在自己的行中。

我希望我的程序只允许用户在每一行上只选择是、只选择否或只选择 N/A。

我想我需要在选中一个选项时取消选中其他复选框选项,但我不太确定如何执行此操作。

我已经设置了 CellValueChangedCellContentClick 事件,但我不确定实现所需功能所需的代码。

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

【问题讨论】:

  • Programmatically check a DataGridView CheckBox that was just unchecked。顺便说一句,与 DataGridView 相关的问题要求您指定 DataSource 类型。
  • @Jimi,我不同意,因为 BindingList 是通用的,可以用作 DataGridView 的数据源(此时类 T 的所有公共属性在 DataGridView 中可见并自动填充)。所以,我的观点是这样的问题可以用通用的形式来回答,因为它适用于任何类 T
  • @IVSoftware 是的,BindingList 可以做它可以做的事情。 DataTable 可以做其他事情。绑定到 BindingSource 的 DataTable 等等。我的观点:使用 DataTable(例如),所需要的只是一个 calibrated 表达式(即使使用计算的列)。使用 BindingList 和类对象:类是否实现 INotifyPropertyChannged?和/或,这个逻辑是否可以在类对象本身中实现,所以 Single Option 实际上是在内部触发的,您根本不会打扰 UI 层?如果您可以避免在视图方面工作,那是一件好事。等
  • 当然!我的观点非常狭窄:BindingList、DataTable 很容易为类 T 实现,因此不需要在发布时知道。 DataTable from Constrained InterfaceDataTable from IEnumerable<T>。我认为你和我在这个问题上大多站在同一边。实际上,它是在 MyDataGridView 类内部处理的:DataGridView{}

标签: c# .net winforms datagridview datagridviewcolumn


【解决方案1】:

我希望这个示例有助于使 DataGridView 变得简单而强大;它与最初措辞“任何帮助表示赞赏”的帖子有关。

this video 是否显示您正在寻找的行为?对我有用的是使用 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 时,列将自动填充名为“问题”的列(它是只读的(因为“集合”标记为 internal)和三个值 可以 更改的复选框列(因为 getset 都是公开的)。 采用这种方法适用于任何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。我已经让 cmets 解释发生了什么:

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

Clone or Download 这个例子来自 GitHub。

【讨论】:

  • 您好,感谢您非常简洁的回答。该视频确实显示了我正在寻找的功能,但我的 DGV 已绑定到 DataTable。对不起,我本来应该这么说的。
  • 别担心!我绝对明白这如何使它不是“”答案,但我希望有人在不同的背景下遇到您的问题。
  • 对于任何偶然发现此线程的人,如果您还没有致力于 BindingList 或 DataTable,这里有一个很好的 MS blog 讨论这两种方法的优缺点。
  • 是的,它肯定会对其他人有所帮助。谢谢。
【解决方案2】:

您似乎已正确设置了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;
}

希望这会有所帮助。

【讨论】:

  • 这是完美的。非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-22
  • 2014-10-07
  • 2018-02-27
  • 1970-01-01
  • 1970-01-01
  • 2020-01-10
相关资源
最近更新 更多