powershell wpf xaml treeview binding understanding - wpf

I have a script with a gui based on native WPF and XAML (from here: https://foxdeploy.com/functions/ise-snippets/xaml-to-gui/ ).
The hierarchical structure XAMl Code is from this tutorial: https://dlaa.me/blog/post/9898803 .
For now I try to figure out how should a pscustomobject should look like to pass it to "Binding SubItems".
How can I declare what is a parent item and whats a childitem?
I think its very easy but I can not get it. I have read and tried a lot but can't find any native WPF examples.
Could anyone point me into the right direction. I appreciate any help.
$inputXML = #"
<Window x:Class="SimpleTreeGridUX.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SimpleTreeGridUX" Height="400" Width="400">
<Grid Margin="10">
<!-- TreeGrid "Control" -->
<Border BorderBrush="Black" BorderThickness="1">
<!-- Resources -->
<Border.Resources>
<Style x:Key="TextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="3 0 3 0"/>
</Style>
<Style x:Key="TextBlockBoldStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockStyle}">
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</Border.Resources>
<!-- Content -->
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- Column headers -->
<TreeViewItem Grid.Row="0" BorderThickness="1">
<TreeViewItem.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<!-- Placeholders for two columns of ToggleButton -->
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Task" Style="{StaticResource TextBlockBoldStyle}"/>
<!-- Empty TreeViewItem to measure the size of its ToggleButton into the "Toggle" group-->
<TreeViewItem Grid.Column="1" Padding="0"/>
<TextBlock Grid.Column="3" Text="Duration" Style="{StaticResource TextBlockBoldStyle}"/>
<TextBlock Grid.Column="4" Text="Notes" Style="{StaticResource TextBlockBoldStyle}"/>
</Grid>
</TreeViewItem.Header>
</TreeViewItem>
<!-- Data rows -->
<TreeView Grid.Row="1" x:Name="treeview" ItemsSource="{Binding SubItems}" BorderBrush="Gray" BorderThickness="0 1 0 0">
<TreeView.ItemTemplate>
<!-- Level 0 template leaves space for 2 child "Toggle" levels -->
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Task}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding Duration}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding Notes}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
<!-- Level 1 template leaves space for 1 child "Toggle" level -->
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<ColumnDefinition/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Task}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding Duration}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding Notes}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
<!-- Level 2 template has no children -->
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Task}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding Duration}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding Notes}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Border>
</Grid>
</Window>
"#
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = $inputXML
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch [System.Management.Automation.MethodInvocationException] {
Write-Warning "We ran into a problem with the XAML code. Check the syntax for this control..."
write-host $error[0].Exception.Message -ForegroundColor Red
if ($error[0].Exception.Message -like "*button*"){
write-warning "Ensure your <button in the `$inputXML does NOT have a Click=ButtonClick property. PS can't handle this`n`n`n`n"}
}
catch{#if it broke some other way <img draggable="false" class="emoji" alt="😀" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f600.svg">
Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."
}
#===========================================================================
# Store Form Objects In PowerShell
#===========================================================================
$xaml.SelectNodes("//*[#Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name)}
Function Get-FormVariables{
if ($global:ReadmeDisplay -ne $true){Write-host "If you need to reference this display again, run Get-FormVariables" -ForegroundColor Yellow;$global:ReadmeDisplay=$true}
write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}
Get-FormVariables
#===========================================================================
# Use this space to add code to the various form elements in your GUI
#===========================================================================
$Data = #{
Task = "Main-Task";
Duration = "10";
Notes = "notes";
SubItems = #{
Task = "Task Ebene 2";
Duration = "20";
Notes = "notes2";
SubItems = #{
Task = "Task Ebene 3";
Duration = "30";
Notes = "notes3";
}
}
}
$Object = New-Object -TypeName PSObject -Property $Data
$WPFtreeview.items.Add($Object)
#Reference
#Adding items to a dropdown/combo box
#$vmpicklistView.items.Add([pscustomobject]#{'VMName'=($_).Name;Status=$_.Status;Other="Yes"})
#Setting the text of a text box to the current PC name
#$WPFtextBox.Text = $env:COMPUTERNAME
#Adding code to a button, so that when clicked, it pings a system
# $WPFbutton.Add_Click({ Test-connection -count 1 -ComputerName $WPFtextBox.Text
# })
#===========================================================================
# Shows the form
#===========================================================================
write-host "To show the form, run the following" -ForegroundColor Cyan
$Form.ShowDialog() | out-null

