./images/folder.png as source in WPF - wpf

I'm new to PowerShell and WPF. I am creating an app with folder treeview but unable to add the icons for the treeviewitem. I followed this link but unable to show the icons on the treeviewitems. The ObjectTagToImageConverter is working fine but I'm not sure if this path "./images/folder.png" is correct to show the icons. Any assistance you guys can provide would be greatly appreciated.
PowerShell Script
Try { Set-ExecutionPolicy -ExecutionPolicy 'ByPass' -Scope 'Process' -Force -ErrorAction 'Stop' } Catch {}
# Project Root Path
$Global:pathPanel= split-path -parent $MyInvocation.MyCommand.Definition
if ($MyInvocation.MyCommand.CommandType -eq "ExternalScript") {
$ScriptPath = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
} else {
$ScriptPath = Split-Path -Parent -Path ([Environment]::GetCommandLineArgs()[0]
)
if (!$ScriptPath){
$ScriptPath = "."
}
}
#Add WPF and Windows Forms assemblies
try{
Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase,system.windows.forms
[System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework')
[System.Reflection.Assembly]::LoadFrom("$ScriptPath\assembly\System.Windows.Interactivity.dll")
[System.Reflection.Assembly]::LoadFrom("$ScriptPath\assembly\dev4sys.Tree.dll")
} catch {
Write-Verbose $_.Exception.Message
Throw "Failed to load assemblies."
}
function Import-Xaml-MainPage {
[xml]$XAMLWindow = $XAMLContent
$manager = New-Object System.Xml.XmlNamespaceManager -ArgumentList $XAMLWindow.NameTable
$manager.AddNamespace("x", "http://schemas.microsoft.com/winfx/2006/xaml");
$xamlReader = New-Object System.Xml.XmlNodeReader $XAMLWindow
[Windows.Markup.XamlReader]::Load($xamlReader)
}
function OneDriveBackupTabFolderTreeView {
$dummyNode = $null
$AllUserFiles = [IO.Directory]::GetFiles("C:\Users")
$AllUserDirectory = [IO.Directory]::GetDirectories("C:\Users")
# ================== Handle Folders ===========================
foreach ($UserFolder in $AllUserDirectory | Where {$_ -ne "C:\Users\All Users" -And $_ -ne "C:\Users\Default" -And $_ -ne "C:\Users\Default User" -And $_ -ne "C:\Users\Public" -And $_ -NotLike "*.dotnet" -And $_ -NotLike "*.nbi" -And $_ -NotLike "*.oracle_jre_usage" -And $_ -NotLike "*.templateengine"}){
$treeViewItem = [Windows.Controls.TreeViewItem]::new()
$treeViewItem.FontSize = "14"
$treeViewItem.Header = $UserFolder.Substring($UserFolder.LastIndexOf("\") + 1)
$treeViewItem.Tag = #("folder",$UserFolder)
$treeViewItem.Items.Add($dummyNode) | Out-Null
$treeViewItem.Add_Expanded({
Write-Host $_.OriginalSource.Header " is expanded"
TreeExpanded($_.OriginalSource)
})
$onedriveFolderTreeview.Items.Add($treeViewItem)| Out-Null
$treeViewItem.Add_PreviewMouseLeftButtonDown({
[System.Windows.Controls.TreeViewItem]$sender = $args[0]
[System.Windows.RoutedEventArgs]$e = $args[1]
Write-Host "Left Click: $($sender.Tag)"
})
$treeViewItem.Add_PreviewMouseRightButtonDown({
[System.Windows.Controls.TreeViewItem]$sender = $args[0]
[System.Windows.RoutedEventArgs]$e = $args[1]
Write-Host "Right Click: $($sender.Tag)"
})
}
# ================== Handle Files ===========================
foreach ($UserFile in $AllUserFiles | Where {$_ -NotLike "*.ini" -And $_ -NotLike "*.lnk" -And $_ -NotLike "*.pst" -And $_ -NotLike "*.DAT" -And $_ -NotLike "*.LOG" -And $_ -NotLike "*.db"}){
$treeViewItem = [Windows.Controls.TreeViewItem]::new()
$treeViewItem.Header = $UserFile.Substring($UserFile.LastIndexOf("\") + 1)
if ($UserFile | Where {$_ -like "*.csv" -Or $_ -like "*.xlsx" -Or $_ -like "*.xlsm" -Or $_ -like "*.xls"}) {
$treeViewItem.Tag = #("excel",$UserFile)
} elseif ($UserFile | Where {$_ -like "*.docx" -Or $_ -like "*.doc" -Or $_ -like "*.docm" -Or $_ -like "*.dotm"}) {
$treeViewItem.Tag = #("word",$UserFile)
} elseif ($UserFile | Where {$_ -like "*.pptx" -Or $_ -like "*.pptm" -Or $_ -like "*.ppt" -Or $_ -like "*.potm"}) {
$treeViewItem.Tag = #("powerpoint",$UserFile)
} elseif ($UserFile | Where {$_ -like "*.msg"}) {
$treeViewItem.Tag = #("outlook",$UserFile)
} elseif ($UserFile | Where {$_ -like "*.png" -Or $_ -like "*.jpg" -Or $_ -like "*.jpeg" -Or $_ -like "*.jpe" -Or $_ -like "*.jxr" -Or $_ -like "*.gif" -Or $_ -like "*.bmp"}) {
$treeViewItem.Tag = #("image",$UserFile)
} else {
$treeViewItem.Tag = #("file",$UserFile)
}
$onedriveFolderTreeview.Items.Add($treeViewItem)| Out-Null
$treeViewItem.Add_PreviewMouseLeftButtonDown({
[System.Windows.Controls.TreeViewItem]$sender = $args[0]
[System.Windows.RoutedEventArgs]$e = $args[1]
Write-Host "Left Click: $($sender.Tag)"
})
$treeViewItem.Add_PreviewMouseRightButtonDown({
[System.Windows.Controls.TreeViewItem]$sender = $args[0]
[System.Windows.RoutedEventArgs]$e = $args[1]
Write-Host "Right Click: $($sender.Tag)"
})
}
}
Function TreeExpanded($sender){
$dummyNode = $null
$item = [Windows.Controls.TreeViewItem]$sender
If ($item.Items.Count -eq 1 -and $item.Items[0] -eq $dummyNode)
{
$item.Items.Clear();
Try
{
foreach ($string in [IO.Directory]::GetDirectories($item.Tag[1].ToString()) | Where {$_ -NotLike "*.dotnet" -And $_ -NotLike "*.nbi" -And $_ -NotLike "*.oracle_jre_usage" -And $_ -NotLike "*.templateengine"})
{
$subitem = [Windows.Controls.TreeViewItem]::new();
$subitem.Header = $string.Substring($string.LastIndexOf("\") + 1)
$subitem.Tag = #("folder",$string)
$subitem.Items.Add($dummyNode)
$subitem.Add_Expanded({
TreeExpanded($_.OriginalSource)
})
$item.Items.Add($subitem) | Out-Null
$subitem.Add_PreviewMouseLeftButtonDown({
[System.Windows.Controls.TreeViewItem]$sender = $args[0]
[System.Windows.RoutedEventArgs]$e = $args[1]
Write-Host "Left Click: $($sender.Tag)"
})
$subitem.Add_PreviewMouseRightButtonDown({
[System.Windows.Controls.TreeViewItem]$sender = $args[0]
[System.Windows.RoutedEventArgs]$e = $args[1]
Write-Host "Right Click: $($sender.Tag)"
})
}
foreach ($file in [IO.Directory]::GetFiles($item.Tag[1].ToString()) | Where {$_ -NotLike "*.ini" -And $_ -NotLike "*.lnk" -And $_ -NotLike "*.pst" -And $_ -NotLike "*.DAT" -And $_ -NotLike "*.LOG" -And $_ -NotLike "*.db"}){
$subitem = [Windows.Controls.TreeViewItem]::new()
$subitem.Header = $file.Substring($file.LastIndexOf("\") + 1)
if ($file | Where {$_ -like "*.csv" -Or $_ -like "*.xlsx" -Or $_ -like "*.xlsm" -Or $_ -like "*.xls"}) {
$subitem.Tag = #("excel",$file)
} elseif ($file | Where {$_ -like "*.docx" -Or $_ -like "*.doc" -Or $_ -like "*.docm" -Or $_ -like "*.dotm"}) {
$subitem.Tag = #("word",$file)
} elseif ($file | Where {$_ -like "*.pptx" -Or $_ -like "*.pptm" -Or $_ -like "*.ppt" -Or $_ -like "*.potm"}) {
$subitem.Tag = #("powerpoint",$file)
} elseif ($file | Where {$_ -like "*.msg"}) {
$subitem.Tag = #("outlook",$file)
} elseif ($file | Where {$_ -like "*.png" -Or $_ -like "*.jpg" -Or $_ -like "*.jpeg" -Or $_ -like "*.jpe" -Or $_ -like "*.jxr" -Or $_ -like "*.gif" -Or $_ -like "*.bmp"}) {
$subitem.Tag = #("image",$file)
} else {
$subitem.Tag = #("file",$file)
}
$item.Items.Add($subitem)| Out-Null
$subitem.Add_PreviewMouseLeftButtonDown({
[System.Windows.Controls.TreeViewItem]$sender = $args[0]
[System.Windows.RoutedEventArgs]$e = $args[1]
Write-Host "Left Click: $($sender.Tag)"
})
$subitem.Add_PreviewMouseRightButtonDown({
[System.Windows.Controls.TreeViewItem]$sender = $args[0]
[System.Windows.RoutedEventArgs]$e = $args[1]
Write-Host "Right Click: $($sender.Tag)"
})
}
}
Catch [Exception] { }
}
}
OneDriveBackupTabFolderTreeView
$MainPageWindow.ShowDialog() | Out-Null
WPF
<Window x:Name="wpfWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Controls1="clr-namespace:System;assembly=mscorlib"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:sys="clr-namespace:dev4sys.Tree;assembly=dev4sys.Tree"
Width="848"
Height="573"
Background="White"
Icon="file:///sourceLogoMain"
ResizeMode="CanMinimize"
Title="Title"
WindowStartupLocation="CenterScreen" Topmost="True">
<Grid>
<Label Content="" Height="57" Margin="0,1,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Top" Grid.Column="0" Grid.Row="0">
<Label.Background>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.79045092838196285" Color="#FF031438" />
<GradientStop Offset="1" Color="#FFFFFFFF" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Label.Background>
</Label>
<Label Height="42" Margin="78,9,21,0" HorizontalAlignment="Stretch" VerticalAlignment="Top" Background="{x:Null}" Grid.Column="0" Grid.Row="0" />
<Label Content="Title" Width="367" Height="42" Margin="0,9,419.5,0" HorizontalAlignment="Right" VerticalAlignment="Top" Background="{x:Null}" FontSize="20" Foreground="#FFF9FAFC" Grid.Column="0" Grid.Row="0" />
<Image Width="39" Height="36" Margin="11,12,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Source="file:///sourceLogoMain" Grid.Column="0" Grid.Row="0" />
<TabControl Width="813" Height="458" Margin="11,66,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Column="0" Grid.Row="0">
<TabControl.Resources>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Grid Name="Panel">
<ContentPresenter x:Name="ContentSite" Margin="15,5" HorizontalAlignment="Center" VerticalAlignment="Center" ContentSource="Header" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" TargetName="Panel" Value="LightGreen" />
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock FontWeight="Bold" Text="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter Property="Background" TargetName="Panel" Value="LightSkyBlue" />
<Setter Property="FontWeight" Value="Normal" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabControl.Effect>
<DropShadowEffect />
</TabControl.Effect>
<TabItem x:Name="backupTab" Cursor="Hand" FontSize="12" FontWeight="Normal" Header="Backup User Data (OneDrive)" Visibility="Visible">
<TabItem.Effect>
<DropShadowEffect />
</TabItem.Effect>
<Canvas>
<GroupBox Width="275" Height="122" BorderBrush="#FF0400F2" BorderThickness="2,2,2,2" Cursor="Arrow" FontWeight="Bold" Foreground="Black" Header="Connect to OneDrive Admin" Canvas.Left="9" Canvas.Top="13">
<Canvas>
<Label Content="Email Address" Width="92" Height="26" FontSize="10" Canvas.Left="6" Canvas.Top="0" />
<Label x:Name="connectOneDriveLabel" Content="Failed" Width="135" Height="25" HorizontalContentAlignment="Center" Background="#FFFF002E" FontSize="12" Visibility="Hidden" Canvas.Left="125" Canvas.Top="-3.5" />
</Canvas>
</GroupBox>
<Button x:Name="connectOneDriveButton" Content="Connect" Width="132" Height="28" FontFamily="Segoe UI" FontSize="13" FontWeight="Bold" IsEnabled="False" Canvas.Left="62.5" Canvas.Top="96" />
<Image x:Name="connectOneDriveIcon" Width="34" Height="28" Cursor="Arrow" Source="file:///sourceConnectedOneDriveIcon" Canvas.Left="199" Canvas.Top="96" />
<TextBox x:Name="connectOneDriveTextBox" Width="241" Height="20" BorderBrush="#FF020000" BorderThickness="0,0,0,2" Cursor="Pen" Canvas.Left="26" Canvas.Top="62" />
<GroupBox Width="505" Height="405" BorderBrush="#FF0400F2" BorderThickness="2,2,2,2" Cursor="Arrow" FontWeight="Bold" Foreground="Black" Header="Select Folders to Backup" Canvas.Left="294" Canvas.Top="13" />
<TreeView x:Name="onedriveFolderTreeview" Width="489" Height="367" BorderBrush="{x:Null}" Canvas.Left="302" Canvas.Top="45">
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<HierarchicalDataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="onedriveFolderFileCheckBox"/>
<Image Width="20" Height="20" Stretch="Fill" Margin="5,0"
Source="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type TreeViewItem}},
Path=Tag,
Converter={sys:ObjectTagToImageConverter}}"/>
<TextBlock Text="{Binding}" Margin="10,0"/>
</StackPanel>
</HierarchicalDataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
</TreeView>
</Canvas>
</TabItem>
<TabItem x:Name="removeUserTab" Cursor="Hand" FontWeight="Normal" Header="Remove User Profile">
<TabItem.Effect>
<DropShadowEffect />
</TabItem.Effect>
<Canvas />
</TabItem>
<TabItem x:Name="uninstallAppTab" Cursor="Hand" FontSize="12" FontWeight="Normal" Header="Uninstall Applications">
<TabItem.Effect>
<DropShadowEffect />
</TabItem.Effect>
<Canvas />
</TabItem>
<TabItem x:Name="removeDomainTab" Cursor="Hand" FontSize="12" FontWeight="Normal" Header="Remove From Domain">
<TabItem.Effect>
<DropShadowEffect />
</TabItem.Effect>
<Canvas />
</TabItem>
</TabControl>
</Grid>
</Window>
WPF Window

Related

Powershell WPF update on Event

I'm having trouble updating my WPF-Window-Forms.
Basically, I want to trigger different content in my "Console-TextBox" depending on the current connected Network-SSID.
[xml]$xaml = Get-Content -Path $PSScriptRoot\Pattern.xaml
$manager = New-Object System.Xml.XmlNamespaceManager -ArgumentList $xaml.NameTable
$manager.AddNamespace("x", "http://schemas.microsoft.com/winfx/2006/xaml");
$xamlReader = New-Object System.Xml.XmlNodeReader $xaml
[Windows.Markup.XamlReader]::Load($xamlReader)
}
$window = Load-Xaml
$textbox = $window.FindName("textbox")
$textbox_console = $window.FindName("textbox_console")
Get-EventSubscriber | Unregister-Event
Register-EngineEvent -SourceIdentifier 'MyEvent' -Action {$textbox_console.Text += "ManualInput"}
$networkChange = [System.Net.NetworkInformation.NetworkChange];
Register-ObjectEvent -InputObject $networkChange -EventName NetworkAddressChanged -SourceIdentifier NetworkChanged -Action {
$textbox_console.Text += "`nNetworkChanged"
}
$textbox.Add_TextChanged({
New-Event -SourceIdentifier 'MyEvent'
})
$window.ShowDialog() | Out-Null
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Pattern" Width="600" Height="400" WindowStyle="None" ResizeMode="NoResize"
WindowStartupLocation="CenterScreen">
<StackPanel>
<TextBox x:Name="textbox" HorizontalAlignment="Left" Height="70" Margin="124,104,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="262"/>
<TextBox x:Name="textbox_console" HorizontalAlignment="Left" Height="70" Margin="124,104,0,0" TextWrapping="Wrap" Text="console" VerticalAlignment="Top" Width="262"/>
</StackPanel>
</Window>
I'm new to Event Handling and not sure whether this issue is caused by Thread/Process-handling or wrong EventHandling...
Maybe somebody could point out some hint and tell me, how to use the NetworkChange-Event to update the text of the textbox?

PowerShell WPF - Binding DataGrid ComboBox to Column In ItemsSource

I am trying to populate a ComboBox within a DataGrid with unique values from a given column, however I'm getting unexpected results in that it splits the value from that row into individual characters and populates each ComboBox with said characters.
Here's a simple example script of my issue;
$csv = "ID,Fruit,Owner`r`n"
$csv += "1,Apple,Andrew`r`n"
$csv += "2,Banana,Bill`r`n"
$csv += "3,Cherry,Charles`r`n"
$csv += "4,Date,Daniel`r`n"
$csv += "5,Elderberry,Ethan`r`n"
$data = ConvertFrom-Csv $csv
$inputXML = #"
<Window x:Name="DataGridComboTest" x:Class="DataGridComboTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataGridComboTest" Width="640" Height="480" WindowStartupLocation="CenterScreen">
<Grid>
<DataGrid x:Name="DataGrid" Margin="10,10,10,10" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ID}" Header="ID"/>
<DataGridTextColumn Binding="{Binding Fruit}" Header="Fruit"/>
<DataGridTextColumn Binding="{Binding Owner}" Header="Owner"/>
<DataGridTemplateColumn Header="Owner Combo">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
SelectedItem="{Binding Path=Owner, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Owner}"
Text="{Binding Path=Owner, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
"#
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = $inputXML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Form=[Windows.Markup.XamlReader]::Load( $reader )
$xaml.SelectNodes("//*[#Name]") | %{ Set-Variable -Name "$($_.Name)" -Value $Form.FindName($_.Name) -ErrorAction Stop }
$DataGrid.ItemsSource = $data
$Form.ShowDialog() | Out-Null
What I'd like to be able to do is select a different owner for each fruit from the existing owners in the table, however instead I'm given a choice of each letter in the adjacent owner's name;
You should bind the ItemsSource to an IEnumerable<string> rather than a scalar string (which is an IEnumerable<char>).
Try something like this:
...
ItemsSource="{DynamicResource owners}"
...
$owners = $data | Select-Object -ExpandProperty Owner -Unique
$Form.Resources.Add("owners", $owners)
With mm8's help I was able to come up with a working script;
$csv = "ID,Fruit,Owner`r`n"
$csv += "1,Apple,Andrew`r`n"
$csv += "2,Banana,Bill`r`n"
$csv += "3,Cherry,Charles`r`n"
$csv += "4,Date,Daniel`r`n"
$csv += "5,Elderberry,Ethan`r`n"
$csv += "6,Fig,Bill`r`n"
$csv += "7,Grape,Daniel`r`n"
$data = ConvertFrom-Csv $csv
$inputXML = #"
<Window x:Name="DataGridComboTest" x:Class="DataGridComboTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataGridComboTest" Width="640" Height="480" WindowStartupLocation="CenterScreen">
<Grid>
<DataGrid x:Name="DataGrid" Margin="10,10,10,10" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ID}" Header="ID"/>
<DataGridTextColumn Binding="{Binding Fruit}" Header="Fruit"/>
<DataGridTextColumn Binding="{Binding Owner}" Header="Owner"/>
<DataGridTemplateColumn Header="Owner Combo">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
SelectedItem="{Binding Path=Owner, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{DynamicResource owners}"
Text="{Binding Path=Owner, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
"#
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = $inputXML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Form=[Windows.Markup.XamlReader]::Load( $reader )
$xaml.SelectNodes("//*[#Name]") | %{ Set-Variable -Name "$($_.Name)" -Value $Form.FindName($_.Name) -ErrorAction Stop }
$DataGrid.ItemsSource = $data
#$owners = [Linq.Enumerable]::ToArray($data | Select-Object -ExpandProperty Owner)
$owners = $data | Select-Object -ExpandProperty Owner -Unique
$Form.Resources.Add("owners", $owners)
$Form.ShowDialog() | Out-Null
The DynamicResource binding was key, but the [Linq.Enumerable] line from mm8's answer threw an error. However simplifying it to select unique owners from $data resolved this. I also added a couple of duplicate owners to the source data to more accurately simulate a real-world scenario, hence "-Unique".

Powershell ListView using WPF - Items not showing properly

Im trying to output into two columns the result of the following query:
get-mailbox -identity *#$SearchDomain | where ismailboxenabled -eq true
Here is what i've done right now:
$mailboxes = get-mailbox -identity *#$SearchDomain | where ismailboxenabled -eq true
foreach($line in $mailboxes){
$new = new-object System.Windows.Forms.ListViewItem($line.Name)
$new.SubItems.Add($line.Alias + "#" + $SearchDomain)
$WPFLstActiveMailboxes.Items.Add($new)
}
My problem is that things are showing up like this in my GUI:
Behind the black boxes is the data that I want
EDIT 1:
$inputXML = #"
<Window x:Name="TMS_MailboxToolkit" x:Class="TMS_MailboxToolki.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:TMS"
mc:Ignorable="d"
Title="TMS - Mailbox Toolkit" Height="354.303" Width="527.152">
<Grid Margin="0,0,-8,-5">
<Button x:Name="BtnCalculate" Content="Calculate" HorizontalAlignment="Left" Margin="432,289,0,0" VerticalAlignment="Top" Width="75"/>
<Label x:Name="LblSearchDomain" Content="Search domain" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" FontWeight="Bold"/>
<TextBox x:Name="TxtSearchDomain" HorizontalAlignment="Left" Height="23" Margin="10,42,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" Text="malicis.com"/>
<Label x:Name="LblActiveMailboxes" Content="Active mailboxes" HorizontalAlignment="Left" Margin="10,74,0,0" VerticalAlignment="Top" FontWeight="Bold"/>
<Label x:Name="LblCurrentCount" Content="Current count:" HorizontalAlignment="Left" Margin="10,286,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="TxtCurrentCount" HorizontalAlignment="Left" Height="23" Margin="101,290,0,0" TextWrapping="Wrap" Text="00000" VerticalAlignment="Top" Width="45" IsEnabled="False" FontWeight="Bold"/>
<ProgressBar x:Name="PrgStatus" HorizontalAlignment="Left" Height="10" Margin="345,70,0,0" VerticalAlignment="Top" Width="162"/>
<TextBox x:Name="TxtStatus" HorizontalAlignment="Left" Height="23" Margin="345,42,0,0" TextWrapping="Wrap" IsEnabled="False" Text="idle" VerticalAlignment="Top" Width="162" FontStyle="Italic"/>
<Label x:Name="LblStatus" Content="Status" HorizontalAlignment="Left" Margin="345,10,0,0" VerticalAlignment="Top" FontWeight="Bold"/>
<ListView x:Name="LstActiveMailboxes" HorizontalAlignment="Left" Height="181" Margin="10,100,0,0" VerticalAlignment="Top" Width="497">
<ListView.View>
<GridView>
<GridViewColumn Header="Disable">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Margin="5, 0" IsChecked="False"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn >
<GridViewColumn Header="Name"/>
<GridViewColumn Header="Alias"/>
<GridViewColumn Header="Mailbox"/>
</GridView>
</ListView.View>
</ListView>
</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 properties (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
#===========================================================================
# Shows the form
#===========================================================================
Add-Type -AssemblyName System.Windows.Forms
Set-Variable -Name credsalreadyprovided -Value $false -Scope global
Set-Variable -Name CONFIG_SERVER -Value "exchpd01" -Scope global
$WPFBtnCalculate.Add_Click({
if ($credsalreadyprovided -eq $true){
calculate
}
else{
initiatesession
calculate
}
})
function initiatesession{
$WPFPrgStatus.Value = 0
Set-Variable -Name cred -Value (get-credential) -Scope global
$credsalreadyprovided = $true
$WPFPrgStatus.Value = 25
$WPFTxtStatus.Text = "Establishing session with " + $CONFIG_SERVER + "..."
Set-Variable -Name session -Value (New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$CONFIG_SERVER/PowerShell/ -credential $cred) -Scope global
$WPFPrgStatus.Value = 50
$WPFTxtStatus.Text = "Importing session..."
Import-PSSession $session -AllowClobber
}
function calculate{
$WPFPrgStatus.Value = 100
$WPFTxtStatus.Text = "Querying " + $CONFIG_SERVER + "..."
$SearchDomain=$WPFTxtSearchDomain.Text
$WPFTxtCurrentCount.Text = (get-Mailbox -identity *#$SearchDomain | where ismailboxenabled -eq true).count
#$mailboxes = get-mailbox -identity *#$SearchDomain | where ismailboxenabled -eq true
get-mailbox -identity *#$SearchDomain | where ismailboxenabled -eq true | ForEach-Object{
$name = $_.Name
$entry = New-Object System.Windows.Controls.ListViewItem($name)
$alias = $_.Alias
$entry.SubItems.Add($alias)
$mailbox = $_.Alias + "#" + $SearchDomain
$entry.SubItems.Add($mailbox)
$WPFLstActiveMailboxes.Items.Add($entry)
}
}
$Form.ShowDialog() | out-null
What I have always done is use databinding in WPF so that it is bound to the property that I want and then just create a psobject with those properties that I have bindings for in WPF, and then just add them to the ListView.Items collection.
Here is a reference of the wpf setup, and then just make sure to have your psobject properties the exact same as the binding properties in the wpf
DataGridView Column binding in WPF

DataGrid AddChild in Runspace

I've created a PowerShell Runspace and wanted to populate a DataGrid from a CSV file. The code works fine without a Runspace, but not in it.
I think the error must be in this two lines, but I can't get it:
$csv = Import-Csv "C:\name.csv" -Delimiter ";" -Encoding UTF8 |
Select-Object #{Name='USER';Expression={$_.USER}},`#{Name='ID';Expression={$_.ID}}
$csv | % { $syncHash.dgusers.AddChild($_) }
With this two lines in the code. The GUI won't show up.
The full code:
Add-Type -AssemblyName PresentationFramework
$syncHash = [hashtable]::Synchronized(#{})
$Runspace =[runspacefactory]::CreateRunspace()
$Runspace.ApartmentState = "STA"
$Runspace.ThreadOptions = "ReuseThread"
$Runspace.Open()
$Runspace.SessionStateProxy.SetVariable("syncHash", $syncHash)
$code = {
#Build the GUI
[xml]$xaml = #"
<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"
Title="UserList" Height="368" Width="525" WindowStyle="ToolWindow" Topmost="True" ResizeMode="NoResize">
<Grid>
<TabControl Name="tabControl" HorizontalAlignment="Left" Height="308" Margin="18,10,0,0" VerticalAlignment="Top" Width="481">
<TabItem Header="Lieferanten">
<Grid Background="#ffffff">
<DataGrid Name="dgusers" AutoGenerateColumns="False" Margin="19,27,158,20">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding ID}" Width="40" />
<DataGridTextColumn Header="USER" Binding="{Binding USER}" Width="233"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>
"#
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$syncHash.Window=[Windows.Markup.XamlReader]::Load($reader)
$syncHash.Error = $Error
# XAML objects
$syncHash.dgusers = $syncHash.window.FindName("dgusers")
$csv = Import-Csv "C:\name.csv" -Delimiter ";" -Encoding UTF8 |
Select-Object #{Name='USER';Expression={$_.USER}},`#{Name='ID';Expression={$_.ID}}
$csv | % { $syncHash.dgusers.AddChild($_) }
$syncHash.Window.ShowDialog()
$Runspace.Close()
$Runspace.Dispose()
}
$PSinstance1 = [powershell]::Create().AddScript($Code)
$PSinstance1.Runspace = $Runspace
$job = $PSinstance1.BeginInvoke()

How to prevent WPF Datagrid reverting to defaultview on header click

I really hope someone can point me in the right direction. I am fairly new to PowerShell/WPF but I am reading/watching as much as I can.
I have a sample bit of code in the link at the bottom, The textbox acts as a filter, typing in svchost and clicking the filter button just does a get-process, filters it via the filter and outputs it to the datagrid. The problem is if I click the header of the datagrid the filtered view is replaced and all processes are listed.
I have been looking at this for ages and think its related to the datagrid changing to the source view of CollectionViewSource rather than the current view but not sure the correct way to resolve it.
$xaml = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width ="400"
SizeToContent="Height"
Title="Example"
Topmost="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="400"/>
</Grid.RowDefinitions>
<TextBox x:Name="txtbx_Filter" Grid.Column="1" Height="20 " VerticalContentAlignment="Center" VerticalAlignment="Bottom" />
<DataGrid
x:Name="dataGrid"
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="2"
IsReadOnly="True"
SelectionMode="Single"
BorderThickness="0"
AlternatingRowBackground="#FFEFFBFB"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Process Name"
Binding="{Binding Name}"
Width="2*"
CanUserResize="False"
/>
</DataGrid.Columns>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
<Button x:Name="Btn_Filter" Content="Filter" Height="20" VerticalAlignment="Bottom"/>
</Grid>
</Window>
'#
#endregion
 
#region Code Behind
function Convert-XAMLtoWindow
{
param
(
[Parameter(Mandatory)]
[string]
$XAML,
[string[]]
$NamedElement=$null,
[switch]
$PassThru
)
Add-Type -AssemblyName PresentationFramework
$reader = [XML.XMLReader]::Create([IO.StringReader]$XAML)
$result = [Windows.Markup.XAMLReader]::Load($reader)
foreach($Name in $NamedElement)
{
$result | Add-Member NoteProperty -Name $Name -Value $result.FindName($Name) -Force
}
if ($PassThru)
{
$result
}
else
{
$null = $window.Dispatcher.InvokeAsync{
$result = $window.ShowDialog()
Set-Variable -Name result -Value $result -Scope 1
}.Wait()
$result
}
}
function Show-WPFWindow
{
param
(
[Parameter(Mandatory)]
[Windows.Window]
$Window
)
$result = $null
$null = $window.Dispatcher.InvokeAsync{
$result = $window.ShowDialog()
Set-Variable -Name result -Value $result -Scope 1
}.Wait()
$result
}
#endregion Code Behind
 
#region Convert XAML to Window
$window = Convert-XAMLtoWindow -XAML $xaml -NamedElement 'Btn_Filter', 'dataGrid', 'txtbx_Filter' -PassThru
$dataGrid = $window.FindName('dataGrid')
$window.Btn_Filter.add_Click{
$myfilter = $window.txtbx_Filter.Text
$process = Get-Process
$a = New-Object -TypeName System.Collections.ObjectModel.ObservableCollection[object]
$process | ForEach-Object -Process {
$a.Add((
New-Object -TypeName PSObject -Property #{
Name = $_.Name
}
))
}
$view = [System.Windows.Data.CollectionViewSource]::GetDefaultView($a)
$filter = "$myfilter"
$view.Filter = {
param ($item) $item -match $filter
}
$view.Refresh()
$dataGrid.ItemsSource = $view
}
# Show Window
$result = Show-WPFWindow -Window $window
I can't believe it, it turned out to be something so simple.
When I press the header button I could see the code looping through lines 128,129,130 and the variable for $filter was suddenly empty which was resetting the filter.
To fix the issue I just had to have:
$view.Filter = {
param ($item) $item -match $window.txtbx_Filter.Text
}
I love ISESteroids!

Resources