Change content of 2 textboxes based on listbox selection - wpf

I am creating a small tool which is split into 2 files:
Form.ps1 - GUI & Button Events
Main.ps1 - All functions
The form itself look likes the following:
When a user is entered and Find User is clicked, if there is only 1 result, then the username and approver are filled (approver is the users line manager) as follows:
However, if more than 1 result is returned, it will then create a listbox of the results.
The part I am struggling with is how to add the approver as well as the username using $listbox.SelectedItem
At the moment I can only get the username like the following example:
I am using the following AD Query to get the Name, Email, Manager and ManagersEmail:
$searchResult = Get-ADUser -Filter "givenName -like ""$Firstname"" -and sn -Like ""$Surname""" -Properties Name, Mail, Manager |
Where { $_.Enabled -eq $True} |
Select-Object Name, Mail, #{Name="ManagersEmail";Expression={(get-aduser -property mail $_.manager).mail}}
The variables $firstname and $surname are taken from the textbox using a split

You can use WPF's element binding feature
I have filled the ListBox1 with Items of string type, therfore it is binds directly no need to get the object's property.
Below is the sample XAML & Code.
XAML:-
<TextBox Text="{Binding ElementName=listbox1, Path=SelectedItem}"
HorizontalAlignment="Left" Height="24" Margin="164,100,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<ListBox x:Name="listbox1"
HorizontalAlignment="Left"
Height="100"
Margin="164,156,0,0"
VerticalAlignment="Top"
Width="100"/>
C#:- Code behind file.
listbox1.Items.Add("ABC");
listbox1.Items.Add("XyZ");
listbox1.Items.Add("123");
You can skip this C# part because you might already fill it by clicking on the Find User button.

Related

Problem with style triggers / datatriggers in powershell WPF script