#snicz: Thank you very much for your answer, I don't know why your answer is disappeared, guess a technical problem from stackoverflow.
regarding your answer, you posted the code from David Anson's Project:
TreeViewItem ParentItem = new TreeViewItem();
ParentItem.Header = "Parent";
TreeView1.Items.Add(ParentItem);
//
TreeViewItem Child1Item = new TreeViewItem();
Child1Item.Header = "Child One";
ParentItem.Items.Add(Child1Item);
//
TreeViewItem Child2Item = new TreeViewItem();
Child2Item.Header = "Child Two";
ParentItem.Items.Add(Child2Item);
TreeViewItem SubChild1Item = new TreeViewItem();
SubChild1Item.Header = "Sub Child One";
Child2Item.Items.Add(SubChild1Item);
TreeViewItem SubChild2Item = new TreeViewItem();
SubChild2Item.Header = "Sub Child Two";
Child2Item.Items.Add(SubChild2Item);
TreeViewItem SubChild3Item = new TreeViewItem();
SubChild3Item.Header = "Sub Child Three";
Child2Item.Items.Add(SubChild3Item);
//
TreeViewItem Child3Item = new TreeViewItem();
Child3Item.Header = "Child Three";
ParentItem.Items.Add(Child3Item);
//
ParentItem.IsExpanded = true;
Child2Item.IsExpanded = true;
}
I also found this but I'm looking for a "powershell only" way, how would exactly this looks like if I create pscustomobject?
I also found some Windows Forms Examples but I would like to include it in a project thats only XAML and WPF/Powershell (like the Code in initial Question above).
I'm not sure if its clear what I'am talking about?

