[英]NullReferenceException while adding item to ObservableCollection
[英]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
,或者屬性heureDebut
和heureFin
可以為空
如果您已定義單元格以便可以顯示可為空的日期時間,請考慮更改您的代碼:
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.