powershell datatemplate binding syntax - wpf

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()

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.

How can I set background color for combobox?

This code shows a combobox without background color. Why this doesn't work and how can I colorize background? What I want to colorize is combobox itself but not its item.
Add-Type -AssemblyName PresentationFramework
[xml]$xaml = #'
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400"
SizeToContent="Height">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<ComboBox
Grid.Row="0" Grid.Column="0"
FontSize="12pt"
Background="Red"
IsEditable="True" />
</Grid>
</Window>
'#
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
$window = [Windows.Markup.XamlReader]::Load($reader)
[void]$window.ShowDialog()
ComboBox Template includes a ToggleButton with its own style,
so you need to add to your window's resources a ControlTemplate with overridden ToggleButton style,
and add to it a Setter for red background.

powershell wpf xaml treeview binding understanding

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

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

Powershell WPF XAML Collapse RowDefinition Height or set to Zero with event

The end effect I'm looking for is to "disappear" row "1", aka: the second row from the top so that Text1 expands down into the area as if row 1 did not exist.
This is a mockup of what I am trying to accomplish. It is nearly exactly the same for the top 2 rows and columns.
If I plug this XAML into VS2017 Community and set the row height to 0 this works. After googling for a couple days I have not come up with a solution.
I do not know C# at all.
Platform:
Windows 7 // Posh v5
Soon to be Windows 10 // Posh v5
Ideally, I'd like to start with it closed and open it on event.
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TabularGrid" Height="300" Width="300">
<Grid Name="Grid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="40" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<TextBox Name="Text1" Background="Silver" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" >Text1</TextBox>
<TextBox Name="Text2" Grid.Row="1" Grid.Column="0" >Text2</TextBox>
<Button Name="Enter" Grid.Row="1" Grid.Column="1" >Enter</Button>
<Button Name="Open" Grid.Row="2" Grid.ColumnSpan="2" >Open</Button>
<Button Name="Close" Grid.Row="3" Grid.ColumnSpan="2" >Close</Button>
</Grid>
</Window>
'#
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Form=[Windows.Markup.XamlReader]::Load( $reader )
$xaml.SelectNodes("//*[#Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name)}
#===========================================================================
# Store Form Objects In PowerShell
#===========================================================================
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
#===========================================================================
# Shows the form
#===========================================================================
$WPFOpen.Add_Click({
$WPFGrid.RowDefinition[1].Visibility = $true
# or
$WPFGrid.RowDefinition[1].Height="40"
})
$WPFClose.Add_Click({
$WPFGrid.RowDefinition[1].Visibility = $false
# or
$WPFGrid.RowDefinition[1].Height="0"
})
#>
$Form.ShowDialog() | out-null
Thank you for looking.
That's because it's not $WPFGrid.RowDefinition but $WPFGrid.RowDefinitions with a s at the end.
I removed Visibility method because it's not implemented with RowDefinitions and changed <RowDefinition Height="40" /> by <RowDefinition Height="0" /> to start window with the row invisible.
This code is working
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TabularGrid" Height="300" Width="300">
<Grid Name="Grid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="0" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<TextBox Name="Text1" Background="Silver" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" >Text1</TextBox>
<TextBox Name="Text2" Grid.Row="1" Grid.Column="0" >Text2</TextBox>
<Button Name="Enter" Grid.Row="1" Grid.Column="1" >Enter</Button>
<Button Name="Open" Grid.Row="2" Grid.ColumnSpan="2" >Open</Button>
<Button Name="Close" Grid.Row="3" Grid.ColumnSpan="2" >Close</Button>
</Grid>
</Window>
'#
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Form=[Windows.Markup.XamlReader]::Load( $reader )
$xaml.SelectNodes("//*[#Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name)}
#===========================================================================
# Store Form Objects In PowerShell
#===========================================================================
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
#===========================================================================
# Shows the form
#===========================================================================
$WPFOpen.Add_Click({
$WPFGrid.RowDefinitions[1].Height= 40
})
$WPFClose.Add_Click({
$WPFGrid.RowDefinitions[1].Height= 0
})
$Form.ShowDialog() | out-null

Resources