I have a powershell script that displays as a WPF form to allow users to control printers etc (map / unmap / set default), everything on it is working nicely, bit I have an issue with trying to hide a printer (from a work around where understandable listview hate having just 1 item bound to them)
So if the user has 1 mapped printer I also add in the "Send to OneNote 16" printer into the object that is bound to the list view, but to avoid confusion I'd ideally like to hide that printer from the list.
XAML Code section
<ListBox x:Name="MyListbox" HorizontalContentAlignment="Stretch">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ListBoxItem.Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Name}" Value="Send to OneNote 16">
<Setter Property="ListBoxItem.Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border CornerRadius="6" BorderBrush="LightGray" Background="White" BorderThickness="1" Padding="8">
<StackPanel>
<DockPanel>
<TextBlock Text="Name: " FontWeight="Bold" Foreground="LimeGreen" />
<TextBlock>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" />
</TextBlock>
</DockPanel>
<DockPanel>
<TextBlock Text="Server: " />
<TextBlock Foreground="Black">
<TextBlock Text="$($PrintServerName)" />
</TextBlock>
<TextBlock Text=" | Job Count: " />
<TextBlock Foreground="Black">
<TextBlock Text="{Binding Path=JobCount}" />
</TextBlock>
</DockPanel>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Powershell bit that populates the object that is bound to MyListBox
function Get-MappedPrinterInfo {
BEGIN {
# Setup variables, and get mapped printer information from registry
[object]$Printers = Get-ChildItem Registry::\HKEY_CURRENT_USER\Printers\Connections
[object]$Output = New-Object System.Collections.ObjectModel.ObservableCollection[Object]
}
PROCESS {
foreach ($Printer in $Printers) {
# Extract printer name
[string]$PrinterName = $Printer.Name
# Correct Case
$PrinterName = $PrinterName.ToLower()
# Cleanup data
$PrinterName = $PrinterName.replace("printerserver.mynetwork.local", "")
$PrinterName = $PrinterName.replace("printerserver", "")
$PrinterName = $PrinterName.replace("hkey_current_user\printers\connections\", "")
$PrinterName = $PrinterName.replace(",", "")
# Get additional data on printer from its print server
$Output += Get-Printer -ComputerName $PrintServerName | Where-Object Shared -eq $true | Where-Object Name -eq $PrinterName
}
# Check object length, and pad out if required.
if ($Output.Count -eq 1) {
$Output += Get-Printer | Where-Object Name -eq "Send to OneNote 16"
}
}
END {
# Return data
return $Output
}
}
# Populate my mapped printer list.
$MyListbox.ItemsSource = Get-MappedPrinterInfo
The style section all works a treat, but the datatrigger never fires, if I set the Setter property (4th line in XAML section) then I can hide/ unhide all items, but Ideally I want to be able to hide by name.
Thanks in advance.
There is no need for your add-an-extra-item-then-hide-it workaround:
The root of your problem is that outputting a collection from a PowerShell function causes its enumeration by default, which in the case of a single-item collection means that that item itself is output, not an array of items ([object[]], which is how PowerShell implicitly collects multiple outputs from a command when you assign to an l-value).
#(), the array-subexpression operator, exists precisely to address this scenario:
You wrap it around a command whose output you always want to treat as an array, even if the command happens to output a single object:
# Populate my mapped printer list.
# #(...) ensures that the function output is an array.
$MyListbox.ItemsSource = #(Get-MappedPrinterInfo)
Note that it is possible to solve this problem from inside your function - by wrapping your output collection in an aux. single-item array, so that the original collection is received by the caller - but it's generally better not to write such functions, as they will exhibit nonstandard behavior when used in a pipeline, where other commands expect item-by-item input, not entire collections.
If you want to do it nonetheless (which is fine for private, special-purpose functions):
...
END {
# Return data wrapped in a single-item aux. array to ensure
# that the $Output collection is returned as a whole.
return , $Output
}
A few asides regarding your code:
[object]$Printers = ...
An [object] cast is pointless in PowerShell, and casts generally don't work the same way as in C#, for instance.
Whatever type the RHS has becomes the effective type of $Printers.
If you use a specific type to constrain the LHS, however, conversion to that type is attempted, whenever you assign a value to that variable.
$Output += ...
+= quietly converts $Output to a (new instance of an) array ([object[]]), irrespective of the original type, which isn't your intent and is also inefficient.
You need to call the .Add() method on the collection instance originally stored in $Output instead.

Link WPF XAML to DataGrid in PowerShell

I was following along with the guide located here trying to make a GUI with PowerShell, and it all goes well except I have a DataGrid that needs to be populated in my GUI.
In my XML grid I have:
[xml]$form=#"
<Window
[...]
<Grid>
[...]
<DataGrid Name="CSVGrid" HorizontalAlignment="Left" Height="340" Margin="10,60,0,0" VerticalAlignment="Top" Width="765"/>
[...]
</Grid>
</Window>
"#
Now in the tutorial to create the form it uses the following:
$XMLReader = (New-Object System.Xml.XmlNodeReader $Form)
$XMLForm = [Windows.Markup.XamlReader]::Load($XMLReader)
But to get my DataGrid to work, I believe I need to define my "CSVGrid" DataGrid as "system.Windows.Forms.DataGridView" somewhere, but I'm not sure how to tie that together. Running it without defining this will spit out errors if I try to invoke any DataGrid properties, like setting column amounts or column names.
Any ideas?
The way POSHGUI implements their forms actually works perfectly for my purpose, but I prefer editing the WPF forms in Visual Studio. If need be, I can rebuild the form in POSHGUI but hopefully there's a way to tie this together here so I can continue to use the VS GUI for editing the form's GUI.
Edit: It should be noted there's not just the data grid on the form, in case that wasn't clear.
Edit 2: As an extra bit of info, the way POSHGUI formats the controls is like so:
#the form itself
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = '400,400'
$Form.text = "Form"
$Form.TopMost = $false
#a datagrid
$DataGridView1 = New-Object system.Windows.Forms.DataGridView
$DataGridView1.width = 382
$DataGridView1.height = 335
$DataGridView1.location = New-Object System.Drawing.Point(8,55)
#a button
$Button1 = New-Object system.Windows.Forms.Button
$Button1.text = "My button"
$Button1.width = 126
$Button1.height = 30
$Button1.location = New-Object System.Drawing.Point(156,13)
$Button1.Font = 'Microsoft Sans Serif,10'
It then ties them together with:
$Form.controls.AddRange(#($DataGridView1,$Button1))
So that happily defines the DataGrid variable as a "system.Windows.Forms.DataGridView", &c, whereas the method of putting the entire XML into a $variable and passing that into a "System.Xml.XmlNodeReader" doesn't make the distinction I don't believe, which is why I'm unable to call any DataGrid properties.
But again, I'd rather create the GUI in Visual Studio if I can...
Edit 3: If it helps at all, checking the intellisense dropdown, various DataGrid properties are there, but not ColumnCount for example:
So maybe it's working as intended? But at the same time, if my DataGrid variable is defined explicitly as a system.Windows.Forms.DataGridView, like in the POSHGUI example, then setting ColumnCount works flawlessly...
Before we get too far into it, you can't use a DataGridView in WPF (which is what I'll be showing you here). You can, however, use a DataGrid instead which is pretty much the same thing (and wayyyy shorter and easier in PowerShell than Windows Forms, which is what POSHGUI uses (awesome tool though it is).
If you're still interested though, here's a super basic walkthrough of how you could do this. First, to define the XAML
$inputXML = #"
<Window x:Class="WpfApp2.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:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<DataGrid Name="Datagrid" AutoGenerateColumns="True" >
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="180" />
<DataGridTextColumn Header="Type" Binding="{Binding Type}" Width="233"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
"#
Note that we are defining which columns from our input object we wanna display. Haven't found a great way to automatically create them yet.
Next, load the form (using the method from my walkthrough on the topic here)
$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 <span class="wp-smiley wp-emoji wp-emoji-bigsmile" title=":D">:D</span>
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)}
Now, to add some items to this DataGridView. The above code also gives us a variable titled $WPFGridView in our session. We can add child items to our DataGridView by calling the .AddChild() method of that variable and feeding it items that have at least the same properties as we defined in our DataGrid earlier.
$WPFDatagrid.AddChild([pscustomobject]#{Name='Stephen';Type=123})
$WPFDatagrid.AddChild([pscustomobject]#{Name='Geralt';Type=234})
Then, to display our GUI, we call the $Form's ShowDialog() method like so, and then the bottom should greet us.
$Form.ShowDialog()
Full code sample is available here. If you want to learn more about using WPF GUI elements like this, I have a full walkthrough available here.
I modified it a bit:
is a function (allows for multiple windows)
returns an array of the elements
loads the presentation framework (supports x:Bind as Binding)
you can create any WPF form with visual studio and load it (mostly, do not forget any extra classes used to be defined within the XAML and maybe load its type in posh)
A more advanced version: supporting click handler
function LoadXamlForm
{
Param
(
[Parameter(Mandatory=$true)] $xamlFile
)
Add-Type -AssemblyName PresentationFramework
$inputXML = Get-Content -Path $xamlFile
# https://stackoverflow.com/a/52416973/1644202
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window' -replace "x:Bind", "Binding"
[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 <span class="wp-smiley wp-emoji wp-emoji-bigsmile" title=":D">:D</span>
Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."
}
#===========================================================================
# Store Form Objects In PowerShell
#===========================================================================
$Elements = #{}
$xaml.SelectNodes("//*[#Name]") | %{ $Elements[$_.Name] = $Form.FindName($_.Name) }
return $Form, $Elements
}
$Form,$Elements = LoadXamlForm .\gui.xaml
$Elements.lvApps.AddChild([pscustomobject]#{Name='Ben';Description="sdsd 343"})
$Form.ShowDialog() | out-null
just any xaml - i know this is no datagrid - but gives you a use case nonetheless
<Window x:Class="WpfApp.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:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ListView SelectionMode="Multiple" x:Name="lvApps" Height="371" VerticalAlignment="Top">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="64" AutomationProperties.Name="{Binding Name}">
<Ellipse Height="48" Width="48" VerticalAlignment="Center">
<Ellipse.Fill>
<ImageBrush ImageSource="{Binding ButtonImage}"/>
</Ellipse.Fill>
</Ellipse>
<StackPanel Orientation="Vertical" VerticalAlignment="Center" Margin="12,0,0,0">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Description}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>

base64 as textbox background?

I have a textbox that has an image linked as it's background.
I'd like to use the base64 version of this image.
I used a very simple windows -form- to ensure the code for enacting the base64 is working correctly, it did.
The part I'm having issue with is linking it in xaml in a wpf style gui.
<TextBox Name="OutTBox" Grid.Column="0" Grid.ColumnSpan="7" Grid.Row="4" Grid.RowSpan="6" Margin="20,5,20,20" Foreground="#FF8CFF8C" IsReadOnly="True" >
<TextBox.Background>
<ImageBrush ImageSource="C:\background.jpg" Stretch="UniformToFill"/>
</TextBox.Background>
</TextBox>
So here is the base64 language I used for the windows form:
Add-Type -AssemblyName PresentationFramework
# Setup the XAML
[xml]$script:xaml = '<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="base64 image demo" Height="350" Width="650" Background="Black">
<Grid>
<Image Name="thisistheimage" Stretch="UniformToFill" />
</Grid>
</Window>'
# Create the form and set variables
$script:form = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $xaml))
$xaml.SelectNodes("//*[#Name]") | ForEach-Object { Set-Variable -Name ($_.Name) -Value $form.FindName($_.Name) -Scope Script }
# here's the base64 string
$base64 = "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"
# Create a streaming image by streaming the base64 string to a bitmap streamsource
$bitmap = New-Object System.Windows.Media.Imaging.BitmapImage
$bitmap.BeginInit()
$bitmap.StreamSource = [System.IO.MemoryStream][System.Convert]::FromBase64String($base64)
$bitmap.EndInit()
# Freeze() prevents memory leaks.
$bitmap.Freeze()
# Set source here. Take note in the XAML as to where the variable name was taken.
$thisistheimage.source = $bitmap
# Show form
$null = $form.ShowDialog()
I'd give you my actual image but it's over 5k lines long. It only takes a fraction of a second to load.
To be clear the base64 will load in the Xaml. I removed the textbox from those grid coords and replaced it with the base64 and it loads fine. just can't seem to work the two together.
Thanks!
I've never used powershell, but it should be as simple as this:
$brush = New-Object System.Windows.Media.ImageBrush
$brush.ImageSource = $bitmap
$thetextbox.Background = $brush

