簡體   English   中英

Winforms Datagridview 行選擇問題

[英]Winforms Datagridview row selection isssue

我正在做一個功能,就像我的UserControl中有一個DataGridView一樣,它托管在 Base Form 中。 這個Grid有 3 列,例如 ID、Name、Age,這個 Age 列是一個ComboBox控件。 當應用程序啟動時, Grid會填充來自數據庫表 Student 的數據。 選擇模式是單行。 起初,它會顯示 ID 和名稱,但 Age 列是空的,用戶可以單擊該列 - 這是一個ComboBox控件。

假設用戶填充的數據來自數據庫,並且只有 10 行。 用戶單擊第 4 行並嘗試從ComboBox指定一個值。 好吧,沒關系。 現在看到當前行是第 4 行並且該行被選中。 然后用戶嘗試點擊第 6 行。 但它突然選擇了第一行 [0 索引]。 我點擊了第 6 行,但它沒有選擇第 6 行。 相反,會自動選擇第一行。

dgv_RowEnter是我們單擊行時觸發的第一個事件。 但是在我不知道的dgv_RowEnter事件之前調用了另一個事件。

那是什么活動?

首先,我選擇一行並從ComboBox [Age Column] 中選擇一個數字,然后,當我單擊任何其他行時 - 當時它正在選擇 [highlighting] 第一行 [0th rowindex]。

如何防止這種情況?

在你的帖子中,你問:

dgv_RowEnter 是我們單擊行時觸發的第一個事件。 但是在我不知道的 dgv_RowEnter 事件之前調用了另一個事件。 那是什么活動?

您提出的問題表明DataGridView的默認行為沒有按照您想要的順序提供您想要的事件。 將數據綁定與DataSource屬性結合使用是使 DGV 開箱即用地表現得體的最快方法,這將是我的第一個建議。 但聽起來您好像直接與 DGV 進行交互,這意味着您是一位高級用戶,希望在 DGV 發送的事件中“處於領先地位”。 SelectedRowSelectedColumnSelectedCell作為事件發出之前攔截它們的方法是制作一個CustomDataGridView : DataGridView並分別覆蓋SetSelectedRowCoreSetSelectedColumnCoreSetSelectedCellCore方法。

您將需要更改 Form.Designer.cs 文件中的兩行:

// private System.Windows.Forms.DataGridView dataGridView1;
private CustomDataGridView dataGridView1;

和:

private void InitializeComponent()
{
    // this.dataGridView1 = new System.Windows.Forms.DataGridView();
    this.dataGridView1 = new CustomDataGridView();
    .
    .
    .
}

下面顯示的控制台輸出表明,當這些狀態發生時,所示的自定義類現在是“第一響應者”。

class CustomDataGridView : DataGridView
{
    protected override void SetSelectedRowCore(
        int rowIndex, 
        bool selected)
    {
        Debug.WriteLine($"{MethodBase.GetCurrentMethod().Name} Row {rowIndex}");
        base.SetSelectedRowCore(rowIndex, selected);
    }
    protected override void SetSelectedCellCore(
        int columnIndex, 
        int rowIndex, 
        bool selected)
    {
        Debug.WriteLine($"{MethodBase.GetCurrentMethod().Name} Column {columnIndex} Row {rowIndex}");
        base.SetSelectedCellCore(columnIndex, rowIndex, selected);
    }
    protected override void SetSelectedColumnCore(
        int columnIndex, 
        bool selected)
    {
        Debug.WriteLine($"{MethodBase.GetCurrentMethod().Name} Column {columnIndex}");
        base.SetSelectedColumnCore(columnIndex, selected);
    }
    protected override bool SetCurrentCellAddressCore(
        int columnIndex, 
        int rowIndex, 
        bool setAnchorCellAddress, 
        bool validateCurrentCell, 
        bool throughMouseClick)
    {
        Debug.WriteLine($"{MethodBase.GetCurrentMethod().Name} Column {columnIndex} Row {rowIndex}");
        return base.SetCurrentCellAddressCore(
            columnIndex, 
            rowIndex, 
            setAnchorCellAddress, 
            validateCurrentCell, 
            throughMouseClick);
    }
} 

事件簿

您的帖子描述了行選擇問題並以問題結尾:“如何防止這種情況? ”這是一種方法。 如果將 DataGridView 的DataSource屬性分配給將通知記錄集更改的源,則DataGridView效果最好並且更易於管理。 如圖所示,此特定示例將使用BindingList創建DataGridView 請自行嘗試代碼,看看您的選擇問題現在是否已修復:

數據網格視圖

假設這是您的Student課程:

class Student
{
    static int _id = 1;
    public int ID { get; } = _id++;
    public string Name { get; set; }
    public string Age { get; set; } = "Select";
}

然后,您可以(例如)將DataSource設置為BindingList<Student> 現在DataGridView將完成選擇事物的工作,而無需大驚小怪。 只需像這樣初始化dataGridView1

private void initDataGridView()
{
    dataGridView1.DataSource = Students;
    // Add one or more Student records to autogenerate columns
    foreach (var student in MockDatabaseQuery())
    {
        Students.Add(student);
    }
    // Swap autogenerated column for DataGridViewComboboxColumn.
    int index = dataGridView1.Columns[nameof(Student.Age)].Index;
    dataGridView1.Columns.RemoveAt(index);
    var comboBoxColumn = 
    new DataGridViewComboBoxColumn
    {
         Name = nameof(Student.Age),
         DataPropertyName = nameof(Student.Age),
         DataSource = new string[]{
             "Select","15 and Under","16","17","18","19","20","21","22","23","24","25 and Older"
        }
    };
    dataGridView1.Columns.Insert(index, comboBoxColumn);

    // Format columns
    dataGridView1.Columns[nameof(Student.ID)].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
    dataGridView1.Columns[nameof(Student.Name)].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
    dataGridView1.Columns[nameof(Student.Age)].Width = 160;
    // Prevents leaving the row in Edit state When the combo box changes
    dataGridView1.CurrentCellDirtyStateChanged += onCurrentCellDirtyStateChanged;
}
BindingList<Student> Students { get; } = new BindingList<Student>();

試驗台

此代碼使用模擬查詢返回Student記錄列表。 這樣我們就可以專注於您所詢問的選擇問題,而無需在線實際數據庫。

private IEnumerable<Student> MockDatabaseQuery()
{
    return new List<Student>
    { 
        new Student { Name = "Katherine Johnson" },
        new Student { Name = "Dorothy Vaughan" },
        new Student { Name = "Mary Jackson" },
        new Student { Name = "Ken Thompson" },
        new Student { Name = "Alan Turing" },
        new Student { Name = "Ada Lovelace" },
        new Student { Name = "Linus Torvalds" },
        new Student { Name = "Mark Zuckerberg" },
        new Student { Name = "Bill Gates" },
        new Student { Name = "Larry Page" },
    };
}

當 ComboBox 更改值時,此處理程序結束行編輯。

private void onCurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    switch (dataGridView1.Columns[dataGridView1.CurrentCell.ColumnIndex].Name)
    {
        case nameof(Student.Age):
            dataGridView1.EndEdit();
            break;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM