views:

977

answers:

3

I've been struggling with a NullReferenceException and hope someone here will be able to point me in the right direction. I'm trying to create and populate a DataTable and then show the results in a DataGridView control. The basic code follows, and Execution stops with a NullReferenceException at the point where I invoke the new UpdateResults_Delegate. Oddly enough, I can trace entries.Rows.Count successfully before I return it from QueryEventEntries, so I can at least show 1) entries is not a null reference, and 2) the DataTable contains rows of data. I know I have to be doing something wrong, but I just don't know what.

private void UpdateResults(DataTable entries)
{
    dataGridView.DataSource = entries;
}

private void button_Click(object sender, EventArgs e)
{
    PerformQuery();
}

private void PerformQuery()
{
    DateTime start = new DateTime(dateTimePicker1.Value.Year,
                                  dateTimePicker1.Value.Month,
                                  dateTimePicker1.Value.Day,
                                  0, 0, 0);

    DateTime stop  = new DateTime(dateTimePicker2.Value.Year,
                                  dateTimePicker2.Value.Month,
                                  dateTimePicker2.Value.Day,
                                  0, 0, 0);

    DataTable entries = QueryEventEntries(start, stop);
    UpdateResults(entries);
}

private DataTable QueryEventEntries(DateTime start, DateTime stop)
{
    DataTable entries = new DataTable();
    entries.Columns.AddRange(new DataColumn[] {
        new DataColumn("event_type", typeof(Int32)),
        new DataColumn("event_time", typeof(DateTime)),
        new DataColumn("event_detail", typeof(String))});

    using (SqlConnection conn = new SqlConnection(DSN))
    {
        using (SqlDataAdapter adapter = new SqlDataAdapter(
            "SELECT event_type, event_time, event_detail FROM event_log " +
            "WHERE event_time >= @start AND event_time <= @stop",
            conn))
        {
            adapter.SelectCommand.Parameters.AddRange(new Object[] {
                new SqlParameter("@start", start),
                new SqlParameter("@stop", stop)});
            adapter.Fill(entries);
        }
    }
    return entries;
}

Update

I'd like to summarize and provide some additional information I've learned from the discussion here and debugging efforts since I originally posted this question.

I am refactoring old code that retrieved records from a database, collected those records as an array, and then later iterated through the array to populate a DataGridView row by row. Threading was originally implemented to compensate and keep the UI responsive during the unnecessary looping. I have since stripped out Thread/Invoke; everything now occurs on the same execution thread (thank you, Sam).

I am attempting to replace the slow, unwieldy approach using a DataTable which I can fill with a DataAdapter, and assign to the DataGridView through it's DataSource property (above code updated).

I've iterated through the entries DataTable's rows to verify the table contains the expected data before assigning it as the DataGridView's DataSource.

foreach (DataRow row in entries.Rows)
{
    System.Diagnostics.Trace.WriteLine(
        String.Format("{0} {1} {2}", row[0], row[1], row[2]));
}

One of the column of the DataGridView is a custom DataGridViewColumn to stylize the event_type value. I apologize I didn't mention this before in the original post but I wasn't aware it was important to my problem. I have converted this column temporarily to a standard DataGridViewTextBoxColumn control and am no longer experiencing the Exception.

The fields in the DataTable are appended to the list of fields that have been pre-specified in Design view of the DataGridView. The records' values are being populated in these appended fields. When the run time attempts to render the cell a null value is provided (as the value that should be rendered is done so a couple columns over).

In light of this, I am re-titling and re-tagging the question. I would still appreciate it if others who have experienced this can instruct me on how to go about binding the DataTable to the existing column definitions of the DataGridView.

A: 

If the error occurs inside UpdateResults then it sounds like dataGridView is null.

private void UpdateResults(DataTable entries)
{
    dataGridView.DataSource = entries; // when is dataGridView set?
}
Sam
dataGridView is set and displays on the form. I placed it using the UI editor in Visual Studio, as well as named its columns, formatting, etc.
Timothy
@Timothy, so which line exactly does the error occur on? What's the full stack trace? Did you attach a debugger and step through to see what's null?
Sam
I tried refactoring code elsewhere in the application in the same manner that was similar, though one of the elements I'm adding to the dataTable is a custom datatype.entries.Columns.Add("colCustType", typeof(CustDataType));CustDataType ct = new CustDataType();ct.Property = result.GetInt32(2);entries.Rows.Add(..., ct, ...);Here I get the NRE further along the execution path in the custom dgvCell's paint method.Perhaps they are similar? There is a custom dgvCell in the first table as well and maybe I'm not able to identify properly where the exception is occurring because of threading?
Timothy
@Sam, it is getting late. I will try stepping through tomorrow evening and update you here with the results. Thank you for your suggestions!
Timothy
I stepped through the code as suggested and I can confirm the NRE is thrown by code in the custom cell's overridden Paint() method. A null value is being passed into the method which then attempts to parse the value to determine how it should be formatted/displayed. Why there is a null value passed to paint is now my issue. Once I have the DataTable object populated, is there an easy way to see the row contents? Its Rows property seems opaque when inspected in the watch list, but Rows.Count show there are records.
Timothy
@Timothy, use the debug visualizer to see what's in the `DataTable`. Mouse over the reference in the debugger and you'll see a small magnifying class. Click it and it'll show the `DataTable` in a grid.
Sam
I have changed the column to a DataGridViewTextBoxColumn. Here is what I see happening now. http://imgur.com/KL89H The values of each record in the DataTable are appended to columns that have already been configured in Designer mode (there should be 3 columns displayed, not 6). The rendering of the empty columns was what was causing the NRE. How can I set the DataTable so the values map over the existing columns as opposed to being appended?
Timothy
@Timothy, since you have a new problem, ask another question on Stack Overflow. You'll get a better response that way as opposed to just asking more questions in comments. Be sure to post your code related to the DataGridView data population and aspx.
Sam
A: 

Take a look at the following line:

Invoke(new UpdateResults_Delegate(UpdateResults), entries); 

This is a method call for a method that is not present in your code sample above. (It is not the actual invocation of the delegate.) Try stepping into the method and find where the NullReferenceException occurs.

Seventh Element
A: 

I've found the solution here: http://bytes.com/topic/c-sharp/answers/487884-datagridview-datasource-columns-datatable

I need to ensure each column's DataProperyName property is set to the corresponding name of the DataColumn's ColumnName, and set the DataGridView's AutoGenerateColumns property to false.

Timothy