Home > Mobile >  Problem in databinding Array data to DataGridView in c#
Problem in databinding Array data to DataGridView in c#

Time:06-11

I have been binding short data to DataGridView in C# Winforms. However, I need to bind long string array with size 75 to DataGridView. My data list class consists of 6 individual variables with get and set and array of string which I have defined get and set properties. The individual variables are displayed but the array of strings is not displayed in DataGridView. In debug, I checked the data source of DataGridView and it seems ok. How can I display binded array in gridview.

Below is my source code to populate DataGridView named Logview

    public void populateLogData(string path)
    {
        StreamReader sr = null;
        BindingList<LogList> bindLogList;
        BindingSource bLogsource = new BindingSource();
        List<LogList> loglist = new List<LogList>();
        
        try
        {
            Logview.DataSource = null;
            Logview.Rows.Clear();
            Logview.Columns.Clear();
            Logview.AutoGenerateColumns = true;
            
            if (File.Exists(path))
            {
                try
                {                        
                    sr = new StreamReader(path);
                    StringBuilder readline = new StringBuilder(sr.ReadLine());
                    if (readline.ToString() != null && readline.ToString() != "")
                    {
                        readline = new StringBuilder(sr.ReadLine());
                        while (readline.ToString() != null && readline.ToString() != "")
                        {
                            string[] subdata = readline.ToString().Split(',');
                            LogList tloglist = new LogList(subdata[0], subdata[1], subdata[2], subdata[3], subdata[4], subdata[5], max_index);
                            for (int i = 6; i < subdata.Length; i  )
                                tloglist.setPartList(i-6, subdata[i]);                                
                            loglist.Add(new LogList(subdata, subdata.Length));
                            readline = new StringBuilder(sr.ReadLine());
                        }
                    }
                    bindLogList = new BindingList<LogList>(loglist);
                    bLogsource.DataSource = bindLogList;
                    Logview.AutoGenerateColumns = true;
                    Logview.DataSource = bindLogList;
                    
                    Logview.Columns[0].Width = 140;        // project name
                    Logview.Columns[1].Width = 140;        // data/time

                    Logview.Columns[2].Width = 90;
                    Logview.Columns[3].Width = 90;
                    Logview.Columns[4].Width = 90;
                    Logview.Columns[5].Width = 90;

                    // max_index is set from another part of code
                    for(int i = 0; i <= max_index; i  )
                    {
                        int counter = 6   i;
                        Logview.Columns.Add(headertext[i], headertext[i]);
                        Logview.Columns[counter].Width = 90;
                        Logview.Columns[counter].HeaderText = headertext[i];
                    }
                }
                catch (IOException io)
                {
                    MessageBox.Show("Error: Cannot Open log file.");
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
                finally
                {
                    if (sr != null) sr.Close();
                }
            }
            else
            {
                MessageBox.Show("Log file not found \n"   path);
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
        finally
        {
            GC.Collect();
        }
    }

Below is LogList class

    class LogList
    {
        const int max_size = 100;
        private string[] holdList;
        public string project { get; set; }
        public string date_time { get; set; }
        public string Qty { get; set; }
        public string Pass { get; set; }
        public string Fail { get; set; }
        public string Result { get; set; }
        public string[] partlist
        {
            get
            {
                return holdList;
            }
            set
            {
                holdList = value;
            }
        }

        public LogList(string project, string date_time, string Qty, string Pass, string Fail, string Result, int partsize)
        {
            this.project = project;
            this.date_time = date_time;
            this.Qty = Qty;
            this.Pass = Pass;
            this.Fail = Fail;
            this.Result = Result;

            partlist = new string[partsize 1];            
        }

        public void setPartList(int size, string getValue)
        {
            partlist[size] = getValue;
        }
    }

Project, date/time, Qty, Pass, Fail, Result is displayed. But partlist array is not displayed.

enter image description here

CodePudding user response:

To supplement IVSoftware’s answer, below is an example using two grids in a master-detail scenario.

One issue I would have with your current approach, is that it uses an Array for the “parts list.” Currently this is a string array, and that isn’t going to work if we want to display it in a grid. Fortunately, there are a few easy ways we can get the data to display as we want.

One simple solution is to create a “wrapper” Class for the string. I will call this Class Part. I added a simple int ID property and the string PartName property. You could easily leave out the ID and have a simple string wrapper. This simple Class may look something like…

public class Part {
  public int ID { get; set; }
  public string PartName { get; set; }
}

This should allow the data to display correctly in the grid using just about any construct like an array, list etc.… So, we “could” change your current code to use an array of Part objects like…

Part[] Parts = new Parts[X];

And this would work, however, if we use an array and we know for sure that each LogItem may have a different number of parts in its PartsList, then we will have to manage the array sizes. So, a BindingList of Part objects will simplify this. The altered LogList (LogItem) Class is below…

public class LogItem {
  public BindingList<Part> PartsList { get; set; }
  public string Project { get; set; }
  public string Date_Time { get; set; }
  public string Qty { get; set; }
  public string Pass { get; set; }
  public string Fail { get; set; }
  public string Result { get; set; }

  public LogItem(string project, string date_Time, string qty, string pass, string fail, string result) {
    Project = project;
    Date_Time = date_Time;
    Qty = qty;
    Pass = pass;
    Fail = fail;
    Result = result;
    PartsList = new BindingList<Part>();
  }
}

So given the updated Classes, this should simplify things and we will use the same DataSource for both grids. This DataSource for the “master” grid will be a BindingList of LogItem objects. In the “detail” grid, we simply need to point it’s DataMember property to the PartsList property of the currently selected LogItem. And this would look something like…

dgvLogs.DataSource = LogsBL;
if (LogsBL.Count > 0) {
  dgvParts.DataMember = "PartsList";
  dgvParts.DataSource = LogsBL;
}

Below is the code to test the Classes above in a master-detail scenario with two grids. Create a new winform solution and drop two (2) DataGridViews on the form. The grid on the left is dgvLogs and the grid on the right is dgvParts.

enter image description here

public void populateLogData(string path) {
  BindingList<LogItem> LogsBL = new BindingList<LogItem>();
  string currentLine;
  if (File.Exists(path)) {
    try {
      using (StreamReader sr = new StreamReader(path)) {
        LogItem tempLogItem;
        currentLine = sr.ReadLine();   // <- header row - ignoring
        currentLine = sr.ReadLine();
        while (currentLine != null) {
          if (!string.IsNullOrEmpty(currentLine)) {
            string[] splitArray = currentLine.Split(',');
            if (splitArray.Length >= 6) {
              tempLogItem = new LogItem(splitArray[0], splitArray[1], splitArray[2], splitArray[3], splitArray[4], splitArray[5]);
              for (int i = 6; i < splitArray.Length; i  ) {
                tempLogItem.PartsList.Add(new Part { ID = i, PartName = splitArray[i] });
              }
              LogsBL.Add(tempLogItem);
            }
            else {
              Debug.WriteLine("DataRead Error: Not enough items to make a LogItem: "   currentLine);
            }
          }
          else {
            Debug.WriteLine("DataRead Empty row");
          }
          currentLine = sr.ReadLine();
        }
      }
      dgvLogs.DataSource = LogsBL;
      if (LogsBL.Count > 0) {
        dgvParts.DataMember = "PartsList";
        dgvParts.DataSource = LogsBL;
      }
    }
    catch (IOException io) {
      MessageBox.Show("Error: Cannot Open log file.");
    }
    catch (Exception ex) {
      MessageBox.Show(ex.Message   " Stacktrace- "   ex.StackTrace);
    }
  }
  else {
    MessageBox.Show("Log file not found \n"   path);
  }
}

And some test data…

H1,h2,h3,h4,h5,h6,h7,h8
Model: LMG600N_IF_2blablas,2022-9-6,112,61,51,Fail,p1,p3,p4,p5,p6
1,2022-9-6,2112,621,251,Pass,px4,px5,px6,px1,px2,px3
data1,2022-9-7,3456,789,123,Fail,z3,z3,z4
Model: LMG600N_IF_2blablas,2022-9-6,112,61,51,Fail


Model: LMG600N_IF_2blablas,2022-9-6,112,61,51,Fail,p1,p3,p4,p5,p6,p7,p8,p99
BadData Model: LMG600N_IF_2blablas,2022-9-6,112,61

Moxxxdel: LMG600N_IF_2blablas,2022-9-6,11x2,6x1,5x1,Fail

Hope this helps and makes sense.

CodePudding user response:

Your data list class consists of 6 individual variables with get and set, and an array of string. Your question is about the variables are displayed but the array of strings is not.

Here's what has worked for me (similar to the excellent suggestion by JohnG) for displaying the string array. What I'm doing here is taking a DataGridView and dropping in my main form without changing any settings (other than to Dock it). Given the default settings, the LogList class (shown here in a minimal reproducible example of 1 variable and 1 array of strings) is defined with a public string property named PartList and with this basic implementation:

class LogList
{
    public LogList(string product, string[] partList)
    {
        Product = product;
        _partList = partList;
    }
    public string Product { get; set; }

    private string[] _partList;
    public string PartList => string.Join(",", _partList);
}

To autoconfigure the DataGridView with Product and PartList columns, here is an example initializer method that sets the DataSource and adds the first three items as a test:

// Set data source property once. Clear it, Add to it, but no reason to nullify it.
BindingList<LogList> DataSource { get; } = new BindingList<LogList>();

private void InitDataGridView()
{
    dataGridView1.DataSource = DataSource;
    // Auto config columns by adding at least one Record.
    DataSource.Add(
        new LogList(
            product: "LMG450",
            // Four parts
            partList: new string[]
            {
                "PCT2000",
                "WCT100",
                "ZEL-0812LN",
                "EN61000-3-3/-11",
            }
        ));
    DataSource.Add(
        new LogList(
            product: "LMG600N", 
            // Three parts
            partList: new string[] 
            { 
                "LTC2280",
                "BMS6815",
                "ZEL-0812LN",
            } 
        ));
    DataSource.Add(
        new LogList(
            product: "Long Array",
            // 75 parts
            partList: Enumerable.Range(1, 75).Select(x => $"{ x }").ToArray()
        ));

    // Use string indexer to access columns for formatting purposes.
    dataGridView1
        .Columns[nameof(LogList.Product)]
        .AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;

    dataGridView1
        .Columns[nameof(LogList.PartList)]
        .AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;

}

After running this code, the DGV looks like this:

DGV

With the mouse hovered over the item all 75 "parts" can be viewed.

Hovered tool tip

One last thing - I notice you have some methods to assign a new partList[] of perhaps change an individual part at a specified index. (I didn't show them in the minimal sample but for sure you'll want things like that). You probably know this but make sure to call dataGridView1.Refresh after altering properties of an existing row/LogList object so that the view will reflect the changes.

I hope there's something here that offers a few ideas to achieve the outcome you want.

  • Related