got it working this way:
Sure, there is a much shorter way, but it may help someone to understand it easier.
$inputXML = #"
<Window x:Class="SimpleTreeGridUX.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SimpleTreeGridUX" Height="400" Width="400">
<Grid Margin="10">
<!-- TreeGrid "Control" -->
<Border BorderBrush="Black" BorderThickness="1">
<!-- Resources -->
<Border.Resources>
<Style x:Key="TextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="3 0 3 0"/>
</Style>
<Style x:Key="TextBlockBoldStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockStyle}">
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</Border.Resources>
<!-- Content -->
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- Column headers -->
<TreeViewItem Grid.Row="0" BorderThickness="1">
<TreeViewItem.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<!-- Placeholders for two columns of ToggleButton -->
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Task" Style="{StaticResource TextBlockBoldStyle}"/>
<!-- Empty TreeViewItem to measure the size of its ToggleButton into the "Toggle" group-->
<TreeViewItem Grid.Column="1" Padding="0"/>
<TextBlock Grid.Column="3" Text="Duration" Style="{StaticResource TextBlockBoldStyle}"/>
<TextBlock Grid.Column="4" Text="Notes" Style="{StaticResource TextBlockBoldStyle}"/>
</Grid>
</TreeViewItem.Header>
</TreeViewItem>
<!-- Data rows -->
<TreeView Grid.Row="1" x:Name="treeview" ItemsSource="{Binding SubItems}" BorderBrush="Gray" BorderThickness="0 1 0 0">
<TreeView.ItemTemplate>
<!-- Level 0 template leaves space for 2 child "Toggle" levels -->
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Task}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding Duration}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding Notes}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
<!-- Level 1 template leaves space for 1 child "Toggle" level -->
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<ColumnDefinition/>
<ColumnDefinition SharedSizeGroup="Toggle"/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Task}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding Duration}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding Notes}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
<!-- Level 2 template has no children -->
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Task"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition SharedSizeGroup="Duration"/>
<ColumnDefinition SharedSizeGroup="Notes"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Task}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding Duration}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding Notes}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Border>
</Grid>
</Window>
"#
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = $inputXML
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch [System.Management.Automation.MethodInvocationException] {
Write-Warning "We ran into a problem with the XAML code. Check the syntax for this control..."
write-host $error[0].Exception.Message -ForegroundColor Red
if ($error[0].Exception.Message -like "*button*"){
write-warning "Ensure your <button in the `$inputXML does NOT have a Click=ButtonClick property. PS can't handle this`n`n`n`n"}
}
catch{#if it broke some other way <img draggable="false" class="emoji" alt="😀" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f600.svg">
Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."
}
#===========================================================================
# Store Form Objects In PowerShell
#===========================================================================
$xaml.SelectNodes("//*[#Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name)}
Function Get-FormVariables{
if ($global:ReadmeDisplay -ne $true){Write-host "If you need to reference this display again, run Get-FormVariables" -ForegroundColor Yellow;$global:ReadmeDisplay=$true}
write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}
Get-FormVariables
#===========================================================================
# Use this space to add code to the various form elements in your GUI
#===========================================================================
$Data=[pscustomobject]#{Task='Erster Haupteintrag';Duration="10";Notes="notes 1"}
$Child=[pscustomobject]#{Task="Zweite Ebene";Duration="20"; Notes="notes 22"}
$Data2=[pscustomobject]#{Task='Zweiter Haupteintrag';Duration="20";Notes="notes 2"; Subitems=#($Child)}
$Child3=[pscustomobject]#{Task="Dritte Ebene";Duration="35"; Notes="notes 3"}
$Child2=[pscustomobject]#{Task="Zweite Ebene 2";Duration="20"; Notes="notes 222"; Subitems=#($Child3)}
$Data3=[pscustomobject]#{Task='Dritter Haupteintrag';Duration="30";Notes="notes 3"; Subitems=#($Child2)}
$WPFtreeview.items.Add($Data)
$WPFtreeview.items.Add($Data2)
$WPFtreeview.items.Add($Data3)
#Reference
#Adding items to a dropdown/combo box
#$vmpicklistView.items.Add([pscustomobject]#{'VMName'=($_).Name;Status=$_.Status;Other="Yes"})
#Setting the text of a text box to the current PC name
#$WPFtextBox.Text = $env:COMPUTERNAME
#Adding code to a button, so that when clicked, it pings a system
# $WPFbutton.Add_Click({ Test-connection -count 1 -ComputerName $WPFtextBox.Text
# })
#===========================================================================
# Shows the form
#===========================================================================
write-host "To show the form, run the following" -ForegroundColor Cyan
$Form.ShowDialog() | out-null

Related

Assigning value from ComboBox with Add_SelectedValueChanged()

I am trying to retrieve the text from the ComboBox. Searching for the answer, it seems that I need to use the Add_SelectedValueChanged method, but I do not see that method available on my $form variable. I also ckecked the $WPFproductTxt variable, but it isn't there either. What am I missing?
My code snippet (ignore the whitespace in the form, I took fields out for testing):
$rawXAML = #"
<Window x:Class="WpfApp8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp8"
mc:Ignorable="d"
Title="Test" Height="650" Width="425">
<Grid Margin="0,0,0,1">
<TextBlock x:Name="textBlock1" HorizontalAlignment="Left" Margin="10,10,0,0" Text="Test form" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="16" FontWeight="Bold"/>
<Label x:Name="custNameLabel" Content="Customer Name*" Margin="10,43,262,0" VerticalAlignment="Top" />
<TextBox x:Name="custNameTxt" Height="26" Margin="158,43,10,0" TextWrapping="Wrap" VerticalAlignment="Top" Text="" />
<StackPanel Margin="10,263,10,269">
<Label FontWeight="DemiBold" BorderThickness="2" BorderBrush="Gray" HorizontalContentAlignment="Center">Hardware</Label>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<RadioButton x:Name="sharedMark" Content="Shared" IsChecked="False" HorizontalAlignment="Center"/>
<RadioButton x:Name="dedicatedMark" Content="Dedicated" IsChecked="False" Grid.Row="1" HorizontalAlignment="Center"/>
<ComboBox x:Name="productTxt" Margin="0,5,0,5" Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" ToolTip="Hardware type." HorizontalAlignment="Center" Width="182">
<ComboBoxItem Content="Gen 3 SFF" IsSelected="False"/>
<ComboBoxItem Content="Gen 3 Rack" IsSelected="False"/>
<ComboBoxItem Content="Gen 4 Rack" IsSelected="False"/>
<ComboBoxItem Content="Virtual Machine" IsSelected="False"/>
</ComboBox>
</Grid>
</StackPanel>
<Button x:Name="okButton" Content="OK" HorizontalAlignment="Left" Margin="13,578,0,0" VerticalAlignment="Top" Width="50" />
<Button x:Name="cancelButton" Content="Cancel" HorizontalAlignment="Left" Margin="68,578,0,0" VerticalAlignment="Top" Width="50"/>
</Grid>
</Window>
"#
$rawXAML = $rawXAML -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework')
[xml]$XAML = $rawXAML
# Read XAML.
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
$form = [Windows.Markup.XamlReader]::Load($reader)
$xaml.SelectNodes("//*[#Name]") | ForEach-Object { Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name) }
$WPFokButton.Add_Click( {
$form.Close()
})
$WPFcancelButton.Add_Click( { Set-Variable -Name UserCancelled -Value $true -Scope Global })
$null = $form.ShowDialog()
I didn't knew either so I entered this into the console to get the events available to the ComboBox:
$WPFproductTxt | Get-Member -MemberType Event
This lists a SelectionChanged event. Thus I was able to write:
$WPFproductTxt.Add_SelectionChanged({
[Windows.MessageBox]::Show( $WPFproductTxt.SelectedValue )
})
This event shows a message box when I select a different ComboBox item.

