Problem with style triggers / datatriggers in powershell WPF script - wpf

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.

Related

Updating WPF XAML Combobox selecteditem from powershell

I'm just simply trying to change the selected item on a combobox drop down from powershell. Whenever I use:
$dg_Servers.Items[0].cbox_Disk.SelectedItem = 88
I receive the error:
"...cannot be found on this object. Verify that the property exists and can be set."
Here's my XAML:
DataGridTemplateColumn Header="DiskC" Visibility="Visible" Width="61" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Path=cbox_DiskC}"
SelectedItem="{Binding Path=cbox_DiskD, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I'm not sure what I'm missing here.. seems like this should be super easy. System.Windows.Forms makes this a no brainer to accomplish.
EDIT - adding the powershell side:
$row= New-Object NewRow -Property #{ServerName = ""; cbox_DiskC = (80..250); cbox_DiskD = (50..250); cbox_DiskE = (1..250); cbox_DiskF = (1..250); cbox_DiskG= (1..250); cbox_Mem=(1..16); cbox_CPU=(1..8); cbox_OSSpec = $Options_OSSpecs; cbox_Template = $Options_Templates; cbox_VLAN = $Options_VLAN ; cbox_Folder = $Options_Folders }
$itemsource = New-Object -TypeName System.Collections.ObjectModel.ObservableCollection[object] -ArgumentList #($row)
$dg_Servers.ItemsSource = $itemsource
I read this morning that you have to use an observablecollection in order for the UI to be notified of a change and refresh.. but I'm still not having any luck. I'm only using one thread so maybe that's the issue?
I was trying to change the combobox's drop down selection with another buttons click event.
$btn_Validate.Add_Click({$dg_Servers.SelectedItems[0].cbox_DiskC = 88})
But as I said, when I click the button on the UI nothing changes and the selection remains the same.
You should set the cbox_DiskD source property that is bound to the SelectedItem property of the ComboBox:
$dg_Servers.Items[0].cbox_DiskD = 88
Probably not the right way to do it but at this point I've spent too much time trying to figure out a better way :)
I had to bind to a different element in my class. Making them different allows me to keep all my items in the dropdown AND select a specific one. Otherwise if I keep them the same, all of my items disappear and it's set to 88. (NOTE: cbox_DiskC as the itemsource and cbox_DiskC_selected as the selecteditem below)
<DataGridTemplateColumn Header="DiskC" Visibility="Visible" Width="61" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Path = cbox_DiskC}"
SelectedItem="{Binding Path=cbox_DiskC_selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Once I did that, in the add_click I basically $nulled the itemsource, and re-added it which forces my GUI to refresh.
$btn_Validate.Add_Click({$row.cbox_DiskC_selected = 88; $dg_Servers.ItemsSource = $null; $dg_Servers.ItemsSource = $itemsource})
Later on I'll make a function that does the refresh to clean it up a bit.

Change content of 2 textboxes based on listbox selection

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.

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

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...

Path.Data styling works only on first instance of styled object

