Quick one. I want to add Add_DoubleClick() to ListBox in PowerShell, but this doesn't work with implemented ListBox via XAML file... and I'm stuck
I will be very grateful for any help here...
I still have msg warning, that my post is mostly code, so I need to write something here...
# $ErrorActionPreference= 'silentlycontinue'
Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Windows.Forms, System.Drawing
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
$AssemblyLocation = Join-Path -Path $ScriptPath -ChildPath .\themes
foreach ($Assembly in (Dir $AssemblyLocation -Filter *.dll)) {
[System.Reflection.Assembly]::LoadFrom($Assembly.fullName) | out-null
}
[xml]$xaml = #"
<Controls:MetroWindow
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:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Title="ddd"
Height="500"
Width="800"
BorderThickness="0"
GlowBrush="{DynamicResource AccentColorBrush}"
ResizeMode="CanResizeWithGrip"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Cyan.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.AnimatedTabControl.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
</ResourceDictionary.MergedDictionaries>
</Window.Resources>
<Grid>
<ListBox SelectionMode="Single" ItemsSource="{Binding}" x:Name="ListBox" HorizontalAlignment="Right" Height="100" Margin="244,144,0,0" VerticalAlignment="Top" Width="112">
<ListBoxItem Content="Coffie"></ListBoxItem>
</ListBox>
</Grid>
</Controls:MetroWindow>
"#
#Read the form
$Reader = (New-Object System.Xml.XmlNodeReader $xaml)
$Form = [Windows.Markup.XamlReader]::Load($reader)
#AutoFind all controls
$xaml.SelectNodes("//*[#*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object {
New-Variable -Name $_.Name -Value $Form.FindName($_.Name) -Force
}
$ListBox.Items.AddRange()
$ListBox.Add_DoubleClick({
$TextBox2.AppendText("dddd`r`n")
})
...`
I used different approach to my problem:
$smth.Add_IsMouseCapturedChanged({
$smth.SelectedIndex = -1
})
Related
I am just about finished with program and need some help getting over the finish line. What is supposed to do is search through log files in a predefined directory. Nothing happens with the text I enter in the search box nor when I use the button that I created to execute the search. I have searched all over the Internet which has me as far as I am. I see lot's of examples in Winforms but I don't know the "translation" between Winforms and WPF.
I think that if someone can get me passed this hurdle I can implement the other features I want to add.
PS: I really have no programming skills, just tenacity.
Thanks in advance to all who help.
'''
# Import needed Assemblies
Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase,
System.Drawing, System.Windows.Forms, WindowsFormsIntegration
[void][System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework')
[Windows.Forms.Application]::EnableVisualStyles()
#XAML form designed using Visual Studio
# Build the GUI
[xml]$Form = #"
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Log Crawler"
HorizontalAlignment="Center"
FontSize="11" Height="700" Width="1300" FontFamily="Calibri"
WindowStartupLocation="CenterScreen"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled" WindowStyle="ThreeDBorderWindow">
<Grid Margin="0,0,0,0">
<Grid.Background>
<LinearGradientBrush EndPoint="0.8,1" StartPoint="0.8,0">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<TextBox x:Name="IntegrationsLogCrawler" Text="Integration Log Crawler"
HorizontalAlignment="Center" VerticalAlignment="Top" TextAlignment="Center" TextWrapping="NoWrap"
Margin="0,20,0,0 " Height="37" Width="260"
FontSize="24" FontFamily="Calibri" FontWeight="Bold" FontStyle="Normal" FlowDirection="LeftToRight"/>
<GroupBox x:Name="SearchBox" Header="SearchBox"
HorizontalAlignment="Center" VerticalAlignment="Top"
FontSize="12" FontFamily="Calibri" FontWeight="Bold" Foreground="#FFE4E41A"
Margin="0,100,0,0" Height="40" Width="400">
<TextBox x:Name="InputBox" TextWrapping="NoWrap"/>
</GroupBox>
<Button x:Name="LogCrawl" Content="Log Crawl"
HorizontalAlignment="Center" VerticalAlignment="Top"
Margin="0,150,0,0" Height="40" Width="190"
FontSize="18" FontFamily="Calibri" FontWeight="Bold" Foreground="#FFEE0C3F"/>
<Button x:Name="Exit" Content="Exit"
HorizontalAlignment="Center" VerticalAlignment="Top"
Margin="0,200,0,0" Height="39" Width="110"
FontSize="16" FontFamily="Calibri" FontWeight="Bold" Foreground="#FFE4E41A" Background="#FF040404"/>
<GroupBox x:Name="Results" Header="Results"
HorizontalAlignment="Center" VerticalAlignment="Top"
FontSize="12" FontFamily="Calibri" FontWeight="Bold" Foreground="#FFE4E41A"
Margin="0,250,0,0 " Height="250" Width="1000">
<TextBox x:Name="OutputBox" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
</GroupBox>
</Grid>
</Window>
"#
#Create a form
$XMLReader = (New-Object System.Xml.XmlNodeReader $Form)
$XMLForm = [Windows.Markup.XamlReader]::Load($XMLReader)
#Connect to Controls
$xaml.SelectNodes("//*[#*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach {
New-Variable -Name $_.Name -Value $XMLForm.FindName($_.Name) -Force
}
# Load Log Crawl Menu Button Controls
$InputBox = $XMLForm.FindName('InputBox')
$OutputBox = $XMLForm.FindName('OutputBox')
$LogCrawl = $XMLForm.FindName('LogCrawl')
$Exit = $XMLForm.FindName('Exit')
# Logfile Location
$LogFiles = "$($ENV:USERPROFILE)\Documents\_data\logfiles"
# Log Crawl Button Action
$LogCrawl1.Add_Click({
Select-String -Path "$LogFiles\*.*" -Pattern $InputBox -Context 10, 20 | Write-Host
})
# EXIT
$Exit.Add_Click({
$XMLForm.Close()
})
#Show XMLform
$null = $XMLForm.ShowDialog()
'''
I'm not sure about the workings of this part (gave me exception "You cannot call a method on a null-valued expression.") :
#Connect to Controls
$xaml.SelectNodes("//*[#*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach {
New-Variable -Name $_.Name -Value $XMLForm.FindName($_.Name) -Force
}
and I think you should remove it.
Then you made a typo in the button variable name $LogCrawl1 --> $LogCrawl, but also change the Add_Click() method to:
# Log Crawl Button Action
$LogCrawl.Add_Click({
# Logfile Location
$LogFiles = "$($ENV:USERPROFILE)\Documents\_data\logfiles"
$OutputBox.Text = Select-String -Path "$LogFiles\*.*" -Pattern $InputBox.Text -SimpleMatch -Context 10, 20
})
P.S. It is unclear if this is also in your real code, but the final closing "# for the XAML here-string must not have whitespace to the left.
I'm trying to make a remote copy tool, which works fine in the console but I'm having trouble creating the GUI for it.
When I populate my Listbox with the list of files to copy they just don't display as intended...
My problem is in the ChooseFiles function
#xaml Code
$inputXML = #"
<Window x:Class="RemoteCopy.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:RemoteCopy"
mc:Ignorable="d"
Title="Remote Copy" Height="556.841" Width="800">
<Grid>
<Label Content="Remote Computer's IP:" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="RemoteIPTextbox" HorizontalAlignment="Left" Height="20" Margin="238,16,0,0" TextWrapping="Wrap" Text="IP address" VerticalAlignment="Top" Width="128"/>
<Label Content="Username: " HorizontalAlignment="Left" Height="26" Margin="10,40,0,0" VerticalAlignment="Top" Width="131"/>
<Label Content="Password: " HorizontalAlignment="Left" Height="26" Margin="10,70,0,0" VerticalAlignment="Top" Width="131"/>
<TextBox x:Name="UsernameTextbox" HorizontalAlignment="Left" Height="20" Margin="238,46,0,0" TextWrapping="Wrap" Text="Username" VerticalAlignment="Top" Width="128"/>
<Button x:Name="CheckConnectionButton" Content="Check Connection" HorizontalAlignment="Left" Margin="389,46,0,0" VerticalAlignment="Top" Width="107" Height="20"/>
<Button x:Name="ChooseFilesButton" Content="Choose Files" HorizontalAlignment="Left" Height="80" Margin="535,16,0,0" VerticalAlignment="Top" Width="122"/>
<ListView x:Name="FilesListView" HorizontalAlignment="Left" Height="234" Margin="33,128,0,0" VerticalAlignment="Top" Width="720">
<ListView.View>
<GridView>
<GridViewColumn Header="File"/>
<GridViewColumn Header="Size"/>
</GridView>
</ListView.View>
</ListView>
<Button x:Name="StartCopyButton" Content="Start Copy" HorizontalAlignment="Left" Height="24" Margin="33,374,0,0" VerticalAlignment="Top" Width="720"/>
<ProgressBar x:Name="ProgressBar" HorizontalAlignment="Left" Height="20" Margin="113,418,0,0" VerticalAlignment="Top" Width="572"/>
<Label Content="Progress:" HorizontalAlignment="Left" Height="28" Margin="33,418,0,0" VerticalAlignment="Top" Width="67"/>
<Label Content="100%" HorizontalAlignment="Left" Height="28" Margin="714,418,0,0" VerticalAlignment="Top" Width="39"/>
<PasswordBox x:Name="Passwordbox" HorizontalAlignment="Left" Margin="238,78,0,0" VerticalAlignment="Top" Width="128"/>
<Button x:Name="ExitButton" Content="Exit" HorizontalAlignment="Left" Height="21" Margin="283,455,0,0" VerticalAlignment="Top" Width="213"/>
</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{
Write-Warning "Unable to parse XML, with error: $($Error[0])`n Ensure that there are NO SelectionChanged or TextChanged properties in your textboxes (PowerShell cannot process them)"
throw
}
#===========================================================================
# Load XAML Objects In PowerShell
#===========================================================================
$xaml.SelectNodes("//*[#Name]") | %{"trying item $($_.Name)";
try {Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name) -ErrorAction Stop}
catch{throw}
}
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
#===========================================================================
#my test folder
c:
Set-Location c:\temp
function DisplayInBytes($num)
#reference: https://stackoverflow.com/questions/24616806/powershell-display-files-size-as-kb-mb-or-gb
{
$suffix = "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
$index = 0
while ($num -gt 1kb)
{
$num = $num / 1kb
$index++
}
"{0:N1} {1}" -f $num, $suffix[$index]
}
function ChooseFiles
{
$WPFFilesListView.Clear()
$filesToCopy = Get-Files
$filesToCopy.FileNames | ForEach-Object {
$currentfilesize = (Get-Item $_).Length
$currentfilesize = DisplayInBytes -num $currentfilesize
$row = New-Object System.Windows.Forms.ListViewItem($_)
[void]$row.SubItems.Add("$currentfilesize")
[void]$WPFFilesListView.Items.Add($row)
}
}
Function Get-Files($initialDirectory="")
#Reference to https://stackoverflow.com/questions/15885132/file-folder-chooser-dialog-from-a-windows-batch-script
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")|Out-Null
$myFile = new-object Windows.Forms.OpenFileDialog
$myFile.InitialDirectory = Get-Location
$myFile.Filter = "Text Files (*.txt)|*.txt|Powershell Script Files (*.ps1)|*.ps1|Batch Files (*.bat)|*.bat|All Files (*.*)|*.*"
$myFile.ShowHelp = $true
$myFile.Multiselect = $true
[void]$myfile.ShowDialog()
if ($myFile.Multiselect) { $myFile.FileNames } else { $myFile.FileName }
return $myFile
}
$WPFExitButton.Add_Click({$form.close()})
$WPFChooseFilesButton.Add_Click({ChooseFiles})
$Form.ShowDialog() | out-null
The Function to Choose my Files and Populate my Listbox is this one:
function ChooseFiles
{
$WPFFilesListView.Clear()
$filesToCopy = Get-Files
$filesToCopy.FileNames | ForEach-Object {
$currentfilesize = (Get-Item $_).Length
$currentfilesize = DisplayInBytes -num $currentfilesize
$row = New-Object System.Windows.Forms.ListViewItem($_)
[void]$row.SubItems.Add("$currentfilesize")
[void]$WPFFilesListView.Items.Add($row)
}
}
But my Result looks like this:
I intended to have the Filename and Filesize displayed,
If I test the Variables they work
Write-host $_
write-host $currentfilesize
One in each column
You need to change two things to fix the display:
Add a DisplayMemberBinding to the ListView
<ListView x:Name="FilesListView" HorizontalAlignment="Left" Height="234" Margin="33,128,0,0" VerticalAlignment="Top" Width="720">
<ListView.View>
<GridView>
<GridViewColumn Header="File" DisplayMemberBinding="{Binding File}"/>
<GridViewColumn Header="Size" DisplayMemberBinding="{Binding Size}"/>
</GridView>
</ListView.View>
</ListView>
Pass a pscustomobject with the specified binding properties to the ListView
function ChooseFiles
{
$WPFFilesListView.Clear()
$filesToCopy = Get-Files
$filesToCopy.FileNames | ForEach-Object {
$currentfilesize = (Get-Item $_).Length
$currentfilesize = DisplayInBytes -num $currentfilesize
$WPFFilesListView.Items.Add([pscustomobject]#{File=$_;Size=$currentfilesize})
}
}
I want to update my WPF PowerShell GUI. I set up a small GUI with one button and one textbox to test my script. It looks like:
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
Add-Type -AssemblyName System.Windows.Forms
[xml]$XAML = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="test" Height="630" Width="500" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Grid>
<StackPanel>
<Button Name="test" Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75"/>
<TextBox Name="write" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
</StackPanel>
</Grid>
</Window>
"#
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
try {
$Form = [Windows.Markup.XamlReader]::Load($reader)
} catch {
Write-Host "Something is wrong"; exit
}
$xaml.SelectNodes("//*[#Name]") | %{
Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)
}
$test.Add_Click({
Write-Host $varvar
Start-Process powershell -Argument ".\varchecker.ps1"
})
$Form.ShowDialog() | Out-Null
By clicking the button I start another script where I read a variable. This looks like:
while ($true) {
$global:varvar = Get-Content var.txt
Write-Host $varvar
}
Now I want to print the var from the started script to the textbox in the other script. Is this possible? Or the better question: is that a good way to do that?
I don't think you can do it this way. You are trying to start a new PowerShell process, getting some data and passing it to the parent process. I think you can sort of do it in C with pipe() and fork() but havent found a way to do it in PowerShell yet.
You can however, use Start-Job, which creates a process in the background. Alternatively, you can look at Runspaces too for multithreading.
You can also use Invoke-Expression to call the script.
All you have to do from the script is return $Varvar.
If you can tell us what problem you are trying to solve with this method, maybe folks can be more helpful.
Getting values from a new PowerShell process inside your GUI is much too complicated if all you want is to read the contents of a file.
Why not do it all from the GUI code you have?
# [System.Reflection.Assembly]::LoadWithPartialName is deprecated.
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"
Title="test" Height="630" Width="500" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Grid>
<StackPanel>
<Button Name="test" Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75"/>
<TextBox Name="write" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
</StackPanel>
</Grid>
</Window>
"#
$reader = New-Object System.Xml.XmlNodeReader $xaml
try {
$Form = [Windows.Markup.XamlReader]::Load($reader)
}
catch {
Write-Host "Something is wrong"; exit
}
# get references for the button and textbox objects into PowerShell variables.
$button = $Form.FindName("test")
$textBox = $Form.FindName("write")
$button.Add_Click({
# don't start a new PowerShell process, but simply read the file here
$textBox.Text = $var = Get-Content -Path 'PATH TO var.txt' -Raw
Write-Host $var
})
$Form.ShowDialog() | Out-Null
# clean-up
$reader.Dispose()
i recently made a robocopy function with PowerShell so i can transfer files over from one computer to another. However, i cannot figure out how to incorporate a progress bar into my script since i did it a bit differently. Here is an excerpt from the script:
$inputXML = #"
<Window x:Class="DataTransferV2.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:DataTransferV2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Background="#FF0D94D2">
<Grid>
<Label x:Name="head_label" Content="Device Data Transfer" HorizontalAlignment="Left" Margin="151,10,0,0" VerticalAlignment="Top" FontSize="18" FontFamily="Segoe UI Black"/>
<Button x:Name="checkall_button" Content="Check All" HorizontalAlignment="Left" Margin="212,49,0,0" VerticalAlignment="Top" Width="75"/>
<CheckBox x:Name="documents_checkBox" Content="Documents" HorizontalAlignment="Left" Margin="57,102,0,0" VerticalAlignment="Top"/>
<Button x:Name="start_button" Content="Start" HorizontalAlignment="Left" Margin="89,246,0,0" VerticalAlignment="Top" Width="88" Height="36"/>
<Button x:Name="cancel_button" Content="Cancel" HorizontalAlignment="Left" Margin="205,246,0,0" VerticalAlignment="Top" Width="88" Height="36"/>
<Button x:Name="exit_button" Content="Exit" HorizontalAlignment="Left" Margin="320,246,0,0" VerticalAlignment="Top" Width="88" Height="36"/>
</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{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)}
#===========================================================================
# Transfer Documents
#===========================================================================
Function global:Documents_Transfer{
if($WPFdocuments_checkBox.IsChecked -eq $true){
Write-Host "Transferring Documents..."
$quotation = ":"
$source = "$DLetter$quotation\Users\$associd\Documents"
$destination = "C:\Users\$env:USERNAME\Documents"
$copy = "/COPY:DTO"
$files = "*.*"
$backup = "/B"
$reportextrafiles = "/X"
$verboseoutput = "V"
$fullpathname = "FP"
$mirror = "/MIR"
$backupmode = "/ZB"
$subfolders = "/E"
$Retries = "/R:2"
$consolewrite = "/TEE"
robocopy $source $destination $files $backup $reportextrafiles $verboseoutput $fullpathname $mirror $backupmode $subfolders $Retries $consolewrite
Write-Host "Documents Transferred"
}
}
$WPFstart_button.Add_Click({
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
$associd = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the associate ID:", "Verification", "")
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
$DLetter = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the external drive letter:", "Drive", "")
Documents_Transfer
})
#===========================================================================
# Closes the form
#===========================================================================
$WPFexit_button.Add_Click({
$Form.Close()
})
#===========================================================================
# Shows the form
#===========================================================================
$Form.ShowDialog() | out-null
Currently i just have a write-host that attempts to track some progress. Can anyone offer advice as to how i can track this progress? Thanks in advanced!
I am trying to do grouped items in a WPF ComboBox using XAML, and I found this that talks about using a list to drive the grouping. But the example Code Behind is of course not Powershell, and I am having a heck of a time groking how to build the list. Can someone point me at a good resource on CollectionViews in Powershell? I have zero experience in C/C++/C#, though I hope Powershell ends up being a gateway drug. ;)
BTW, I did find this, that talks about doing the collection view in XAML, but the content of my collectionView is going to be dynamic, so I have to build it in code.
BTW, the ultimate goal is a combo box that looks like this...
Sets
Arch
MEP
Viz
Packages
RVT_2015
RVT_2016
MAX_2016
... where the actual sets and packages are dynamically pulled from some user customized XML, and the user can pick one set, or one package. Trying to do two lists and enforce one choice with a UX that isn't frustrating has been, less than effective. So I thought a single grouped combo box would address the problem nicely.
EDIT: Woot to progress!
$locations = #('Amsterdam', 'Berlin', 'London')
$sets = #('Arch', 'MEP', 'Viz')
$packages = #('RVT_2015', 'RVT_2016', 'MAX_2016')
[xml]$xaml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window" Title="Initial Window" WindowStartupLocation = "CenterScreen" ResizeMode="NoResize"
Width = "300" Height = "200" ShowInTaskbar = "True" Background = "lightgray">
<StackPanel>
<Label Content="Define your job:" VerticalAlignment="Center" HorizontalAlignment="Left" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Location" VerticalAlignment="Center" HorizontalAlignment="Left" />
<ComboBox Name="Location" Width="200"
Grid.Row="0" Grid.Column="1"
VerticalAlignment="Center" HorizontalAlignment="Right"
VerticalContentAlignment="Center"
Margin="0,0,0,0">
</ComboBox>
<Label Grid.Row="1" Grid.Column="0" Content="Target" VerticalAlignment="Center" HorizontalAlignment="Left" />
<ComboBox Name="Target"
Grid.Row="1" Grid.Column="1"
VerticalAlignment="Center" HorizontalAlignment="Right"
VerticalContentAlignment="Center"
Width="200" Margin="0,0,0,0">
<ComboBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</StackPanel>
</Window>
"#
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$window=[Windows.Markup.XamlReader]::Load($reader)
$data = #()
foreach ($target in $sets) {
$data += New-Object PSObject -prop #{Name="$target";Category="Sets"}
}
foreach ($target in $packages) {
$data += New-Object PSObject -prop #{Name="$target";Category="Packages"}
}
$listView = [System.Windows.Data.ListCollectionView]$data
$listView.GroupDescriptions.Add((new-object System.Windows.Data.PropertyGroupDescription "Category"))
$location = $window.findname("Location")
$location.ItemsSource = $locations
$target = $window.findname("Target")
$target.ItemsSource = $listView
$window.ShowDialog() | out-null
Based on the first example you have linked in your question, here is a simple implementation in PowerShell
Add-Type –assemblyName WindowsBase
Add-Type –assemblyName PresentationCore
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"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ComboBox Name="comboBox" Width="200" VerticalAlignment="Top" HorizontalAlignment="Left"
Margin="10,20,0,0">
<ComboBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Window>
"#
$reader = (New-Object Xml.XmlNodeReader $xaml)
$GUIWindow = [Windows.Markup.XamlReader]::Load( $reader )
$xaml.SelectNodes("//*[#Name]") | % {Set-Variable -Name ($_.Name) -Value $GUIWindow.FindName($_.Name)}
$data = #()
$data += New-Object PSObject -prop #{Name="Arch";Category="Sets"}
$data += New-Object PSObject -prop #{Name="MEP";Category="Sets"}
$data += New-Object PSObject -prop #{Name="Viz";Category="Sets"}
$data += New-Object PSObject -prop #{Name="RVT_2015";Category="Packages"}
$data += New-Object PSObject -prop #{Name="RVT_2016";Category="Packages"}
$data += New-Object PSObject -prop #{Name="MAX_2016";Category="Packages"}
$lview = [System.Windows.Data.ListCollectionView]$data
$lview.GroupDescriptions.Add((new-object System.Windows.Data.PropertyGroupDescription "Category"))
$comboBox.ItemsSource = $lview
$GUIWindow.ShowDialog() | out-null
This is very basic, but you can build on that.
EDIT:
Original answer was using the following two lines:
$lview = [System.Windows.Data.ListCollectionView]::new($data)
$lview.GroupDescriptions.Add([System.Windows.Data.PropertyGroupDescription]::new("Category"))
They work in PS5, but will produce an error in all previous version of PowerShell. The workaround is to use Typecasting for ListCollectionView. This works in PS2.
$lview = [System.Windows.Data.ListCollectionView]$data
$lview.GroupDescriptions.Add((new-object System.Windows.Data.PropertyGroupDescription "Category"))
I have edited the code with this workaround.