Skip to content

Instantly share code, notes, and snippets.

@kewalaka
Last active January 29, 2022 04:38
Show Gist options
  • Save kewalaka/08bb9d0ee35ecbf0454827008ecafd11 to your computer and use it in GitHub Desktop.
Save kewalaka/08bb9d0ee35ecbf0454827008ecafd11 to your computer and use it in GitHub Desktop.
WPF Datagrid with powershell using class to implement INotifyPropertyChanged - still not working
Using Namespace System.ComponentModel
Class ViewModel : INotifyPropertyChanged {
Hidden [PropertyChangedEventHandler] $PropertyChanged
[String] $ServerInstance
[String] $targetAccount
[Boolean] $isEverythingDone
[String] $Status
[Void] add_PropertyChanged([PropertyChangedEventHandler] $propertyChanged) {
$this.PropertyChanged = [Delegate]::Combine($this.PropertyChanged, $propertyChanged)
}
[Void] remove_PropertyChanged([PropertyChangedEventHandler] $propertyChanged) {
$this.PropertyChanged = [Delegate]::Remove($this.PropertyChanged, $propertyChanged)
}
Hidden [Void] NotifyPropertyChanged([String] $propertyName) {
If ($this.PropertyChanged -cne $null) {
$this.PropertyChanged.Invoke($this, (New-Object PropertyChangedEventArgs $propertyName))
}
}
}
[ViewModel] $viewModel = New-Object ViewModel -Property @{
ServerInstance = 'MyServer'
targetAccount = 'svcAccount'
isEverythingDone = $false
Status = ''
}
#-------------------------------------------------------------#
#----Initial Declarations-------------------------------------#
#-------------------------------------------------------------#
Add-Type -AssemblyName PresentationCore, PresentationFramework
$Xaml = @"
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Width="800" Height="300" Name="khsdeflpnonyb">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="200"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Button Content="Update" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Margin="6" Grid.Row="0" Grid.Column="0" Name="updateButton"/>
<Button Content="Update Async" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Margin="6" Grid.Row="0" Grid.Column="0" Name="updateButtonASync"/>
<Button Content="Refresh" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Margin="6" Grid.Row="0" Grid.Column="0" Name="refreshButton"/>
</StackPanel>
<DataGrid Grid.Row="1" Name="observableDataGrid" IsReadOnly="True" Margin="6" ItemsSource="{Binding serverList}" SelectedItem="{Binding selectedServer}">
</DataGrid>
</Grid>
</Window>
"@
#-------------------------------------------------------------#
#----Control Event Handlers-----------------------------------#
#-------------------------------------------------------------#
#Write your code here
function UpdateButton
{
$State.selectedServer.Status = (Get-Random | Out-String)
#$observableDataGrid.Items.Refresh()
}
function UpdateButtonAsync
{
Async {
$State.selectedServer.Status = (Get-Random | Out-String)
}
}
function Refresh
{
$observableDataGrid.Items.Refresh()
}
function OnLoad
{
$results = New-Object 'System.Collections.ObjectModel.ObservableCollection[ViewModel]'
$results.Add($viewModel)
$observableDataGrid.itemsSource = $results
}
#endregion
#-------------------------------------------------------------#
#----Script Execution-----------------------------------------#
#-------------------------------------------------------------#
$Window = [Windows.Markup.XamlReader]::Parse($Xaml)
[xml]$xml = $Xaml
$xml.SelectNodes("//*[@Name]") | ForEach-Object { Set-Variable -Name $_.Name -Value $Window.FindName($_.Name) }
$khsdeflpnonyb.Add_Loaded({OnLoad $this $_})
$updateButton.Add_Click({UpdateButton $this $_})
$updateButtonASync.Add_Click({UpdateButtonASync $this $_})
$refreshButton.Add_Click({Refresh $this $_})
$State = [PSCustomObject]@{}
Function Set-Binding {
Param($Target,$Property,$Index,$Name)
$Binding = New-Object System.Windows.Data.Binding
$Binding.Path = "["+$Index+"]"
$Binding.Mode = [System.Windows.Data.BindingMode]::TwoWay
[void]$Target.SetBinding($Property,$Binding)
}
function FillDataContext($props){
For ($i=0; $i -lt $props.Length; $i++) {
$prop = $props[$i]
$DataContext.Add($DataObject."$prop")
$getter = [scriptblock]::Create("return `$DataContext['$i']")
$setter = [scriptblock]::Create("param(`$val) return `$DataContext['$i']=`$val")
$State | Add-Member -Name $prop -MemberType ScriptProperty -Value $getter -SecondValue $setter
}
}
$DataObject = ConvertFrom-Json @"
{
"serverList" : [{
"ServerInstance" : "",
"targetAccount" : "",
"isEverythingDone" : "",
"Status": ""
}],
"selectedServer":""
}
"@
$DataContext = New-Object System.Collections.ObjectModel.ObservableCollection[Object]
FillDataContext @("serverList","selectedServer")
$Window.DataContext = $DataContext
Set-Binding -Target $observableDataGrid -Property $([System.Windows.Controls.DataGrid]::ItemsSourceProperty) -Index 0 -Name "serverList"
Set-Binding -Target $observableDataGrid -Property $([System.Windows.Controls.DataGrid]::SelectedItemProperty) -Index 1 -Name "selectedServer"
$Global:SyncHash = [HashTable]::Synchronized(@{})
$Jobs = [System.Collections.ArrayList]::Synchronized([System.Collections.ArrayList]::new())
$initialSessionState = [initialsessionstate]::CreateDefault()
Function Start-RunspaceTask
{
[CmdletBinding()]
Param([Parameter(Mandatory=$True,Position=0)][ScriptBlock]$ScriptBlock,
[Parameter(Mandatory=$True,Position=1)][PSObject[]]$ProxyVars)
$Runspace = [RunspaceFactory]::CreateRunspace($InitialSessionState)
$Runspace.ApartmentState = 'STA'
$Runspace.ThreadOptions = 'ReuseThread'
$Runspace.Open()
ForEach($Var in $ProxyVars){$Runspace.SessionStateProxy.SetVariable($Var.Name, $Var.Variable)}
$Thread = [PowerShell]::Create('NewRunspace')
$Thread.AddScript($ScriptBlock) | Out-Null
$Thread.Runspace = $Runspace
[Void]$Jobs.Add([PSObject]@{ PowerShell = $Thread ; Runspace = $Thread.BeginInvoke() })
}
$JobCleanupScript = {
Do
{
ForEach($Job in $Jobs)
{
If($Job.Runspace.IsCompleted)
{
[Void]$Job.Powershell.EndInvoke($Job.Runspace)
$Job.PowerShell.Runspace.Close()
$Job.PowerShell.Runspace.Dispose()
$Runspace.Powershell.Dispose()
$Jobs.Remove($Runspace)
}
}
Start-Sleep -Seconds 1
}
While ($SyncHash.CleanupJobs)
}
Get-ChildItem Function: | Where-Object {$_.name -notlike "*:*"} | select name -ExpandProperty name |
ForEach-Object {
$Definition = Get-Content "function:$_" -ErrorAction Stop
$SessionStateFunction = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList "$_", $Definition
$InitialSessionState.Commands.Add($SessionStateFunction)
}
$Window.Add_Closed({
Write-Verbose 'Halt runspace cleanup job processing'
$SyncHash.CleanupJobs = $False
})
$SyncHash.CleanupJobs = $True
function Async($scriptBlock){ Start-RunspaceTask $scriptBlock @([PSObject]@{ Name='DataContext' ; Variable=$DataContext},[PSObject]@{Name="State"; Variable=$State})}
Start-RunspaceTask $JobCleanupScript @([PSObject]@{ Name='Jobs' ; Variable=$Jobs })
$Window.ShowDialog()
@jfromh
Copy link

jfromh commented Jan 29, 2022

$Class = @'
using System.ComponentModel;

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _ServerInstance;
    public string ServerInstance
    {
        get { return _ServerInstance; }
        set
        {
            _ServerInstance = value;

            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("ServerInstance"));
        }
    }

    private string _targetAccount;
    public string targetAccount
    {
        get { return _targetAccount; }
        set
        {
            _targetAccount = value;

            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("targetAccount"));
        }
    }

    private bool _isEverythingDone;
    public bool isEverythingDone
    {
        get { return _isEverythingDone; }
        set
        {
            _isEverythingDone = value;

            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("isEverythingDone"));
        }
    }

    private string _Status;
    public string Status
    {
        get { return _Status; }
        set
        {
            _Status = value;

            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("Status"));
        }
    }
}
'@

Add-Type -TypeDefinition $Class -Language 'CSharp'
[ViewModel]::New()

Try importing the class this way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment