Search code examples
powershelldata-bindingdatagridviewdatasource

GridViewData Binding source from CSV (or other sorce)


I have an idea to write a PowerShell script for creating GUI with a few WF elements including DataGridView to display, edit and store some data (into a $var; @rray;@hash-table/into csv(or another)-file).

Globally I want it to use by the next scheme.

PS creates the GUI form where DGV displays table consist of 3 columns "#"-number,"PageName"-Name of the Page, "shrt"-short Name of the page. In the first row displayed default row values(1, index, ndx).

If user edits default row values or/-and creates new rows - all changes will be automatically accepted1. I see 3 ways to carry out in practice this plan.

  1. Working with unbounded DGV
  2. Working with sourcedata file
  3. Virtual mode - not considered until I wouldn't be able to use 1 and 2 modes.

Universal part of my code

$DataGridView1                    = New-Object      system.Windows.Forms.DataGridView
$DataGridView1.location           = New-Object System.Drawing.Point(20,21)
$DataGridView1.Name               = "Page-List"
$DataGridView1.AllowUserToAddRowsChanged
$DataGridView1.AllowUserToAddRows
$DataGridView1.width              = 363
$DataGridView1.height             = 150
$DataGridView1.ColumnCount = 3
$DataGridView1.ColumnHeadersVisible = $true
$DataGridView1.Columns[0].Name = "#"
$DataGridView1.Columns[0].Width = "40"
$DataGridView1.Columns[1].Name = 'PageName'
$DataGridView1.Columns[1].Width = "205"
$DataGridView1.Columns[2].Name = "shrt"
$DataGridView1.Columns[2].Width = "75"
$DataGridView1.ReadOnly = $false
$DataGridView1.EditMode  = "EditOnEnter"
foreach ($Row in $DataGridView1Data){
  $DataGridView1.Rows.Add($Row)
}

1st. Working with unbounded DGV

DGVData before changes in this case has this values

$DataGridView1Data = @(@($CurrentRow.index+1, "index", "ndx")) #default Row

or

 $DataGridView1Data = @(@("1", "index", "ndx"),@(" "," "," "))

it doesn't matter (except +1 empty string in the 2nd case).

So in this case "accept1" means to declare/edit variable/array/hash-table.

I ran into 2 problems with the values up to date step.

Problem #1:

I couldn't understand an order procedure of updating $DataGridView1Data array value for the edited cells of existing row. There is nothing happened for

$DataGridView1.Add_CellEndEdit({savechanges})
function savechanges {
    $DataGridView1.CurrentCellDirtyStateChanged
}

Problem #2

I couldn't understand how to get new row values array. I think these process requires some code modification in part

foreach ($Row in $DataGridView1Data){
  $DataGridView1.Rows.Add($Row)
}

Some of googoleplex variants I've tried

 foreach ($row in $DataGridView1Data){
     [ordered]@{ 
         $DataGridView.Columns[0].Name = $cell[0].Value ;
           $DataGridView.Columns[1].Name = $cell[1].Value ;
            $DataGridView.Columns[2].Name = $cell [3].Value;}
 }

or

