Listbox Bound to a PSObject list DisplayMember not working - winforms

I have a form with a listbox on it. In this listbox I want to put PSObjects that have these members: Name, Location, ID.
I have created a variable: $list that holds all the PSObjects and I want to bind $list to the $listbox so that anytime $list is modified the $listbox automatically updates.
I am able to bind $list to the $listbox but it doesn't display it properly. I would like only the name member to be visible. The problem is, regardless of if I set the DisplayMember property or not, the $list is still displayed like this:
_______________________________________
|#{Name=Jim; Location=Somewhere; ID=0}|
|#{Name=Sam; Location=Somewhere; ID=1}|
|_____________________________________|
I want it to look like this:
______
|Jim |
|Sam |
|____|
Here's the relevant code I'm working with:
#####PSObject Initialization Code:##########
$obj1 = New-Object -TypeName PSObject -Property #{
'Name' = 'Jim';'Location' = 'Somewhere';'ID' = 0
};$obj2 = New-Object -TypeName PSObject -Property #{
'Name' = 'Sam';'Location' = 'Somewhere';'ID' = 1
};$list = $obj1,$obj2
#####Listbox Code:##########################
$listbox = New-Object System.Windows.Forms.ListBox
$listbox.Dock = 'Fill'
$listbox.SelectionMode = 'MultiExtended'
$listbox.DisplayMember = 'Name'
$listbox.DataSource = $list
#####Form Code##############################
[void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”)
[void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Drawing”)
$form = New-Object System.Windows.Forms.Form
$form.Size = New-Object System.Drawing.Size(500,500)
$form.Controls.Add($listbox); $form.ShowDialog()
Does anyone have an idea about why the DisplayMember property is seemingly overlooked when I bind the list and how to get the display into the desired format?
Note: If the PSObjects are added from the list one at a time, then it displays properly, but this gets clunky in my application.

Figured I would post this even if it's old, a bunch of folk have looked
The trick was to cast the array as an arraylist:
$listbox.DataSource = [system.collections.arraylist]$itemList

Related

PowerShell: Duplicate windows forms with different properties

I am trying make copies of windows forms objects and change the properties of new objects. For example:
$List1 = New-Object System.Windows.Forms.ListBox
$List1.Location = New-Object System.Drawing.Size(10,10)
$List1.Size = New-Object System.Drawing.Size(280,310)
$List2 = $List1
$List2.Location = New-Object System.Drawing.Size(350,10)
The problem is that $List2 is a pointer of $List1. Whatever I change on $List2 always change the properties on $List1. Is there a solution for this?
$List1.Location
IsEmpty X Y
------- - -
False 350 10
$List1.Location
IsEmpty X Y
------- - -
False 350 10
Whatever I change on $List2 always change the properties on $List1. Is there a solution for this?
Yes, the solution is to create a new instance of ListBox:
$List1 = New-Object System.Windows.Forms.ListBox
$List1.Location = New-Object System.Drawing.Size(10,10)
$List1.Size = New-Object System.Drawing.Size(280,310)
$List2 = New-Object System.Windows.Forms.ListBox
$List2.Size = $List1.Size
$List2.Location = New-Object System.Drawing.Size(350,10)
Notice that $List2.Size = $List1.Size is safe, because Size is a struct, and structs are copied on assignment
If you have many properties to reference, you could wrap the common property values in a hashtable to pass to New-Object -Property:
$ListBoxDefaultProperties = #{
Location = New-Object System.Drawing.Size (10,10)
Size = New-Object System.Drawing.Size (280,310)
BackColor = 'Beige'
DisplayMember = 'SomePropertyName'
# etc...
}
$List1 = New-Object System.Windows.Forms.ListBox -Property $ListBoxDefaultProperties
$List2 = New-Object System.Windows.Forms.ListBox -Property $ListBoxDefaultProperties
$List3 = New-Object System.Windows.Forms.ListBox -Property $ListBoxDefaultProperties
$List1 and $List2 reference the same object because you did $List1 = $List2.
You have to create 2 separate instances. You can set all common properties in a loop and then only change the properties that differ:
# 1. create two separate instances
$list1 = New-Object System.Windows.Forms.ListBox
$list2 = New-Object System.Windows.Forms.ListBox
# 2. set properties on both instances
foreach ($list in ($list1, $list2)) {
$list.Location = New-Object System.Drawing.Size(10, 10)
$list.Size = New-Object System.Drawing.Size(280, 310)
# [...]
}
# 3. set all different properties on the 2nd instance only
$list2.Location = New-Object System.Drawing.Size(350, 10)
# [...]