A have a ListBox of items, every ListBoxItem contains an icon in the form of a Path object, like so:
<ListBox.ItemTemplate>
<DataTemplate>
<Grid ...>
...
<Path Margin="4" Style="{StaticResource ErrorIconPath}"
Stretch="Uniform" Width="26" Height="26"
RenderTransformOrigin="0.5,0.5" Grid.Column="1" Grid.Row="1"
UseLayoutRounding="False"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
The Path's style is contained in Appl.xaml (Application.Resources section) and is the following:
<Style x:Key="ErrorIconPath" TargetType="Path">
<Setter Property="Data" Value="F1M874.094,289.369L854.3,254.63C854.028,254.151 853.515,253.856 852.958,253.856 852.403,253.856 851.89,254.151 851.617,254.63L831.824,289.369C831.555,289.84 831.559,290.416 831.835,290.883 832.111,291.348 832.618,291.634 833.165,291.634L872.752,291.634C873.299,291.634 873.805,291.348 874.081,290.883 874.357,290.416 874.361,289.84 874.094,289.369 M855.653,287.189L850.264,287.189 850.264,282.745 855.653,282.745 855.653,287.189z M855.653,279.41L850.264,279.41 850.264,266.077 855.653,266.077 855.653,279.41z" />
</Style>
The trouble is that only the first item in the ListBox binds the Data property as expected, the other ones don't bind it at all (hence they appear as blank space, but match the size of the Path). Also when I use the style anywhere else (i.e. outside the ListBox), only the first instance that occurs will bind.
The weird thing is that if I define for example the Fill property in the Style instead of inline, it works just fine and doesn't exibit the same problems as the Path property.
My guess is that is has something to do with Data not being a primitive type, but I haven't found any fixes.
EDIT: Interestingly, when I bind the Data property directly to System.String resource, it works. I would still like to be able to define this property via a Style though.
EDIT 2: I've just came across the same issue in WPF, when setting Path to a Content of a Button via a Style that is used across more buttons. The path shows up in just one buttons, the others are blank.
Path.Fill is a DependencyProperty, while Path.Data isn't. Instead do:
<DataTemplate>
<Grid ...>
...
<ContentPresenter Content="{StaticResource MyPath}"/>
</Grid>
</DataTemplate>
ContentPresenter.Content is a DependencyProperty so this should work:
<Path x:Key="MyPath" Margin="4" Style="{StaticResource ErrorIconPath}"
Stretch="Uniform" Width="26" Height="26" VerticalAlignment="Center"
RenderTransformOrigin="0.5,0.5" Grid.Column="1" Grid.Row="1"
UseLayoutRounding="False" HorizontalAlignment="Center"
Data="F1M874.094,289.369L854.3,254.63C854.028,254.151 853.515,253.856 852.958,253.856 852.403,253.856 851.89,254.151 851.617,254.63L831.824,289.369C831.555,289.84 831.559,290.416 831.835,290.883 832.111,291.348 832.618,291.634 833.165,291.634L872.752,291.634C873.299,291.634 873.805,291.348 874.081,290.883 874.357,290.416 874.361,289.84 874.094,289.369 M855.653,287.189L850.264,287.189 850.264,282.745 855.653,282.745 855.653,287.189z M855.653,279.41L850.264,279.41 850.264,266.077 855.653,266.077 855.653,279.41z"/>
I am guessing that Geometry cannot be shared. Have you tried setting the x:Shared= "false" to:
<Style x:Key="ErrorIconPath" TargetType="Path">
I've experienced the same behavior in Silverlight and asked a similar question here on StackOverflow.com
( https://stackoverflow.com/q/13426198/1796930), but as I'm writing this, it's been 1 month and I've yet to get even a single answer.
However, as you mentioned in your first edit, I too was able to perform a workaround by creating a resource with my geometry data as a string and then binding the Data property of the Path objects to the string resource resource.
I also had to create two instances of the Path objects that were identical other than each one using a different resource (i.e. two different icons) and then binding the visibility of each to a property in my ViewModel to display the appropriate one.
I am very sure that you did not forgot the stroke here in Path style
<Setter Property="Stroke" Value="Red"/>
I have tested you code on my machine , it worked fine if above line added in style
My first tought was your Path would be broken or not valid. But then I saw you are using the Syncfusion Metro Studio. I tried it with exactly the same code you have and it worked very well. In a Data Template of 5 Items or as a single Path Item.
Have you tried to set the Fill statically to Red or something?
Also maybe try this for the Style definition
<Style x:Key="ErrorIconPath" TargetType="{x:Type Path}">
Third suggestion would be to move the style definition from the App to your Page or even to your Control itself.
To be sure there will be no default styles applied, try
OverridesDefaultStyle="True"
Hope this helps :)

Resources