簡體   English   中英

向 DataGridView 添加行時出現 NullReferenceException

[英]NullReferenceException while adding row to DataGridView

我在 C# 中向DataGridView添加行有問題,我嘗試添加一個String[]作為DataGridView.Rows.Add參數,但是,總是同樣的問題,現在這是我的最終代碼,它不再起作用,總是NullReferenceException

        {
            ConnectDB con = new ConnectDB();
            CrudDB db = new CrudDB();

            try
            {

                DispoProf disp = new DispoProf(res.ID);
                con.Connexion.Open();
                List<DispoProf> liste = db.Find("dispoprof", disp, "", con.Connexion);
                

                for (int i = 0; i < liste.Count; i += 1)
                {
                    //string[] ligne = { liste[i].date, liste[i].heureDebut, liste[i].heureFin, null, null };
                    dataGridViewListerDV.Rows.Add(liste[i].date, liste[i].heureDebut, liste[i].heureFin, null, null);
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine("Exception :: {0} :: {1} :: {2}",ex.Message, ex.Source , ex.StackTrace);
            }
            finally
            {
                con.Connexion.Close();
            }
        }

它會拋出一個 NullReferenceException

dataGridViewListerDV.Rows.Add(liste[i].date, liste[i].heureDebut, liste[i].heureFin, null, null);

您確定每個liste[i]都不是 null,並且每個 liste[i] 的每個開始時間和每個結束時間都不是 null。 如果這些值為 null,你想顯示什么?

唉,你忘了告訴我們db.Find(...)的返回值,但我很確定liste[i]等於Datetime ,或者屬性heureDebutheureFin可以為空

如果您已定義單元格以便可以顯示可為空的日期時間,請考慮更改您的代碼:

var itemToDisplay = liste[i];
if (itemToDisplay != null)
{
    dataGridViewListerDV.Rows.Add(itemToDisplay.date,
        itemToDisplay.heureDebut, itemToDisplay.heureFin, null, null);
}
else
{
     // decide what to do if item equals null
}

此外,如果 HeureDebut / HeureFine 可能是 null,請考慮更改您的 DataGridViewColumns,以便它們可以顯示可為空的 DateTime 而不是 DateTime。


有改進的余地

初次使用 DataGridViews 的用戶傾向於直接修改 DataGridView 中的行和單元格。 通過這樣做,您將數據(您的模型)與該數據的顯示方式(您的視圖)交織在一起。

很長一段時間以來,有一種趨勢將這兩者分開。 如果您將 model 與視圖分開,您可以輕松更改視圖,而無需更改 model,例如,如果您想在圖表中顯示數據而不是表格,或者如果您想保存XML 文件中的數據,而不是表,您的 model 不必更改。 同樣,如果您不需要表單來顯示它,那么對 model 進行單元測試要容易得多。

將 model 與視圖分開的第三個原因是,它使您可以自由更改 DataGridView 而無需更改 model:您可以添加/刪除列,更改日期時間的顯示方式,顯示不同的顏色負值:無需更改 Model 即可完成所有這些更改。

為了讓您的 Model 和您的視圖分開,您需要一個適配器 class 將您的 model 轉換為您希望它顯示的方式。 這個適配器通常被稱為 ViewModel。

如果您將在幾年后使用 WPF 而不是 Forms,您將看到 model 和視圖之間的這種分離幾乎是強制的,通過使用不同的語言來描述。

但是 Forms 也支持這種分離。

首先,您需要定義將在一行中顯示的 class。 像這樣的東西:

class WorkingHours
{
    public DateTime Date {get; set;}
    public TimeSpan? StartTime {get; set;}
    public TimeSpan? EndTime {get; set;}
}

這樣,可以確定 StartTime 和 EndTime 在同一天。 如果您有夜班,請考慮:

class WorkingHours
{
    public DateTime? StartTime {get; set;}
    public DateTime? EndTime {get; set;}
}

但是你會遇到問題:如果你沒有 StartTime,要顯示什么日期。 在展示您的 model 之前,先讓您的 model 正確,這樣您的屬性就可以很好地定義:哪些值始終可用,哪些值可以為空,它們是否會超出范圍?

使用 Visual Studio Designer,您可能已經定義了列。 您的列有一個屬性DataPropertyName ,它告訴您要顯示的內容:

columnDate.DataPropertyName = nameof(WorkingHours.Date);
columnStartTime.DataPropertyName = nameof(WorkingHours.StartTime);
columnFinishTime.DataPropertyName = nameof(WorkingHours.EndTime);

如果您的 StartTime 和 EndTime 可能是 null,請考慮添加如何顯示 null 值:紅色背景? 或者只有一個'-',也許什么都不顯示?

請參閱:因為您將 model 和您的視圖分開,所以更改視圖不會影響您的模型!

我們需要一種方法來獲取您的數據。 這是您在問題中的方法:

private IEnumerable<WorkingHours> GetWorkingHours(...)
{
    using (var dbConnection = new ConnectedDb(...))
    {
         ... // Create DbCommand, ExecuteQuery and use DbReader to fill WorkingHours
    }
}

注意:如果您將來決定更改獲取數據的方式,例如使用實體框架或 Dapper,或者從 XML 文件中讀取工作時間,這是唯一會改變的地方? 或者更改數據庫布局:再次:Model 更改不會影響您的視圖。

現在我們能夠獲取顯示的數據,Displaying 是一個語句:

this.dataGridView1.DataSource = GetWorkingHours(...).ToList();

等等。 所有獲取的數據都會立即顯示。

但是,這只是顯示。 不監控更改。 如果您想了解更改:添加/刪除/更改行,數據應位於實現 IBindingList 的 object 中,如BindingList<T>

為此,我們需要一行代碼:

private BindlingList<WorkingHours> DisplayedWorkingHours
{
    get => (BindingList<WorkingHours>)this.dataGridView1.DataSource;
    set => this.dataGridView1.DataSource = value;
}

因此,要顯示您的數據:

void InitDisplayedData()
{
    this.DisplayedWorkingHours = new BindingList<WorkingHours>(this.GetWorkingHours().ToList());

}

現在,操作員所做的每一項更改都會在 bindingList 中自動更新。 您不必閱讀行或單元格,只需等待操作員指示他完成了數據編輯,例如通過單擊按鈕:

private void OnButtonOk_Clicked(object sender, ...)
{
    IReadOnlyCollection<WorkingHours> editedWorkingHours = this.DisplayedWorkingHours;

    // Detect which items are added / removed / changed and process the changes:
    this.ProcessEditedWorkingHours(editedWorkingHours);
}

再說一遍:你看到了嗎,因為我將實際數據處理與數據顯示方式分開,所有 model 功能都可以在沒有表單的情況下進行測試。 如果您更改了數據的顯示方式,您的 model 不必更改,如果您更改了 model,則顯示不必更改。

如果您需要處理選定的行,請考慮為此添加功能:

private WorkingHours CurrentWorkingHours =>
   (WorkingHours)this.dataGridView1.CurrentRow?.DataBoundItem;

private IEnumerable<WorkingHours> SelectedWorkingHours =>
   this.dataGridView1.SelectedRows.Cast<DataGridViewRow>()
   .Select(row => row.DataBoundItem)
   .Cast<WorkingHours>();
}

結論

通過將 model 從視圖中分離出來,可以更輕松地更改視圖或 model,而無需更改另一個。 model更容易單元測試,沒有視圖,如果出現問題,您可以在沒有真實數據庫的情況下調試視圖。

Model 和 View 之間的 ViewModel 適配器通常由幾個單行方法組成。

簡單的來吧您好!

暫無
暫無

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

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