How to limit number of lines in a TextBox and split the lines to strings?

How can I achieve that the input of the TextBox with multilines is only possible to a specific number of lines e.g. 10 lines only.
Further I want to get the input of each line and write each line to a separate variable to work later with this variables. It would be nice if the user gets a messagebox with warning that only 10 lines are possible.
Any help would be appreciated
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
#Assembly PresentationFramework wird geladen
Add-Type -AssemblyName PresentationFramework
$form = New-Object System.Windows.Forms.Form
$form.StartPosition = 'CenterScreen' #Formstartposition Zentrum
$form.Size = New-Object System.Drawing.Size(500,400)
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.DataBindings.DefaultDataSourceUpdateMode = 0
$textBox.Location = New-Object System.Drawing.Point((110),(90))
$textBox.Size = New-Object System.Drawing.Size(288,150)
$textBox.TabIndex = 0
$textBox.Multiline =$true
$form.Controls.Add($textBox)
$form.ShowDialog() | Out-Null
1.Get Line count on the event of TextChanged.
2.Turn the iList into a ArrayList.
3.Get the difference from max line to current line count.
4.Remove the range from the ArrayList.
5.Set the content of the Textbox to the Arraylist
6.Set the curser to end of textbox.
$TextboxMaxLines = 10
$textBox.Add_TextChanged({
If($textBox.Lines.Count -gt $TextboxMaxLines){
[System.Collections.ArrayList]$AL = $textBox.Lines
[int]$LC = ($textBox.Lines.Count - $TextboxMaxLines)
$Al.RemoveRange($TextboxMaxLines, $LC)
$textbox.Lines = $AL
$textbox.SelectionStart = ($textbox.Text.Length)
$textbox.SelectionLength = 0
}
})

Powershell: Creating Custom DataGridView

