I'm having trouble with having a WPF-window do continues update from PowerShell, and also not freeze.
The idea is that when I click the button, some function is to be run that updates the value for the progressbar. But which ever method or guide I use, I can't get it to work. The progressbar is being updated and is filled to 100% when the task is done. But during the operation, the window freezes. The only way I can have a window that is not freezing, is if I do a simple value addition in the updateblock. But I need to be able to run different functions and update the progressbar again and again.
What do I need to do to run a function or scriptblock from the Click-event so the window doesn't freeze and the progressbar updates?
PowerShell-code:
Add-Type -Assembly PresentationFramework
$syncHash = [hashtable]::Synchronized( #{} )
$syncHash.Counter = 0.0
$syncHash.Do = {
param ( $syncHash, $ToDo )
$rsR = [runspacefactory]::CreateRunspace()
$rsR.ApartmentState = "STA"
$rsR.ThreadOptions = "ReuseThread"
$rsR.Open()
$rsR.SessionStateProxy.SetVariable( "syncHash", $syncHash )
$c = [powershell]::Create().AddScript( $ToDo )
$c.Runspace = $rsR
$h = $c.BeginInvoke()
do { start-sleep -millisecond 10 } until ( $h.IsCompleted -eq $true )
$c.EndInvoke( $h )
$rsR.Close()
$rsR.Dispose()
}
$syncHash.Window = [Windows.Markup.XamlReader]::Load( ( New-Object System.Xml.XmlNodeReader ( [xml]( Get-Content .\tt.xml ) ) ) )
$syncHash.Button = $syncHash.Window.FindName( "Button" )
$syncHash.Progress = $syncHash.Window.FindName( "Progress" )
$Timer = New-Object System.Windows.Threading.DispatcherTimer
$Task = { 1..100 | % { start-sleep -millisecond 10; $syncHash.Counter = $_ } }
$syncHash.Button.Add_Click( { Something } )
$syncHash.Window.Add_SourceInitialized( {
$Timer.Start()
$Timer.Add_Tick( $UpdateBlock )
$Timer.Interval = [TimeSpan]"0:0:0.01"
} )
function Something
{
& $syncHash.Do $syncHash $Task
}
$UpdateBlock =`
{
$syncHash.Window.Resources["Progress"] = [double]$syncHash.Counter
$syncHash.Window.Resources["PbText"] = "$( $syncHash.Counter ) %"
}
[void]$syncHash.Window.ShowDialog()
XAML-code:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Topmost="True"
WindowStartupLocation="CenterScreen"
SizeToContent="WidthAndHeight">
<Window.Resources>
<system:Double x:Key="Progress">0.0</system:Double>
<system:String x:Key="PbText"></system:String>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Click" Name="Button"/>
<Grid Grid.Row="1">
<ProgressBar Name="Progress" Minimum="0" Value="{DynamicResource Progress}" Width="230"/>
<TextBlock Text="{DynamicResource PbText}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
</Window>
I finally found what to do. Hade to rethink some stuff, and removed the runspace functionality all together.
Thanks to binding, it all got simpler.
Here is what I came to, also added binding for maxvalue of the progressbar:
Add-Type -Assembly PresentationFramework
$syncHash = #{}
$syncHash.Window = [Windows.Markup.XamlReader]::Load( ( New-Object System.Xml.XmlNodeReader ( [xml]( Get-Content .\tt.xml ) ) ) )
$syncHash.Button = $syncHash.Window.FindName( "Button" )
$syncHash.Progress = $syncHash.Window.FindName( "Progress" )
$Something = { param( $d )
function SomethingElse { gci }
$d[1] = ( SomethingElse ).Count
[system.windows.messagebox]::show($d[1])
foreach ( $i in ( 1..$d[1] ) ) { Start-Sleep -MilliSeconds 300; $d[0] = [double]( ( $i/$d[1] ) * 100 ) }
}
$syncHash.Button.Add_Click( { ( [powershell]::Create().AddScript( $Something ).AddArgument( $DataContext ) ).BeginInvoke() } )
$DataContext = New-Object System.Collections.ObjectModel.ObservableCollection[Object]
$DataContext.Add( 0.0 )
$DataContext.Add( 0.0 )
$syncHash.Progress.DataContext = $DataContext
$Binding = New-Object System.Windows.Data.Binding -ArgumentList "[0]"
$Binding.Mode = [System.Windows.Data.BindingMode]::OneWay
[void][System.Windows.Data.BindingOperations]::SetBinding( $syncHash.Progress, [System.Windows.Controls.ProgressBar]::ValueProperty, $Binding )
$Binding = New-Object System.Windows.Data.Binding -ArgumentList "[1]"
$Binding.Mode = [System.Windows.Data.BindingMode]::OneWay
[void][System.Windows.Data.BindingOperations]::SetBinding( $syncHash.Progress, [System.Windows.Controls.ProgressBar]::MaximumProperty, $Binding )
[void]$syncHash.Window.ShowDialog()
Related
In a WPF DataGrid via PowerShell, I have added a context menu and MenuItems from an Array. I would like to get the header value of clicked MenuItem to handle the click event further. I had tried add_Click event to the MenuItem object but it does return any value. I'm looking for some ideas to get the Header value in this scenario (or) if there is any different better approach to achieve this goal. Thanks in advance.
[xml]$inputXML=#"
<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"
xmlns:local="clr-namespace:Test"
Title="MainWindow" Height="275" Width="375">
<Window.InputBindings>
<KeyBinding x:Name="keyTest" Modifiers="Control" Key="N" Command="{Binding CreateCustomerCommand}" />
</Window.InputBindings>
<Grid>
<DataGrid x:Name="dg" Margin="5,5,0,0" Height="250" Width="350" ColumnWidth="Auto" AlternationCount="1" IsReadOnly="True" SelectionMode="Extended" SelectionUnit="Cell" Background="White" ClipboardCopyMode="IncludeHeader" >
<DataGrid.ContextMenu >
<ContextMenu x:Name="cxmenu" />
</DataGrid.ContextMenu>
</DataGrid>
</Grid>
</Window>
"#
[xml]$XAML = $inputXML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load($reader)
$xaml.SelectNodes("//*[#*[contains(translate(name(.),'n','N'),'x:Name')]]") | %{Set-Variable -Name "$($_.Name)" -Value $Window.FindName($_.Name)}
#sample data
$DataSet = New-Object System.Data.DataSet
$Table = $DataSet.Tables.Add("Table")
$Properties = #("Country","Capital","Population")
$Properties | foreach {
$Column = New-Object System.Data.DataColumn($_)
$Table.Columns.Add($Column)
}
$Null=$Table.Rows.Add("USA","Washington, D.C","658,893")
$Null=$Table.Rows.Add("China PR","Beijing","20,693,000")
$Null=$Table.Rows.Add("India","New Delhi","16,787,949")
$Null=$Table.Rows.Add("Japan","Tokyo","13,189,000")
$Null=$Table.Rows.Add("Philippines","Manila","12,877,253")
$Null=$Table.Rows.Add("Russia","Moscow","11,541,000")
$Null=$Table.Rows.Add("Egypt","Cairo","10,230,350")
#populate datagrid
$DataView = New-Object System.Data.DataView($Table)
$array = New-Object System.Collections.ArrayList
[void] $array.AddRange($DataView)
$dg.clear()
$dg.ItemsSource = $array
$dg.IsReadOnly = $true
#add MenuItem to context menu
$cxMenuitem = New-Object Windows.Controls.MenuItem
$header = ('Test1','Test2','Test3')
for ($i = 0; $i -le $header.Count -1; $i++)
{
$cxMenuitem.Header = [string]$header[$i]
$cxMenu.Items.Add($cxMenuitem.Header)
}
$cxMenuitem.Add_Click({
#Get the Header of the clicked MenuItem from Context Manu here for further handling
[System.Windows.MessageBox]::Show($cxMenu.Items)
})
#Display Form
$Window.ShowDialog() | Out-Null
You should be able to cast $args[0] to a MenuItem and access its Header property directly:
$cxMenuitem.Add_Click({
$sender = [System.Windows.Controls.MenuItem]$args[0]
# Use $sender.Header ...
})
I create a window from PowerShell that contains a ListView. The ListView gets its items from ItemsSource with binding. The list gets filled without problem, but when all is done, the listviewitems are not shown. They are only shown when I resize the window.
Same goes if I run again, any new items are only shown after I resize the window.
XAML:
<ListView Grid.Column="0" Name="Lv1">
<ListView.View>
<GridView>
<GridViewColumn Header="N" DisplayMemberBinding="{Binding Name}" Width="200"/>
<GridViewColumn Header="I" DisplayMemberBinding="{Binding LastWriteTime}" Width="150"/>
</GridView>
</ListView.View>
</ListView>
PowerShell, I have tried with PSCustomObject, PSObject, hashtable, but same result:
$Something = { param( $syncHash )
# Gather stuff in $t
$syncHash.DataContext[4].Add( [pscustomobject]#{ Name = $t.Name; LastWriteTime = $t.LastWriteTime } )
}
$syncHash = [hashtable]::Synchronized(#{})
$syncHash.Window = [Windows.Markup.XamlReader]::Load( ( New-Object System.Xml.XmlNodeReader ( [xml]( Get-Content .\tt.xml ) ) ) )
$syncHash = [hashtable]::Synchronized(#{})
$Bindings = New-Object System.Collections.ArrayList
$Lv1Source = New-Object System.Collections.ObjectModel.ObservableCollection[Object]
$Lv2Source = New-Object System.Collections.ObjectModel.ObservableCollection[Object]
0.0, 0.0, "", 0.0, $Lv1Source, $Lv2Source | foreach { $syncHash.DataContext.Add( $_ ) }
$syncHash.Button.Add_Click( {
( [powershell]::Create().AddScript( $Something ).AddArgument( $syncHash ) ).BeginInvoke()
} )
$syncHash.Window.DataContext = $syncHash.DataContext
0..( $syncHash.DataContext.Count - 1 ) | foreach { [void]$Bindings.Add( ( New-Object System.Windows.Data.Binding -ArgumentList "[$_]" ) ) }
$Bindings | foreach { $_.Mode = [System.Windows.Data.BindingMode]::OneWay }
[void][System.Windows.Data.BindingOperations]::SetBinding( $syncHash.Lv1, [System.Windows.Controls.ListView]::ItemsSourceProperty, $Bindings[4] )
[void][System.Windows.Data.BindingOperations]::SetBinding( $syncHash.Lv2, [System.Windows.Controls.ListView]::ItemsSourceProperty, $Bindings[5] )
$syncHash.DataContext[4].Add( [pscustomobject]#{Name=$t.Name;LastWriteTime=$t.LastWriteTime } )
I found a solution to the problem thanks to the last post here, "When calling between runspaces we need to use the 'Dispatcher'".
In conclusion, the DataContext is updated through the syncHash, but in the scriptblock for the button-click, I need to use:
$syncHash.Window.Dispatcher.Invoke( [action] {
$syncHash.Lv1.Items.Refresh()
$syncHash.DataContext[5].Add( [pscustomobject]#{ Name = $syncHash.h[$syncHash.ti] ; LastWriteTime = ( Get-Date ) } )
} )
I am creating a wpf ui in powershell using xaml. I am attempting to fill my combo box with an array and whenever I load the window/combobox, no options appear for me to select from. I have tried a few methods to resolve this, including using the ItemsSource element in xaml scriptblock, using DisplayMemberPath inside and outside the xaml block, creating a function to help display the window and then passing the variables from inside the window to the scriptblock.
Please let me know what I am doing wrong - any and all help is appreciated.
Do {
Connect-AzAccount -ErrorAction SilentlyContinue
$AzureAccount = Get-AzContext
$CurrentAccount = $AzureAccount.Account.Id
$CurrentAccount
} While ([String]$AzureAccount::isnullorempty)
Do {
# Select Subscription
Try {
$azureSubscription = (Get-AzSubscription | Sort-Object Name | Out-GridView -Title "Choose your Azure subscription and click OK." -PassThru)
Write-host "Switching to Azure subscription: $($azureSubscription.Name)" -ForegroundColor Green;
$azureSubscriptionInfo = Select-AzSubscription -SubscriptionId $azureSubscription.Id
} Catch {
Write-Output "Script Terminated"
}
# Select Location
Try {
$azureLocation = (Get-AzLocation | Sort-Object Name | Out-GridView -Title "Chosose your Azure location and click OK." -PassThru)
Write-Host "Switching to Azure location: $($azureLocation.DisplayName)" -ForegroundColor Green
} Catch {
Write-Output "Script Terminated"
}
if ([String]$azureSubscription::isnullorempty) {
$Primary = "False"
} elseif ([String]$azureLocation::isnullorempty) {
$Primary = "False"
}
} while ($Primary -eq "False")
# Get Virtual Networks and associated Subnets
$vnet = Get-AzVirtualNetwork
$vnetList = #()
$subnetsList = #()
foreach ($vn in $vnet) {
if ($vnet.Location -eq $azureLocation.Location) {
$vnetList += $vnet.Name
$subnetsList += $vnet.Subnets.Name }
}
$vnetList = $vnetList | Where-Object { $_ } | Select-Object -Unique
$subnetsList = $subnetsList | Where-Object { $_ } | Select-Object -Unique
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"
x:Name="Window" Title="Virtual Machine Deployment Tool" WindowStartupLocation="CenterScreen"
SizeToContent="WidthAndHeight" >
<Grid x:Name="Grid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="10">
<ComboBox x:Name="vnselect" Margin="5" Width="100" />
</StackPanel>
</Grid>
</Window>
"#
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
$window = [Windows.Markup.XamlReader]::Load($reader)
$window.ShowDialog() | Out-Null
$vnselect = $window.FindName("vnselect")
foreach ($item in $vnetList) {
$vnselect.Items.Add($item)
}
The code that added the items to the ComboBox should be placed before $window.Showdialog(). That's the resolution.
I have a ComboBox that doesn't quite work.
It has 3 static selections for essentially sorting a long list of connection profiles plus a 'Select One' which is automatically selected.
No matter what selection I choose, nothing happens until I click on another selection. Now this is the interesting part. No matter what I choose next, the listbox populates with my previous selection.
<ComboBox Name="CBox" Grid.Row="0" HorizontalAlignment="Left" VerticalAlignment="Top" >
<ComboBoxItem Name="cbSelect" IsSelected="True">Select Profile</ComboBoxItem>
<ComboBoxItem Name="cbRemote">Remote</ComboBoxItem>
<ComboBoxItem Name="cbLab">Lab</ComboBoxItem>
<ComboBoxItem Name="cbTomcats">Tomcats</ComboBoxItem>
</ComboBox>
<ListBox Name="ListBox" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" />
$syncHash.CBox.Add_SelectionChanged({
If($syncHash.CBox.Text -eq "Select Profile")
{
$syncHash.ListBox.Items.Clear()
}
If($syncHash.CBox.Text -eq "Remote")
{
$syncHash.ListBox.Items.Clear()
$profiles = New-Object System.Collections.ArrayList
$profiles = (Get-ChildItem "C:\Profiles")
foreach ($profile in $profiles)
{
if ($profile -match 'Remote \-')
{
[void]$syncHash.ListBox.Items.Add($profile.BaseName)
}
}
}
If($syncHash.CBox.Text -eq "Lab")
{
$syncHash.ListBox.Items.Clear()
$profiles = New-Object System.Collections.ArrayList
$profiles = (Get-ChildItem "C:\Profiles")
foreach ($profile in $profiles)
{
if ($profile -match 'Lab \-')
{
[void]$syncHash.ListBox.Items.Add($profile.BaseName)
}
}
}
If($syncHash.CBox.SelectedItem -eq "Tomcats")
{
$syncHash.ListBox.Items.Clear()
$profiles = New-Object System.Collections.ArrayList
$profiles = (Get-ChildItem "C:\Profiles")
foreach ($profile in $profiles)
{
if ($profile -match 'Tomcats \-')
{
[void]$syncHash.ListBox.Items.Add($profile.BaseName)
}
}
}
})
Final Solution using Rohin Sidharth's answer:
Add-Type -Path 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.dll'
$Global:syncHash = [hashtable]::Synchronized(#{})
[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="ComboBox Test" Height="500" Width="350">
<Grid Width="340" Height="470">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions >
<RowDefinition Height="0.1*" />
<RowDefinition Height="0.9*" />
</Grid.RowDefinitions>
<ComboBox Name="CBox" Grid.Row="0" HorizontalAlignment="Left" VerticalAlignment="Top" >
<ComboBoxItem Name="cbSelect" IsSelected="True">Select Profile</ComboBoxItem>
<ComboBoxItem Name="cbRemote">Remote</ComboBoxItem>
<ComboBoxItem Name="cbLab">Lab</ComboBoxItem>
<ComboBoxItem Name="cbTomcats">Tomcats</ComboBoxItem>
</ComboBox>
<ListBox Name="ListBox" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Grid>
</Window>
"#
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$syncHash.Window=[Windows.Markup.XamlReader]::Load( $reader )
[xml]$XAML = $xaml
$xaml.SelectNodes("//*[#*[contains(translate(name(.),'n','N'),'Name')]]") | %{
$syncHash.Add($_.Name,$syncHash.Window.FindName($_.Name) )
}
function Get-Profiles
{
$profiles = (Get-ChildItem "C:\Profiles")
$syncHash.ListBox.Items.Clear()
If ($syncHash.CBox.SelectedIndex -eq 1)
{
foreach ($profile in $profiles)
{
if ($profile -match 'Remote \-')
{
$syncHash.ListBox.Items.Add($profile.BaseName)
}
}
}
ElseIf ($syncHash.CBox.SelectedIndex -eq 2)
{
foreach ($profile in $profiles)
{
if ($profile -match 'Lab \-')
{
$syncHash.ListBox.Items.Add($profile.BaseName)
}
}
}
ElseIf ($syncHash.CBox.SelectedIndex -eq 3)
{
foreach ($profile in $profiles)
{
if ($profile -match 'Tomcats \-')
{
$syncHash.ListBox.Items.Add($profile.BaseName)
}
}
}
}
$syncHash.CBox.Add_SelectionChanged({Get-Profiles})
$null = $syncHash.Window.ShowDialog()
$syncHash.Error = $Error
$Error
I haven't quite worked with WPF in powershell but in Winforms, I think to select an item on the combobox, you have to set the SelectedIndex property and not the text of the item. An index of 0 will select the 1st item on your dropdownlist
ipmo WPK
$ConnectionString = $ConnectionString = "Server=localhost;Integrated Security=True"
$conn = new-object System.Data.SQLClient.SQLConnection
$conn.ConnectionString = $ConnectionString
$conn.Open()
function Invoke-sql1
{
param( [string]$sql,
[System.Data.SQLClient.SQLConnection]$connection
)
$cmd = new-object System.Data.SQLClient.SQLCommand($sql,$connection)
$ds = New-Object system.Data.DataSet
$da = New-Object System.Data.SQLClient.SQLDataAdapter($cmd)
$da.fill($ds) | Out-Null
return $ds.tables[0]
}
function Show-Bockmarks ($conn) {
New-ListView -Name ListView -View {
New-GridView -AllowsColumnReorder -Columns {
New-GridViewColumn "title"
}
} -show -On_Loaded {
$ff_sql = #"
SELECT 'abc' title
union
SELECT 'xyz' title
union
SELECT 'efg' title
"#
$TableView = $window | Get-ChildControl ListView
$TableView.ItemsSource = #(Invoke-sql1 -sql $ff_sql -connection $conn)
}
}
Show-Bockmarks $conn
Edit:
I transformed the code to XAML
ipmo WPK
$ConnectionString = $ConnectionString = "Server=localhost;Integrated Security=True"
$conn = new-object System.Data.SQLClient.SQLConnection
$conn.ConnectionString = $ConnectionString
$conn.Open()
function Invoke-sql1
{
param( [string]$sql,
[System.Data.SQLClient.SQLConnection]$connection
)
$cmd = new-object System.Data.SQLClient.SQLCommand($sql,$connection)
$ds = New-Object system.Data.DataSet
$da = New-Object System.Data.SQLClient.SQLDataAdapter($cmd)
$da.fill($ds) | Out-Null
return $ds.tables[0]
}
function Show-Bockmarks ($conn) {
[xml] $xaml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" >
<ListView ItemsSource="{Binding Persons}"
IsSynchronizedWithCurrentItem="True"
Name="Listview">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="title"
DisplayMemberBinding="{Binding title}"
/>
<GridViewColumn Header="itemid"
DisplayMemberBinding="{Binding itemid}"
/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</Window>
"#
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Form=[Windows.Markup.XamlReader]::Load( $reader )
$ff_sql = #"
SELECT 'abc' title, 3 itemid
union
SELECT 'xyz' title, 2 itemid
union
SELECT 'efg' title, 1 itemid
"#
$TableView = $Form.FindName("Listview")
$TableView.ItemsSource = #(Invoke-sql1 -sql $ff_sql -connection $conn)
$Form.ShowDialog() #| out-null
}
Show-Bockmarks $conn
But when I added the lines proposed by Thomas Levesque
ipmo WPK
$ConnectionString = $ConnectionString = "Server=localhost;Integrated Security=True"
$conn = new-object System.Data.SQLClient.SQLConnection
$conn.ConnectionString = $ConnectionString
$conn.Open()
function Invoke-sql1
{
param( [string]$sql,
[System.Data.SQLClient.SQLConnection]$connection
)
$cmd = new-object System.Data.SQLClient.SQLCommand($sql,$connection)
$ds = New-Object system.Data.DataSet
$da = New-Object System.Data.SQLClient.SQLDataAdapter($cmd)
$da.fill($ds) | Out-Null
return $ds.tables[0]
}
function Show-Bockmarks ($conn) {
[xml] $xaml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:util="clr-namespace:TheNameSpace;assembly=TheAssembly"
Title="MainWindow" >
<ListView ItemsSource="{Binding Persons}"
IsSynchronizedWithCurrentItem="True"
util:GridViewSort.AutoSort="True"
Name="Listview">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="title"
DisplayMemberBinding="{Binding title}"
util:GridViewSort.PropertyName="title"
/>
<GridViewColumn Header="itemid"
DisplayMemberBinding="{Binding itemid}"
util:GridViewSort.PropertyName="itemid"
/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</Window>
"#
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Form=[Windows.Markup.XamlReader]::Load( $reader )
$ff_sql = #"
SELECT 'abc' title, 3 itemid
union
SELECT 'xyz' title, 2 itemid
union
SELECT 'efg' title, 1 itemid
"#
$TableView = $Form.FindName("Listview")
$TableView.ItemsSource = #(Invoke-sql1 -sql $ff_sql -connection $conn)
$Form.ShowDialog() #| out-null
}
Show-Bockmarks $conn
I get the error
Exception calling "Load" with "1" argument(s): "The property 'GridViewSort.AutoSort' does not exist in XML namespace 'clr-namespace:TheNameSpace;assembly=TheAssembly'. Line '0'
Position '0'."
I guess I have to register some assembly.
See this blog post (and this one) for a XAML solution
You can also use this solution in code using the GridViewSort.SetAutoSort and GridViewSort.SetPropertyName method. I don't know the Powershell syntax, but here it is in C#:
GridViewSort.SetAutoSort(TableView, true);
GridViewSort.SetPropertyName(TitleColumn, "title");