0

I have matter on adding row to DataGridView in C#, I tried to add an String[] as DataGridView.Rows.Add argument but, always the same issue, now this is my final code, and It doesn't work again, always 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();
            }
        }

And It throw a NullReferenceException at

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

  • 6
    Please confirm you have read [what-is-a-nullreferenceexception-and-how-do-i-fix-it](https://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) , understood it, applied its advice and state exactly why you weren't able to make it work, otherwise this question will end up closed as a duplicate of that one. Probably liste[i] is null – Caius Jard Mar 22 '21 at 07:06

1 Answers1

0

Are you sure that every liste[i] is not null, and that every starting hour and every finish hour of every liste[i] is not null.? What do you want to display if any of these values is null?

Alas you forgot to tell us the return value of db.Find(...), but I am pretty sure that either liste[i] equals null, or properties heureDebut et heureFin are nullable Datetime

If you've defined your cells such that you can display nullable Datetimes, consider to change your code:

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
}

Also, if HeureDebut / HeureFine may be null, consider to change your DataGridViewColumns such that they can show nullable DateTime instead of DateTime.


There is room for improvement

First time users of DataGridViews tend to tinker directly with the Rows and Cells in the DataGridView. By doing this, you intertwine data (your model) with the way this data is displayed (your view).

For quite some time now, there is a tendency to keep those two separated. If you keep your model separated from your view, you can easily change the view, without having to change your model, for instance, if you want to show your data in a Graph, instead of a table, or if you want to save the data in an XML file, instead of a table, your model doesn't have to change. Similarly, it is much easier to unit test your model, if you don't need a Form to display it.

A third reason to keep the model separated from the view, is that it gives you the freedom to change your DataGridView without having to change your model: you can Add / Remove columns, change the way how DateTimes are displayed, show a different color for negative values: all these changes can be done without having to change your Model.

To keep your Model and your View separated, you need an adapter class in between that converts your model to the way that you want it displayed. This adapter is quite often called the ViewModel.

If you will be using WPF instead of Forms in a few years, you will see that this separation between model and view is almost enforced, by using a different language to describe the view (XAML).

But Forms also supports this separation.

First you need to define the class that will be displayed in one row. Something like this:

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

This way, it is certain that StartTime and EndTime are on the same day. If you have night shifts, consider:

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

But then you have problems: what Date to show if you haven't got a StartTime. Before dislaying your model, get your model straight, so that your properties are well defined: what values are always available, what values are nullable, can they ever be out-of-range?

Using visual Studio Designer, you probably have defined columns. Your columns have a property DataPropertyName, which tells you what to show:

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

If your StartTime and EndTime may be null, consider to add how to show null values: Red background? or only a '-', maybe show nothing?

See: because you separate your model and your view, that changing the view does not influence your model!

We need a method to fetch your data. This is your method in your question:

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

Note: This is the only place that will change if in future you decide to change how you fetch your data, like use entity framework or Dapper, or read the working hours from an XML file? Or change the database layout: again: Model change does not influence your view.

Now that we are able to fetch the displayed data, Displaying is one statement:

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

Et voila! All fetched data is instantly displayed.

However, this is display only. Changes are not monitored. If you want to know about changes: adding / removing / changing rows, the data should be in an object that implements IBindingList, like BindingList<T>

For this, we need one line of code:

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

So to display your data:

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

}

Now every change made by the operator is automatically updated in the bindingList. You don't have to read Rows nor Cells, just wait until the operator indicates he finished editing the data, for instance by clicking a button:

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

Again: did you see, that because I separate the actual data processing from how the data is displayed, all model functionality can be tested without the form. If you ever change how the data is displayed, your model does not have to change, if you ever change your model, the display does not have to change.

If you need to process selected rows, consider to add functionality for this:

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

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

Conclusion

By separating your model from your view, it is easier to change either the view or the model, without having to change the other. It is easier to unit test the model, without the view, and if problems occur, you can debug the view without a real database.

The ViewModel adapter between the Model and the View consists usually of a few one-liner methods.

Simple comme bonjour!

Harald Coppoolse
  • 24,051
  • 6
  • 48
  • 92
  • I tried to display in Console every result of `liste[i]`and they was not null, so I think that It talk about this `dataGridViewListerDV` – Herin'ny Aina Mar 22 '21 at 10:50