So I'm trying to create a custom Datagridview to put into a GUI program I've made but I'm having some trouble.
So far I have this:
$form = New-Object System.Windows.Forms.Form
$form.Size = New-Object System.Drawing.Size(900,600)
$dataGridView = New-Object System.Windows.Forms.DataGridView
$dataGridView.Size=New-Object System.Drawing.Size(800,400)
$form.Controls.Add($dataGridView)
$dataGridView.ColumnCount = 4
$dataGridView.ColumnHeadersVisible = $true
$dataGridView.Columns[0].Name = "Process"
$dataGridView.Columns[1].Name = "ID"
$dataGridView.Columns[2].Name = "Description"
$dataGridView.Columns[3].Name = "Memory"
$row1 = get-process -property name | select Name
$rows = #($row1)
foreach ($row in $rows)
{
$dataGridView.Rows.Add($row.name)}
$form.ShowDialog()
My question is this:
How do I go about assigning different columns to differnt properties, so column 'process' would be for the procress name, column 'id' would be for the process id and so on.
so far, all I've managed to do is to assign one column a input range: Process Name.
Please help!
Thanks
Loop on all processes, and add each process properties in the order you have defined the columns:
get-process | foreach{
$dataGridView.Rows.Add($_.Name,$_.ID,$_.Description,$_.WorkingSet)
}
You could also generate the columns dynamically by selecting the properties you want to display, each property becomes a column name, and use the grid's DataSource property and an Array list to add the objects to the grid:
$form = New-Object System.Windows.Forms.Form
$form.Size = New-Object System.Drawing.Size(900,600)
$gps = get-process | select Name,ID,Description,#{n='Memory';e={$_.WorkingSet}}
$list = New-Object System.collections.ArrayList
$list.AddRange($gps)
$dataGridView = New-Object System.Windows.Forms.DataGridView -Property #{
Size=New-Object System.Drawing.Size(800,400)
ColumnHeadersVisible = $true
DataSource = $list
}
$form.Controls.Add($dataGridView)
$form.ShowDialog()
But why no to user Out-GridView from PoSH?:
get-process | select name, process, id, description, workingSet | Out-GridView
Akim - scripters try one-liners with output you can see but do little with, while programmers think about a user interface and putting control of the output in the user's hands, thus System.Windows.Forms.Form is a powershell programmer's best friend.
Shay - I have been doing some programming to use a DataGridView and my experience so far shows me I must take control of defining the DataGridView properties and NOT use .DataSource as shown in one example above. Convenient as it is, you do not then do much with your DataGridView other than show it on the form.
I started by setting .ColCount to the number of columns I wanted. Then named the columns. Thereafter I can tweak each column's properties by numbered location or name. I chose to let the user SORT on selected columns. As of 2013-03-23 I am still working on how to set the backgroundcolor of cells that I want to highlight. Should have that answer soon for those wanting to do the same. One example I found uses the value in the cell, not the location.
$datagridview = New-Object System.Windows.Forms.DataGridView
$datagridview.ColumnCount = 8
$datagridview.Columns[0].Name = "#ID"
$datagridview.Columns[1].Name = "Name"
...[snip]...
$datagridview.Columns[7].Name = "Company"
$datagridview.Columns["Name"].SortMode = "Automatic"
$datagridview.Columns[8].SortMode = "Automatic"
$datagridview.Columns[0].Width = 50
$datagridview.Columns["Description"].Width = 350
...[snip]...
foreach ($_ in $arraylist){[void]$datagridview.Rows.Add($($_.ID), $($_.Name),$($_.Path), $($_.Description), $($_.VM), $($_.WS), $($_.CPU), $($_.Company))}
I tried several ways to fiddle with cell backgroundcolors and only had success with Add_CellPainting. Heed the MSDN warning to NOT set the cellstyle in a specific location unless you truly want that location changed no matter what the user does. In the code below, row 2 column 4 is red no matter how you sort the datagridview. That could be an OOPS or did you really want that. Hmmm.... setting by value does all matching values, so if you have non-unique values then maybe you need additional logic to change only the one you want and repaint if the contents change.
$datagridview.Add_CellPainting(
{
param($Null, $EventArgs)
if ($([String]$EventArgs.Value).ToLower().Contains("ms") -eq $True) { $EventArgs.CellStyle.BackColor = "Blue" ; $EventArgs.CellStyle.ForeColor = "White" }
if ($([String]$EventArgs.Value).ToLower().Contains("windows") -eq $True) { $EventArgs.CellStyle.BackColor = "Yellow" }
if ($([String]$EventArgs.Value).ToLower().Contains("windows powershell") -eq $True) { $EventArgs.CellStyle.BackColor = "Green" }
if (($EventArgs.RowIndex -eq 2) -and ($EventArgs.ColumnIndex -eq 4)) {$EventArgs.CellStyle.BackColor = "Red" }
}
) # End of Add_CellPainting
Have since found another way to highlight the cell of my choosing:
$Script:PS_Log_Viewer_Form_row = $PS_Log_Viewer_Form_dataGridView1.Rows.Add("$($PS_Log_Viewer_total_records_ctr)", "$($PS_Log_Viewer_Form_line_date_time_sub)","$($PS_Log_Viewer_Form_line_acct)","$($PS_Log_Viewer_Form_line_msg)", "$($PS_Log_Viewer_Form_full_filename)-$($PS_Log_Viewer_file_records)")
$PS_Log_Viewer_Form_dataGridView1.Rows[$PS_Log_Viewer_Form_row].Cells[1].Style.BackColor = "BlanchedAlmond"
And searching all rows and setting (think find all):
for ($i = ($PS_Log_Viewer_Form_dataGridView1.FirstDisplayedScrollingRowIndex - 1) ; $i -gt 0 ; $i-- )
{
if ($PS_Log_Viewer_Form_dataGridView1.Rows[$i].Cells[3].Value.Contains("$($find_form_middle_flp_textbox_1.Text)") )
{
$PS_Log_Viewer_Form_dataGridView1.Rows[$i].Cells[3].Style.BackColor = $find_form_middle_flp_color_combobox_dropdownlist.SelectedItem
$PS_Log_Viewer_Form_dataGridView1.FirstDisplayedScrollingRowIndex = $i
$find_form_bottom_remarks = "Previous found at $($i)."
$i = 0
} # End of if ($PS_Log_Viewer_Form_dataGridView1.Rows[$i].Cells[3].Value.Contains("$($Script:PS_Log_Viewer_search_string)") )
} # End of for ($i = 0 ; $i -lt $PS_Log_Viewer_Form_dataGridView1.RowCount ; $i++ )