PowerShell XAML WPF Window obtaining values

Nice and easy one to explain, but struggling to find an answer.
In the below code, a nice pretty box opens up, and when you select a row, a sub row appears with further data.
What I'm trying to do is get access to the sub row headers.
[void][System.Reflection.Assembly]::LoadWithPartialName( 'presentationframework' )
[xml]$XAML = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="Global Core Desktop Toolbox" Height="550" Width="800" WindowStartupLocation="CenterScreen"
WindowStyle="ToolWindow">
<Grid>
<Grid Background="#FF0A2F64">
<DataGrid Name="Datagrid" AutoGenerateColumns="True" HorizontalAlignment="Left" VerticalAlignment="Top"
Height="250" Width="675" Margin="40,125,0,0" Visibility="Visible" IsReadOnly="True" >
<DataGrid.Columns>
<DataGridTextColumn Header="Domain" Binding="{Binding Domain}" Width="Auto" />
<DataGridTextColumn Header="Total Live PCs" Binding="{Binding LiveMachines}" Width="Auto"/>
<DataGridTextColumn Header="DR" Binding="{Binding DR}" Width="40"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DockPanel Background="GhostWhite">
<Grid Margin="0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="Domain: " FontWeight="Bold" Grid.Row="1" />
<TextBlock Text="{Binding Domain}" Grid.Column="1" Grid.Row="1" />
<TextBlock Text="Disabled Machines: " FontWeight="Bold" Grid.Row="2" />
<TextBlock Text="{Binding SamAccountDisabled}" Grid.Column="1" Grid.Row="2" />
<TextBlock Text="Servers: " FontWeight="Bold" Grid.Row="3" />
<TextBlock Text="{Binding Servers}" Grid.Column="1" Grid.Row="3" />
</Grid>
</DockPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
</Grid>
</Grid>
</Window>
'#
#Read XAML
$READER = ( New-Object System.Xml.XmlNodeReader $XAML )
Try {
$FORM = [Windows.Markup.XamlReader]::Load( $READER )
}
Catch {
Write-Host "Unable to load Windows.Markup.XamlReader."
Sleep 4
}
$XAML.SelectNodes( "//*[#Name]" ) | %{ Set-Variable -Name ( $_.Name ) -Value
$FORM.FindName( $_.Name ) }
$datagrid.AddChild( [pscustomobject]#{
Domain = "happytimes.local"
LiveMachines = 6
DR = 1
Servers = 2
SamAccountDisabled = 3
} )
$form.ShowDialog()
So if i do $datagrid.Columns.header , I have access to "Domain", "Total Live PCs" and "DR" - which i use in a foreach loop. It's the "Domain", "Disabled Machines" and "Servers" I'm trying to get to. $datagrid.items gives me everything with the numbers, but I can't break it down further.
Am i missing something? Any ideas how I get to the variable?
Thanks

Assign an event to an WPF control created via Datatemplate