Calling runas from Powershell with forms

I have a series of AD domains, each domain having it's own admin accounts.
I would like to be able to open ADUC from within PowerShell for each domain using the correct credentials.
Because of domain trust setup I can't use the -credential switch when starting a new PS instance so runas /netonly seems to be the best option.
Now this works fine in Powershell - until a GUI front end is added using WPF - the reason being I suspect that PS is trying to use the existing window to display the user/pass prompt but can't because it's tied to a window.
For some reason I can't get it to spawn a new window, even by using start-process. (The start process command below worked fine under PS2 but since upgrading to PS4 it now fails telling me that -STA and -Command aren't real parameters)
I'm using the xaml converter from http://blogs.technet.com/b/heyscriptingguy/archive/2014/08/01/i-39-ve-got-a-powershell-secret-adding-a-gui-to-scripts.aspx to load a xaml form:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UAT" Height="300" Width="300">
<Grid>
<Button Name="btnGo" Content="Go" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="207,240,0,0"/>
<TextBox Name="txtOut" HorizontalAlignment="Left" Height="233" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="272" Margin="10,0,0,0"/>
</Grid>
</Window>
The .PS1 file
$c = "admin-" + [Environment]::UserName
$server = "foo.bar"
.\loadDialog.ps1 -XamlPath '.\UAT.xaml'
$btnGo.add_Click({
#Start-Process PowerShell -STA -Command {runas /netonly /user:DOMAIN\$c "mmc.exe dsa.msc /server=$server"}
& runas /netonly /user:DOMAIN\$c "mmc.exe dsa.msc /server=$server"
})
$xamGUI.ShowDialog() | out-null 2>> Errors.txt
Is there another way to start this process in such a way that it will spawn it's own window?