$DataGridView1Data.Add_CellValueChanged({Bind-Pages})
 function Bind-Pages {
     Foreach ($DataGridView1.Row in $DataGridView1RowCollection){
     $DataGridView1.Row = @{id = $cell[0].text; name = $cell[1].text; shrt = $cell[2].text}
}

or

{$DataGridView1.Row = @($Cell[0].Value, $Cell[1].Value, $Cell[2].Value)}

By the way "$CurrentRow.Index+1" always return "1" regardless of its actual value.$DGV.RowCount always returns "1". But $Row[i] - returns correct arrays for all existed Rows.This is the most weirdest.

I think both troubles requires an addition some code entries for a new row and for events okbutton click.

My question:

How could I get arrays or hash-tables with updated values for edited rows? (incl. added by me.) Or updated hash-table for all DGVData.

I DONT ASK YOU TO WRITE FULL CODE FOR ME.COULD YOU JUST TELL ME AN ALGORITHM OR GIVE ME THE LINK TO MANUAL. PLEASE JUST NOT THIS $H[ i ] +

I was reading docs.ms for 2 long weeks and absolutely nothing became more understandable... including because I don't know C#.. I installed MS Office, SQL, PowerShell Studio.. but still I can't find any solutions

2nd. Working with bounded DGV

In this way accepted1 means for me updating data in source file or export new values to other file. Not important.

But I couldn't get closer for export/update than the source binding stage.

I created CSV: https://yadi.sk/i/RDwpL37TNwROgg Screenshot

And tried this:

$x =  @(Import-Csv -Delimiter ";"  "C:\Users\vkons\OneDrive\Документы\PowerShell\Scripts\SANDBOXES\WEB\resourses\CSV\DATA1.csv"  -Header "#", "Page_Name", "shrt" )
   $DataGridView1Data = @($x)

First column cells values bound by the hash-tables row {All columnsNameS =All cellvalueS in this row}. Firstly I thought that because my csv content has wrong format (without header). Noway. but nothing

https://yadi.sk/i/umWT6EnwDkmg0Q -Screenshot

and if try this

$DataGridView1.Row[0] = $x[0]

returns an error

InvalidOperation: Cannot index into a null array.

I made another googolplex different attempts to bind data using ".DataSource" property to DGV; DGVData; DGV.Rows; DGVRowCollection. Nothing worked properly.

So, my 2nd question:

Could you write me an ALGORITHM for binding sourcefile data?


Solution

  • This question is very broad and it is unclear what “specifically” you are asking. From your comment… ”JUST TELL ME AN ALGORITHM” … is not really possible. No algorithm will fit all situations. Without specific details a “general” algorithm may be present, however it would still lack specifics that would be different depending on the context in which it is used in.

    For starters I am only guessing that you MUST use PowerShell for this or PowerShell is what you are most familiar with. This can definitely be done in PowerShell, however depending on the environment you code in, using PowerShell IMHO is going to make this much more difficult.

    I am just saying that IMHO, this would be much easier using an IDE like Visual Studio with C# or VB. I am guessing there may be other options, but an IDE that helps with errors is going to be crucial and from my limited PowerShell experience, help with errors is fairly non-existent. I am not trying to dissuade you from using PowerShell and my opinion is clearly based from a NON-PowerShell user. I am just saying that I do not use PowerShell much and I had a difficult time trying to help here using PowerShell.

    Getting off my soap box, I can say that there are a couple things that I found that may be causing you some problems when trying to bind a data source to the grid.

    To begin, in the posted code under the “2nd. Working with bounded DGV.” Header section, there are two lines of code…

    $x =  @(Import-Csv -Delimiter ";"  "C:\Users\vkons\OneDrive\Документы\PowerShell\Scripts\SANDBOXES\WEB\resourses\CSV\DATA1.csv"  -Header "#", "Page_Name", "shrt" )
    $DataGridView1Data = @($x)
    

    From what I can tell, after the lines have executed successfully, is that the variable $DataGridView1Data is an Object[] array. And each item/element in $DataGridView1Data is also an Object. Specifically. it appears that each item is a System.Management.Automation.PSCusomObject type. I will not deny that this “custom” object contains simple fields (#, PageName, shrt) however, the grid and a binding source are going to have problems resolving these properties with these “generic objects.” I do not know “why” this is and can only validate from my tests, that the line of code below in the foreach loop through all $DataGridView1Data rows… will fail…

    $DataGridView1.Rows.Add($Row)
    

    The grid is NOT going to parse out the individual values into the separate columns. This is adding the rows directly to the grid that already has columns. Even setting each column’s DataPropertyName failed to properly display the data.

    Fortunately, since the $DataGridView1Data object array DOES actually have the values we need, manually parsing out the values from each row appears to work when adding the rows to the grid…

    $DataGridView1.Rows.Add($Row.'#', $Row.Page_Name, $Row.shrt)
    

    Next it appears you want to use a BindingSource. The same issues previously described are going to remain using a BindingSource. A binding source is going to want a DataSet, DataTable or List<T> among others. Again it is not going to parse out the Object. Therefore, if you want to use a BindingSource, then you need to convert the data to an appropriate data source. In this case the example below uses a DataTable for this.

    Similar to looping through the $DataGridView1Data object and adding the rows to the grid directly, we use the same loop and add the rows to a DataTable instead. We can then use this DataTable itself as a data source to the grid OR we can use it as data source to a BindingSource. Either way will work, and in the example below a BindingSource is introduced.

    First a DataTable is created and the three (3) columns are added. Then a loop through the data to add the rows to the table. Next the table is used as a data source to the BindingSource and finally the grids data source is set to the BindingSource.

    $GridDT = New-Object system.Data.DataTable
    $col = New-Object System.Data.DataColumn(“#”)
    $GridDT.Columns.Add($col)
    $col = New-Object System.Data.DataColumn(“Page_Name”)
    $GridDT.Columns.Add($col)
    $col = New-Object System.Data.DataColumn(“shrt")
    $GridDT.Columns.Add($col)
    
    # add rows to the DataTable
    foreach ($Row in $DataGridView1Data){
      $GridDT.Rows.Add($Row.'#', $Row.Page_Name, $Row.shrt)
    }
    # use the DataTable as a data source to the binding source
    $DataGridViewBS = New-Object system.Windows.Forms.BindingSource
    $DataGridViewBS.DataSource = $GridDT
    $DataGridView1.DataSource = $DataGridViewBS
    

    I have tested this code and it appears to work successfully. I hope this helps.