Using powershell, I have a WPF treeview control, where I used a data template to populate. Here is the example :
https://i.stack.imgur.com/FFMQN.png
My current problem is that I'm not able to register a CLICK event for the controls that are created via DATATEMPLATE. Actually $window.Findname does not find the control, if it has been created via datatemplate.
If I add a simple button via editing XAML code or creating the control during runtime, the FINDNAME or the variable attached to the control works well. and I can register click event.
But if the control is created via datatemplate, FINDNAME simply does not work.
Any suggestions ?
PS: Here is the powershell code WPF embeded :
Cls
Add-Type -Assembly PresentationFramework
Add-Type -Assembly PresentationCore
Function btnReload_click
{
Write-Host $this.name
}
$btnReload_click=({
$MyItemsListProperty = #(New-Object PSObject -Property #{Title='New guy in the town';icon="C:\temp\computer.png";Reloadbtn="Visible";spLoading="Hidden"})
$myDATA.Add($MyItemsListProperty)
$treeView.ItemsSource = $myDATA
})
[xml]$xaml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Threaded WPF Explorer" Height="840" Width="350" Icon="C:\temp\Computer.png" WindowStartupLocation="CenterScreen" >
<Grid>
<TreeView x:Name="foldersTree">
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate DataType="ContentPresenter">
<Grid>
<StackPanel Name="spImg" Orientation="Horizontal">
<Image Name="img"
Source="{Binding icon}"
Width="20" Height="20" Stretch="Fill" VerticalAlignment="Center" />
<TextBlock Text="{Binding Title}" Margin="5,0" VerticalAlignment="Center" />
<Button x:Name="btnReload"
Visibility="{Binding Reloadbtn}"
Height="14" VerticalAlignment="Center"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}}"
>
<TextBlock FontSize="9" Text="Reload..." TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>
</Button>
<StackPanel Name="spLoading" Orientation="Horizontal"
Visibility="{Binding spLoading}"
>
<Grid Height="13" MinWidth="50" MaxWidth="75" Margin="5,0" >
<ProgressBar Name="pbLoading"
Height="13"
MinWidth="50"
MaxWidth="75"
IsIndeterminate="True"
HorizontalAlignment="Center"
VerticalAlignment="Center">
</ProgressBar>
<TextBlock Name="txtLoading" Text="Loading..." FontSize="8.6" Margin="5,0" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="Red"/>
</Grid>
<Button Name="btnCancelLoad"
IsEnabled="$True"
Height="14" VerticalAlignment="Center"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}}"
>
<TextBlock FontSize="9" Text="Cancel" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>
</Button>
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
</TreeView>
<Button Name="btnTest" Height="25" VerticalAlignment="Bottom" IsEnabled="$True">Add Item</Button>
</Grid>
</Window>
"#
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )
$treeView=$Window.FindName("foldersTree")
$tstBTN=$Window.FindName("btnTest")
$treeview.add_SelectedItemChanged({
$sender = $args[0]
Write-Host $this.selecteditem.Title
})
$tstBTN.add_Click($btnReload_click)
$myDATA=New-Object System.Collections.ObjectModel.ObservableCollection[object]
$MyItemsListProperty = #(New-Object PSObject -Property #{Title='Image with computer';icon="C:\temp\computer.png";Reloadbtn="Visible";spLoading="Hidden"})
$myDATA.Add($MyItemsListProperty)
$MyItemsListProperty = #(New-Object PSObject -Property #{Title='Image with folder';icon="C:\temp\folder.png";Reloadbtn="Visible";spLoading="Hidden"})
$myDATA.Add($MyItemsListProperty)
$MyItemsListProperty = #(New-Object PSObject -Property #{Title='Image with diskdrive';icon="C:\temp\diskdrive.png";Reloadbtn="Visible";spLoading="Hidden"})
$myDATA.Add($MyItemsListProperty)
#$myWindow_btnReload.Add_Click($btnReload_click)
$treeView.ItemsSource = $myDATA
$Window.ShowDialog()|Out-Null

powershell datatemplate binding syntax