How can I attach an event handler to a Button in a DataGridTemplateColumn defined in XAML, in PowerShell?

I have some XAML I'm importing into a PowerShell script. It contains a DataGrid which displays the result of a PowerShell cmdlet. I've added an 'Edit' button to each row using a DataGridTemplateColumn. Now, I'd like to add an event handler to each button. I can do it in code:
$adUsersList_Results = $activeDirectory_Users_ListControl.FindName('ResultsDataGrid')
$dataTemplate = New-Object 'Windows.DataTemplate'
$buttonElement = New-Object 'Windows.FrameworkElementFactory' ([Windows.Controls.Button])
$buttonElement.SetValue([Windows.Controls.Button]::ContentProperty, "Edit")
$buttonElement.AddHandler([Windows.Controls.Button]::ClickEvent, [Windows.RoutedEventHandler]{
PARAM (
[Object]$sender,
[Windows.RoutedEventArgs]$e
)
Write-Host "Clickety-click!"
Write-Host $sender.DataContext
})
$dataTemplate.VisualTree = $buttonElement
$dataGridTemplateColumn = New-Object 'Windows.Controls.DataGridTemplateColumn'
$dataGridTemplateColumn.Header = 'Poo'
$dataGridTemplateColumn.CellTemplate = $dataTemplate
$adUsersList_Results.Columns.Add($dataGridTemplateColumn)
but it's a bit unwieldy. The XAML looks like this:
<DataGrid Grid.Row="1"
x:Name="ResultsDataGrid"
IsReadOnly="True"
HorizontalAlignment="Stretch"
Margin="3"
VerticalAlignment="Stretch">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Edit" Width="60" x:Name="ADUserListButton"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I've tried using FindName('ADUserListButton') but it doesn't work. Is there a way to keep all of the visual stuff in XAML and just do the event handler in the PowerShell?
Aside: I'm aware of Show-UI. I'm actually doing the same thing in Show-UI and in PowerShell & XAML before deciding which is the best fit for us.
I'm also aware that you can do editing in-grid but I don't think it's a great user-experience for this type of data.
I'm not exactly sure but could it be because the XAML button you list doesn't have the handler connection to it? i.e.
<Button Content="Edit" Width="60" x:Name="ADUserListButton" Click="ADUserListButton_Click"/>
I have not done that with powershell but if it was in the .cs code-behind file of a usercontrol for instance you could try GetTemplateChild("your control name").
Below is an example of calling button inside data-grid using Powershell, hope this helps.
Add-Type -AssemblyName PresentationFramework
Function Check-Event($ev,$co,$da){
$comp=$co.trim()
$re = Get-EventLog Application -ComputerName "$comp" -InstanceId $ev -After $da
$res.Text += "There are "+$re.count+" events pf EventID $ev on Computer $Comp after the time $da ch"
}
[Xml]$Form = #"
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid HorizontalAlignment="Left" Height="151" Margin="112,35,0,0" VerticalAlignment="Top" Width="279"/>
<Button Name="ADUserListButton" Content="Edit" HorizontalAlignment="Left" Margin="140,85,0,0" VerticalAlignment="Top" Width="75"/>
<TextBox Name="Result" HorizontalAlignment="Left" Height="23" Margin="140,124,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" Text="Hello World"/>
</Grid>
</Window>
"#
$NR=(New-Object System.Xml.XmlNodeReader $Form)
$Win=[Windows.Markup.XamlReader]::Load( $NR )
$Start = $Win.FindName("ADUserListButton")
$res = $Win.FindName("Result")
$Start.Add_Click({
$event=$res.text
If($event -eq "") {
[System.Windows.MessageBox]::Show("Please make sure all values are entered", "Missing Values")}
Else{Get-WMIObject -class Win32_ComputerSystem | select username -OutVariable a
$name=$a.username
[System.Windows.MessageBox]::Show("User name is $name", "Hello")
}
})
What I have done with my grid clicks where lots of data is listed, rather than creating code for each cell, you can create a click handler on the datagrid that gets the selected row or cell and does something with it.
$adUsersList_Results = $activeDirectory_Users_ListControl.FindName('ResultsDataGrid')
$adUsersList.Add_Click({
$sender = $args[0]
$e = $args[1]
$prop = $sender.SelectedItem.<#[Some property of the row]#>
#do something with $prop....
})
Don't know if this is what you are looking for, and I know it is a little bit late, but it might help someone else out there...

Resources