Powershell: Selecting DataGridView Row

So far I have this code.
$form = New-Object System.Windows.Forms.Form
$form.Size = New-Object System.Drawing.Size(900,600)
$dataGridView = New-Object System.Windows.Forms.DataGridView
$dataGridView.Size=New-Object System.Drawing.Size(800,400)
$go = New-Object System.Windows.Forms.Button
$go.Location = New-Object System.Drawing.Size(300,450)
$go.Size = New-Object System.Drawing.Size(75,23)
$go.text = "Select"
$form.Controls.Add($go)
$form.Controls.Add($dataGridView)
$dataGridView.ColumnCount = 4
$dataGridView.ColumnHeadersVisible = $true
$dataGridView.Columns[0].Name = "Name"
$dataGridView.Columns[1].Name = "ID"
$dataGridView.Columns[2].Name = "Description"
$dataGridView.Columns[3].Name = "Memory"
$dataGridView.Columns[0].width = 240
get-process | foreach {
$dataGridView.Rows.Add($_.Name,$_.ID,$_.Description,$_.WorkingSet) | out-null
}
$go.Add_Click({
$selectedRow = $dataGridView.CurrentRowIndex
write-host $selectedRow
})
[void]$form.ShowDialog()
It simply gets the Process Name, ID, etc. properties and puts them into pre-defined headers in a DataGridView.
My problem is that I want to see the row I've clicked on via $selectedRow = $dataGridView.CurrentRowIndex and output it to the console. Instead, when the 'Select' button is pushed, a blank string is output to the terminal.
You can also get the row index with:
$dataGridView.CurrentCell.RowIndex
or
$dataGridView.SelectedRows[0].Index
You may also want to set the grid MultiSelect property to $false. Currently it allows multiple rows selection. Another thing to consider is setting the SelectionMode property to 'FullRowSelect'. When the grid is populated the first column is selected, not the whole row.
Change
$selectedRow = $dataGridView.CurrentRowIndex
to
$selectedRow = $dataGridView.CurrentRow.Index

Powershell: How to simply output date to DataGridView

I'm having a real problem here.
Take this code:
Get-WmiObject Win32_ComputerSystemProduct -ComputerName summer | Select Vendor,Version,Name,IdentifyingNumber,UUID
I'm trying to get the above Get-WMIobject that has been piped into a Datagridview with the data output the same as you'd see it if it was run via the console directly.
For some reason, it keeps being outputted into the DatagridView, with the columns of Vendor, Version etc. showing what seems to be the directory path or some such data that I don't want.
Try it for yourself in a console, that how I want it to be outputted.
This is the datagridview I'm using:
$arrayt = New-Object System.Collections.ArrayList
$global:target_active_d = Get-WmiObject Win32_ComputerSystemProduct -ComputerName $target_hostname.text | get-member Vendor,Version,Name,IdentifyingNumber,UUID
$arrayt.AddRange($target_active_d)
$target_output.DataSource = $arrayt
Please help
Thanks!
Try something like this:
$arrayt = New-Object System.Collections.ArrayList
$global:target_active_d = Get-WmiObject Win32_ComputerSystemProduct -ComputerName $target_hostname.text | Select-Object Vendor,Version,Name,IdentifyingNumber,UUID
$arrayt.Add($target_active_d)
$target_output.DataSource = $arrayt
This code showed me the fields you selected in your Get-WmiObject code snippet:
Clear-Host
$objForm = New-Object System.Windows.Forms.Form
$objForm.AutoSize = $True
$current_output = New-Object System.Windows.Forms.DataGridView
$current_output.AutoSize = $True
$objForm.Controls.Add($current_output)
$arraylist = New-Object System.Collections.ArrayList
$arraylist.Add((Get-WmiObject Win32_ComputerSystemProduct -ComputerName "." | Select-Object Vendor,Version,Name,IdentifyingNumber,UUID))
$current_output.DataSource = $arraylist
[void]$objForm.Activate()
[void]$objForm.ShowDialog()
[void]$objForm.Dispose()

Resources