I'm having problems figuring out the syntax for itemscontrol databinding in powershell. I have a simple WPF script below with 2 examples of itemscontrol with a data template. The first one (list01) has multiple elements and does not show properly, the 2nd itemscontrol (list02) has only one binding element and works just fine.
I'm looking for the correct syntax to bind objects to the first itemscontrol (list01).
Complete powershell script (:
[xml]$xaml = #'
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="MainWindow"
Title="ItemsControlDataBindingSample" Height="350" Width="300">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition MinHeight="20"/>
<RowDefinition MinHeight="50"/>
<RowDefinition MinHeight="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<ItemsControl Name="LIST01" Grid.Row="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}" />
<ProgressBar Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Completion}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Name="LIST02" Grid.Row="2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="{Binding}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
'#
#New-Object System.Windows.Controls.ItemsControl
[void][System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework')
[void][reflection.assembly]::LoadWithPartialName('System.Drawing')
#Read XAML
$Form = [Windows.Markup.XamlReader]::Load( (New-Object System.Xml.XmlNodeReader $xaml) )
#Find objects
$MainWindow = $Form.FindName('MainWindow')
$List01 = $Form.FindName('LIST01')
$List02 = $Form.FindName('LIST02')
## this does not work ##
$Source01 = #(
[ordered]#{ Title=([string]'Complete this WPF tutorial'); Completion=([int]45) },
[ordered]#{ Title=([string]'Learn C#'); Completion=([int]80) },
[ordered]#{ Title=([string]'Wash the car'); Completion=([int]25) }
[ordered]#{ Title=([string]'Make KIDS do homework'); Completion=([int]3) }
);
## this does not work ##
$Source01 = #{ title='test01'; completion=50 }
## this does not work ##
$testArray = #()
$tmpObject = Select-Object -InputObject "" Title,Completion
$tmpObject.Title = 'Complete this WPF tutorial'
$tmpObject.Completion = 45
$testArray += $tmpObject
$List01.ItemsSource = $testArray
#$List01 | gm -type method
## this WORKS ##
$Source02 = #('TEST01','TEST02','TEST03')
$List02.ItemsSource = $Source02
[void]$Form.ShowDialog();
This code is based off a very simple C# example: http://www.wpf-tutorial.com/list-controls/itemscontrol
However, I'm not sure how that syntax directly transates to powershell.
I would probably suggest using an MVVM approach. Just set the DataContext property of the window and then bind the ItemsControl.ItemsSource property to the list you want, e.g. ItemsSource="{Binding MyItemsListProperty}".
A full example of what this could look like (based on the example code you posted above):
$xaml = #"
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="MainWindow"
Title="ItemsControlDataBindingSample" Height="350" Width="300">
<Grid Margin="10">
<ItemsControl ItemsSource="{Binding MyItemsListProperty}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}" />
<ProgressBar Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Completion}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
"#
[void][System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework')
#Read XAML
$window = [Windows.Markup.XamlReader]::Parse($xaml)
$viewModel = New-Object PSObject -Property #{
MyItemsListProperty = #(
New-Object PSObject -Property #{
Title='Complete this WPF tutorial'
Completion=45.0
};
New-Object PSObject -Property #{
Title='Learn C#'
Completion=80.0
};
New-Object PSObject -Property #{
Title='Wash the car'
Completion=25.0
};
New-Object PSObject -Property #{
Title='Make KIDS do homework'
Completion=3.0
};
)
};
$window.DataContext = $viewModel
$window.ShowDialog()

WPF: how can I have ListViewItems fit the window width and use TextEllipsis

I'm currently working with a ListView. Its ListViewItems consist of a left-aligned TextBlock and a right-aligned Button:
Now, I'd like to have my Items always display the Button and shorten the TextBlock accordingly, so they don't need to display a ScrollBar:
Unfortunately, right now that doesn't work:
What can I do to make it work?
Here's my example code:
<Window x:Class="JansWpfTestUmgebung.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ListView x:Name="AllItemsList"
HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch">
<ListViewItem>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
TextTrimming="CharacterEllipsis"
Text="Item 1 with a very long description"/>
<Button Grid.Column="1"
Content="Modify" />
</Grid>
</ListViewItem>
<ListViewItem>Item 2</ListViewItem>
<ListViewItem>Item 3</ListViewItem>
</ListView>
</Window>
Thanks for any hints! :-)
You can do this by forcing a size on the button column:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="JansWpfTestUmgebung.MainWindow"
d:DesignWidth="394">
<ListView x:Name="AllItemsList"
HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListViewItem>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="45" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
TextTrimming="CharacterEllipsis"
Text="Item 1 with a very long description"/>
<Button Grid.Column="1"
Content="Modify"/>
</Grid>
</ListViewItem>
<ListViewItem Content="Item 2"/>
<ListViewItem Content="Item 3"/>
</ListView>
</Window>
The way it was, It does its best to keep the * sized column as big as possible, at the expense of the Auto sized column. But a fixed size will win out against a * sized.
The Most important Parts are these
<ListView HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
That way your ListViewItems will fill the space, and the disabled horizontal scroll bar will prevent them from taking up more space.
My code looks like this
<ListView HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding MyItemSourceList}"
Width="{Binding ActualWidth, ElementName=SearchBox}"
>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding Name}"
TextTrimming="CharacterEllipsis"/>
<TextBlock Grid.Column="1"
Text="{Binding Tag}"
HorizontalAlignment="Right"
FontWeight="Bold"
Foreground="LightGray"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Resources