0

Greetings from a ultimate rookie.

I'm preparing a time sheet schedule program in WinForms with C# for my geologist crew and having problem while populating PDF tables from DataGridView values.

There is a monthly schedule in my program. When you click Get Excel (I'll change it as PDF), program creates a pdf file from DataGridView values. If you got a full filled datagridview with 31 days, there is no problem. But when the month has 30 days or the user want to get PDF before month ends, it gives null exception.

enter image description here

Code is here.

#region Functions


    DataTable MakeDataTable()
    {
        //Create time sheet table object
        DataTable dtgdata = new DataTable();

        //Define columns
        dtgdata.Columns.Add("Date");
        dtgdata.Columns.Add("Type of Work");
        dtgdata.Columns.Add("Contract");
        dtgdata.Columns.Add("Personnel ID");

       


        #region PopulateData



        string date_1 = dtgList.Rows[0].Cells[1].Value.ToString();
        string date_2 = dtgList.Rows[1].Cells[1].Value.ToString();
        string date_3 = dtgList.Rows[2].Cells[1].Value.ToString();
        string date_4 = dtgList.Rows[3].Cells[1].Value.ToString();
        string date_5 = dtgList.Rows[4].Cells[1].Value.ToString();
        string date_6 = dtgList.Rows[5].Cells[1].Value.ToString();
        string date_7 = dtgList.Rows[6].Cells[1].Value.ToString();
        string date_8 = dtgList.Rows[7].Cells[1].Value.ToString();
        string date_9 = dtgList.Rows[8].Cells[1].Value.ToString();
        string date_10 = dtgList.Rows[9].Cells[1].Value.ToString();
        string date_11 = dtgList.Rows[10].Cells[1].Value.ToString();
        string date_12 = dtgList.Rows[11].Cells[1].Value.ToString();
        string date_13 = dtgList.Rows[12].Cells[1].Value.ToString();
        string date_14 = dtgList.Rows[13].Cells[1].Value.ToString();
        string date_15 = dtgList.Rows[14].Cells[1].Value.ToString();
        string date_16 = dtgList.Rows[15].Cells[1].Value.ToString();
        string date_17 = dtgList.Rows[16].Cells[1].Value.ToString();
        string date_18 = dtgList.Rows[17].Cells[1].Value.ToString();
        string date_19 = dtgList.Rows[18].Cells[1].Value.ToString();
        string date_20 = dtgList.Rows[19].Cells[1].Value.ToString();
        string date_21 = dtgList.Rows[20].Cells[1].Value.ToString();
        string date_22 = dtgList.Rows[21].Cells[1].Value.ToString();
        string date_23 = dtgList.Rows[22].Cells[1].Value.ToString();
        string date_24 = dtgList.Rows[23].Cells[1].Value.ToString();
        string date_25 = dtgList.Rows[24].Cells[1].Value.ToString();
        string date_26 = dtgList.Rows[25].Cells[1].Value.ToString();
        string date_27 = dtgList.Rows[26].Cells[1].Value.ToString();
        string date_28 = dtgList.Rows[27].Cells[1].Value.ToString();
        string date_29 = dtgList.Rows[28].Cells[1].Value.ToString();
        string date_30 = dtgList.Rows[29].Cells[1].Value.ToString();
        string date_31 = dtgList.Rows[30].Cells[1].Value.ToString();


        ////////////////////////////////////////////////////////////



        string work_1 = dtgList.Rows[0].Cells[3].Value.ToString();
        string work_2 = dtgList.Rows[1].Cells[3].Value.ToString();
        string work_3 = dtgList.Rows[2].Cells[3].Value.ToString();
        string work_4 = dtgList.Rows[3].Cells[3].Value.ToString();
        string work_5 = dtgList.Rows[4].Cells[3].Value.ToString();
        string work_6 = dtgList.Rows[5].Cells[3].Value.ToString();
        string work_7 = dtgList.Rows[6].Cells[3].Value.ToString();
        string work_8 = dtgList.Rows[7].Cells[3].Value.ToString();
        string work_9 = dtgList.Rows[8].Cells[3].Value.ToString();
        string work_10 = dtgList.Rows[9].Cells[3].Value.ToString();
        string work_11 = dtgList.Rows[10].Cells[3].Value.ToString();
        string work_12 = dtgList.Rows[11].Cells[3].Value.ToString();
        string work_13 = dtgList.Rows[12].Cells[3].Value.ToString();
        string work_14 = dtgList.Rows[13].Cells[3].Value.ToString();
        string work_15 = dtgList.Rows[14].Cells[3].Value.ToString();
        string work_16 = dtgList.Rows[15].Cells[3].Value.ToString();
        string work_17 = dtgList.Rows[16].Cells[3].Value.ToString();
        string work_18 = dtgList.Rows[17].Cells[3].Value.ToString();
        string work_19 = dtgList.Rows[18].Cells[3].Value.ToString();
        string work_20 = dtgList.Rows[19].Cells[3].Value.ToString();
        string work_21 = dtgList.Rows[20].Cells[3].Value.ToString();
        string work_22 = dtgList.Rows[21].Cells[3].Value.ToString();
        string work_23 = dtgList.Rows[22].Cells[3].Value.ToString();
        string work_24 = dtgList.Rows[23].Cells[3].Value.ToString();
        string work_25 = dtgList.Rows[24].Cells[3].Value.ToString();
        string work_26 = dtgList.Rows[25].Cells[3].Value.ToString();
        string work_27 = dtgList.Rows[26].Cells[3].Value.ToString();
        string work_28 = dtgList.Rows[27].Cells[3].Value.ToString();
        string work_29 = dtgList.Rows[28].Cells[3].Value.ToString();
        string work_30 = dtgList.Rows[29].Cells[3].Value.ToString();
        string work_31 = dtgList.Rows[30].Cells[3].Value.ToString();


        ///////////////////////////////////////////////////////////

        string contract_1 = dtgList.Rows[0].Cells[4].Value.ToString();
        string contract_2 = dtgList.Rows[1].Cells[4].Value.ToString();
        string contract_3 = dtgList.Rows[2].Cells[4].Value.ToString();
        string contract_4 = dtgList.Rows[3].Cells[4].Value.ToString();
        string contract_5 = dtgList.Rows[4].Cells[4].Value.ToString();
        string contract_6 = dtgList.Rows[5].Cells[4].Value.ToString();
        string contract_7 = dtgList.Rows[6].Cells[4].Value.ToString();
        string contract_8 = dtgList.Rows[7].Cells[4].Value.ToString();
        string contract_9 = dtgList.Rows[8].Cells[4].Value.ToString();
        string contract_10 = dtgList.Rows[9].Cells[4].Value.ToString();
        string contract_11 = dtgList.Rows[10].Cells[4].Value.ToString();
        string contract_12 = dtgList.Rows[11].Cells[4].Value.ToString();
        string contract_13 = dtgList.Rows[12].Cells[4].Value.ToString();
        string contract_14 = dtgList.Rows[13].Cells[4].Value.ToString();
        string contract_15 = dtgList.Rows[14].Cells[4].Value.ToString();
        string contract_16 = dtgList.Rows[15].Cells[4].Value.ToString();
        string contract_17 = dtgList.Rows[16].Cells[4].Value.ToString();
        string contract_18 = dtgList.Rows[17].Cells[4].Value.ToString();
        string contract_19 = dtgList.Rows[18].Cells[4].Value.ToString();
        string contract_20 = dtgList.Rows[19].Cells[4].Value.ToString();
        string contract_21 = dtgList.Rows[20].Cells[4].Value.ToString();
        string contract_22 = dtgList.Rows[21].Cells[4].Value.ToString();
        string contract_23 = dtgList.Rows[22].Cells[4].Value.ToString();
        string contract_24 = dtgList.Rows[23].Cells[4].Value.ToString();
        string contract_25 = dtgList.Rows[24].Cells[4].Value.ToString();
        string contract_26 = dtgList.Rows[25].Cells[4].Value.ToString();
        string contract_27 = dtgList.Rows[26].Cells[4].Value.ToString();
        string contract_28 = dtgList.Rows[27].Cells[4].Value.ToString();
        string contract_29 = dtgList.Rows[28].Cells[4].Value.ToString();
        string contract_30 = dtgList.Rows[29].Cells[4].Value.ToString();
        string contract_31 = dtgList.Rows[30].Cells[4].Value.ToString();


        /////////////////////////////

        string p_id = txtPersonelID.Text;



        //Populate with DataGridView
        dtgdata.Rows.Add($"{date_1}", $"{work_1}", $"{contract_1}", $"{p_id}");
        dtgdata.Rows.Add($"{date_2}", $"{work_2}", $"{contract_2}", $"{p_id}");
        dtgdata.Rows.Add($"{date_3}", $"{work_3}", $"{contract_3}", $"{p_id}");
        dtgdata.Rows.Add($"{date_4}", $"{work_4}", $"{contract_4}", $"{p_id}");
        dtgdata.Rows.Add($"{date_5}", $"{work_5}", $"{contract_5}", $"{p_id}");
        dtgdata.Rows.Add($"{date_6}", $"{work_6}", $"{contract_6}", $"{p_id}");
        dtgdata.Rows.Add($"{date_7}", $"{work_7}", $"{contract_7}", $"{p_id}");
        dtgdata.Rows.Add($"{date_8}", $"{work_8}", $"{contract_8}", $"{p_id}");
        dtgdata.Rows.Add($"{date_9}", $"{work_9}", $"{contract_9}", $"{p_id}");
        dtgdata.Rows.Add($"{date_10}", $"{work_10}", $"{contract_10}", $"{p_id}");
        dtgdata.Rows.Add($"{date_11}", $"{work_11}", $"{contract_11}", $"{p_id}");
        dtgdata.Rows.Add($"{date_12}", $"{work_12}", $"{contract_12}", $"{p_id}");
        dtgdata.Rows.Add($"{date_13}", $"{work_13}", $"{contract_13}", $"{p_id}");
        dtgdata.Rows.Add($"{date_14}", $"{work_14}", $"{contract_14}", $"{p_id}");
        dtgdata.Rows.Add($"{date_15}", $"{work_15}", $"{contract_15}", $"{p_id}");
        dtgdata.Rows.Add($"{date_16}", $"{work_16}", $"{contract_16}", $"{p_id}");
        dtgdata.Rows.Add($"{date_17}", $"{work_17}", $"{contract_17}", $"{p_id}");
        dtgdata.Rows.Add($"{date_18}", $"{work_18}", $"{contract_18}", $"{p_id}");
        dtgdata.Rows.Add($"{date_19}", $"{work_19}", $"{contract_19}", $"{p_id}");
        dtgdata.Rows.Add($"{date_20}", $"{work_20}", $"{contract_20}", $"{p_id}");
        dtgdata.Rows.Add($"{date_21}", $"{work_21}", $"{contract_21}", $"{p_id}");
        dtgdata.Rows.Add($"{date_22}", $"{work_22}", $"{contract_22}", $"{p_id}");
        dtgdata.Rows.Add($"{date_23}", $"{work_23}", $"{contract_23}", $"{p_id}");
        dtgdata.Rows.Add($"{date_24}", $"{work_24}", $"{contract_24}", $"{p_id}");
        dtgdata.Rows.Add($"{date_25}", $"{work_25}", $"{contract_25}", $"{p_id}");
        dtgdata.Rows.Add($"{date_26}", $"{work_26}", $"{contract_26}", $"{p_id}");
        dtgdata.Rows.Add($"{date_27}", $"{work_27}", $"{contract_27}", $"{p_id}");
        dtgdata.Rows.Add($"{date_28}", $"{work_28}", $"{contract_28}", $"{p_id}");
        dtgdata.Rows.Add($"{date_29}", $"{work_29}", $"{contract_29}", $"{p_id}");
        dtgdata.Rows.Add($"{date_30}", $"{work_30}", $"{contract_30}", $"{p_id}");
        dtgdata.Rows.Add($"{date_31}", $"{work_31}", $"{contract_31}", $"{p_id}");


        #endregion


        return dtgdata;
    }
    #endregion

Problem is;

When the DataGridView rows are empty with some reason; code gives Null Exception.

What I want to do?

I want to check the values is it null or not, and convert them to a string (like "n/a") if they're null.

I think i need to use foreach, but I don't know how to do it.

Yagiz
  • 43
  • 4
  • What is `dtgList` and where is it coming from? Is there some reason you don’t use it as a data source and turn your wall of code into one line of code? Your current code is “FIXED” to only work with 31 days, so I am not sure why you would expect it to work with less than 31 days. What if the month has less than 31 days? – JohnG Aug 26 '20 at 15:25

2 Answers2

0

You make life difficult for you if you try to fetch the data in the datagridview by accessing the cells.

It is usually easier to tell the DataGridView which kind of items it will be showing, and to tell every column the name of the property it should show. Finally you give the datagridView a collection that contains the items that it should show.

While the operator edits the datagridview, add and removes rows, changes values, you don't do anything. Once the operator notifies you that he finished editing, for instance by clicking the Apply Now or Ok button, you fetch the DataSource. You can be certain that it is up-to-date, you don't have to read the values from the cells yourself.

class AssignedWork
{
    public DateTime Date {get; set;}
    public string WorkType {get; set;}     // can you make this an enum?
    public string Contract {get; set;}
    public int PersonnelId {get; set;}
}

Use visual studio designer to add columns. Set the DataPropertyName

var columnDate = new DataGridViewColumn
{
    ...
    DataPropertyName = nameof(AssignedWork.Date),
}
var columnPersonnelId = new DataGridViewColumn
{
    ...
    DataPropertyName = nameof(AssignedWork.PersonnelId),
}
var columnWorkType : new DataGridViewComboBoxColumn()
{
    ...
    DataPropertyName = nameof(AssignedWork.WorkType),
}

Now get the data that you want to display, and put it in a BindingList, or start with an empty DataGridView:

IList<AssignedWork> initialData = FetchInitialData();
BindingList<AssignedWork> displayedData = new BindingList<AssignedWork>(initialData);
this.dataGridView1.DataSource = displayedData;

You specified in the columns which columns are editable, the order of the columns, and the format in which the property must be displayed, for instance: how do you want to display DateTimes? The format is similar as String.Format.

Operators can add / update / remove rows. While rows are removed / updated, you don't have to do anything.

If the operator adds a new row, you have to fill the row with initial data.

displayedData.AddingNew += OnAddingNewAssignedWork;

When operator adds a new Row, the following is called, you can specify initial contents for the added Row. If desired you can even show a dialog box in which the operator has to select a PersonnelId.

void OnAddingNewAssignedWork(object sender, AddingNewEventArgs e)
{
    e.NewObject = new AssignedWork
    {
         Date = DateTime.Today,
         ...
    };
}

If you don't subscribe to the event, a default new AssignedWork will be added as new Row

While the operator is editing cells, he may want to leave the cell in an invalid format for a while, for instance because he wants to copy-paste data in the cell. It is up to you to decide whether invalid data is allowed. You can do this by handling events CellValidating / RowValidating / Validating.

CellValidating is the easiest: you get the index of the Cell that must be validated, so yo can fetch the cell and the value that is in the cell. You know the column, so you know what should be in it: if the value is incorrect, set e.Cancel to true.

CellValidating is easy, but not very user friendly: the operator has to finish the contents of the cell before he can leave it. He can't copy paste the value from another row in the datagridview. So consider RowValidating or Validating.

If you want to allow operators to leave the cells / row / grid while it doesn't have valid values, consider to disable the OK button, until it has all valid values. You could also colour the background of the invalid cells, to warn the operator that the cell has an invalid value.

dataGridView1.RowValidating += ValidatingRow;

void ValidatingRow(object sender, DataGridViewCellCancelEventArgs e)
{
    // use e.RowIndex to get the Row, and check the validity of the Cells
    // color the BackGround of the Cells that contain invalid data
}
     

After the operator finished all editing, he can click a button:

private void OnButtonOk_Clicked(object sender, ...)
{
    // Get the data from the datagridview and process it.
    // The updated data is already in the datasource:
    this.ProcessData(this.displayedData);

    // or if you forgot to remember the displayed data:
    BindingList<AssignedWork> displayedData = (BindingList<AssignedWork>)this.dataGridView.DataSource;
    this.ProcessData(this.displayedData);
}

void ProcessData(ICollection<AssignedWork> dataToProcess)
{
    foreach (AssignedWork assignedWork in dataToProcess)
    {
        ...
    }
}
    
Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
0

It is unclear what dtgList is. It appears to be a DataGridView. I will assume that dtgList is a DataGridView.

If dtgList has a DataSource, then you should use that DataSource instead of looping through the grid. However, if the grid does NOT have a DataSource then you obviously need to loop through the grid to get the data.

It is almost ALWAYS better to use a “bindingsource/datasource" of some kind. This will make things much easier for you. It is well worth the effort to learn how to use a data source as opposed to “manually” entering data into a grid.

Given this, I will assume that the DataGridView dtgList does NOT have a DataSource and you want to make a DataTable from the DataGridView. Obviously, you can go the route you have in your posted code; however, you already know the problem with this approach… i.e. months with less than 31 days or running the code before the 31 days have elapsed will fail in its current state.

As I commented, your code is FIXED and will only work when there are 31 days. This obvious limitation means you are going to have to do something else and certainly as you stated… ”I think i need to use foreach…” … and you would be correct. A foreach or a simple for loop is needed here.

Currently the picture and the code are somewhat confusing in a sense that the code (unnecessarily) creates 93 variables: 31 date, 31 work and 31 contract variables. These variables come from the cells in the grid dtgList. However, when the code adds the rows to the DataTable it uses the p_id from a text box for the “id” column? The picture shows that the id is present in the grid and I do not know why this value is taken from the text box instead of the grid. This is confusing.

I will assume you want to use the value from the text box.

Assuming the DataGridView dtgList does not have a DataSource, then the following code should produce a DataTable from a given DataGridView then add an “ID” column and fill each row in that column with the value from the text box.

The approach works like this… First an empty DataTable is created. Next, a foreach loop is started to add “all” the columns in the grid. This example assumes the ID column is NOT in the grid. Once the columns have been added to the table, a DataRow variable newRow is created to provide the variable we need to add the individual grid cell values to. The line… newRow = dt.NewRow(); will create a new row with the proper columns. We will add values from the grid to this row and then add the row to the table.

Next, we start a for loop to loop through all the rows in the grid. We could use a foreach loop for this, however, if we use a for loop, we can use the for loops index to reference the grids row later in the code.

Next a check is made to see if the current row in the grid is the “new row”… we want to ignore this row and not add it to the table. Next, another for loop is started to loop through each column in each row. Inside this loop we want to check and make sure that the Value in the grid cell is NOT null. IF, for any reason, the cell value at dgv.Rows[rowIndex].Cells[colIndex].Value is null, the code will throw an exception when we try to call the ToString() method on a null object. If the value is null we simply ignore it as its value is obviously nothing… i.e. empty.

Finally we add the values of each cell in that row to the newRow we created earlier. Then finally add that row to the DataTable.

Next, since the ID column is not present in the original grid, we need to add the ID column, then loop through all the rows in the table and add the value to each of the rows in the ID column. Then finally return the DataTable

private DataTable GetDTFromGrid(DataGridView dgv, string p_id) {
  DataTable dt = new DataTable();
  foreach (DataGridViewColumn column in dgv.Columns) {
    dt.Columns.Add(column.Name);
  }
  DataRow newRow;
  for (int rowIndex = 0; rowIndex < dgv.Rows.Count; rowIndex++) {
    if (!dgv.Rows[rowIndex].IsNewRow) {
      newRow = dt.NewRow();
      for (int colIndex = 0; colIndex < dgv.Columns.Count; colIndex++) {
        if (dgv.Rows[rowIndex].Cells[colIndex].Value != null) {
          newRow[colIndex] = dgv.Rows[rowIndex].Cells[colIndex].Value.ToString();
        }
      }
      dt.Rows.Add(newRow);
    }
  }
  // remove this code if p_id is already in dgv
  dt.Columns.Add(new DataColumn("ID"));
  foreach (DataRow row in dt.Rows) {
    row["ID"] = p_id;
  }
  return dt;
}

For testing and to complete the answer, below is a complete example. When loaded the form fills the grid (on the left) with some data. Note the grid does not have a data source. A button is added to call the method above. Then the returned DataTable is used as a DataSource to the second grid (on the right).

enter image description here

public Form1() {
  InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e) {
  FillGrid(dataGridView1);
}

private void FillGrid(DataGridView dgv) {
  AddColToDGV(dgv, "Date");
  AddColToDGV(dgv, "Type");
  AddColToDGV(dgv, "Contract");
  //AddColToDGV(dgv, "ID");
  DateTime date = DateTime.Now;
  for (int i = 0; i < 10; i++) {
    date = date.Subtract(new TimeSpan(24, 0, 0));
    dgv.Rows.Add(date, "Type_" + i, "Contact" + i);
  }
}

private void AddColToDGV(DataGridView dgv, string name) {
  DataGridViewTextBoxColumn col = new DataGridViewTextBoxColumn();
  col.Name = name;
  dgv.Columns.Add(col);
}

private void button1_Click(object sender, EventArgs e) {
  DataTable dt = GetDTFromGrid(dataGridView1, txtPersonelID.Text);
  dataGridView2.DataSource = dt;
}
JohnG
  • 9,259
  • 2
  • 20
  • 29