How to view the content of a UI element
When designing an app, we have to know exactly how every UI element should look like, and respond to the user's actions. The way we can change the looks of our elements is by changing their properties, and controlling how they (and the app itself) respond to user's action is done by implementing events.
Let's take for example a basic control like a button. A button looks pretty innocents, right? you instantly think of a rectangular shaped "thing" that has an OK written on it and when we click on it something happens. Great, we're done. let's go! well. Not!
First of all, a "button" doesn't have a shape at all. It can have gazillion shapes. It's shape is totally dependent on your imagination. A button, like every UI element, has a lot of properties that can be set to make it look the way we want. It also has quite a few events we can use. So, how can we, as PowerShell script developers, view all this information? simple answer. We use Windows PowerShell ISE !
Well, you can use whatever PS console you want, but in our case the easiest way would be ISE. It has a great intellisense and auto drop-down it just makes thing a lot easier. Even though I write my code with VS Code, When it comes to exploring objects and basic debugging, nothing beats ISE.
So let's open an ISE session and explore our button.
A button is a Microsoft windows controls class. It is part of our source of UI elements - System.Windows.Controls namespace. This namespace will be the place we go looking for our WPF UI stuff. Go ahead and type:
[System.Windows.Controls.Button].
The moment you type the last 'dot' - a drop-down is opened, revealing all of the properties and methods of the class itself. Don't get confused. It's not the properties of the Button object, but the Button "envelope" class, which is [System.RuntimeType]. That means, we can get things like MS software version and location of Microsoft's dll file that holds this class, by selecting the 'Assembly' property. Just hit enter and see the result.
[System.Windows.Controls.Button].Assembly
At the bottom of the drop-down there is a list of methods declared for this class. if you stand on one of them, a ToolTip will show you all of the parameters options you can use for running this method.
There are two methods that you will mostly use: GetProperties, GetEvents
To get all the button's property names, type:
[System.Windows.Controls.Button].GetProperties()|Select name|Sort name
I haven't used spaces so it will feet the post's page width. You should use spaces for more readability. So we get the complete list of properties we can use on our Xaml or PS code for the 'button' element. Same thing goes for events:
[System.Windows.Controls.Button].GetEvents()|Select name|Sort name
Be careful with event names. We are not using them on our script exactly as they appear on this list. Instead, we need to find the right name to declare the event.
It is usually the name with an 'add_' prefix, but don't count on that.
We have a method called 'GetEvent' that shows the event details.
For example, to find the "Click" event running method name we will type:
[System.Windows.Controls.Button].GetEvent("Click")
and the 'AddMethod' property will show the right name (right after 'void').
In our case it will be 'add_Click' and this is what we will write in our script:
$MyButton.add_Click( {do something} )
A huge gotcha! if you'll put a space between the event's name and the opening parentheses, PS will cry that the method doesn't exist. So be careful with that.
If we want to USE these properties and events as values in our code , we will need to use double colons (::) . It is also used for creating an object of that class.
In our case, let's make a button object in PowerShell!
$MyButton = [System.Windows.Controls.Button]::New()
You can now add any property to it. For example:
$MyButton.Height = 25
Note that there is a New-Object cmdlet that lets you also create objects.
$MyButton2 = New-Object "System.Windows.Controls.Button"
However, the New() method is 40 times faster than New-Object... Not that it's a deal-braker but I prefer the faster one. Skeptic? take a look at that.
Now that we know how to explore Windows elements. we can do the same for Material Design elements. The easiest way would be to load the dll first:
[System.Reflection.Assembly]::LoadFrom(
"MyFileLocation\MaterialDesignThemes.Wpf.dll")
'MyFileLocation' is the path to the dll file. i.e. "C:\MyProject\Assembly"
After loading the dll we can do the same as we did with windows:
[MaterialDesignThemes.Wpf.ColorZone].GetProperties()
This will show the properties for the 'ColorZone' class.
TIP: If you're using ISE console, you can just type, for example [TextBox then hit <tab> button and it will auto-fill the whole class path. keep hitting tab until you come to the right one for you. Don't use Windows.Forms :-)
I think that's a good starting point for you to see UI elements "under the hood".
If you have any questions, you can put those as comments to this post.
Hi
you don’t have to create a new variable for your xaml objects.
they will be created automatically.
look at the first example for simplicity.
in xaml you give a name to the ui object , like mybutton, then in the ps side you can already use $mybutton variable. No need to create it.
Hello I cannot write well my code here so please excuse me, but the code is simple so you could still help me please.
I try to generate several buttons programmatically thus I need to have different add_click mecanism for each of them.
My issue is that whichever button I click on it returns me the last one iterated instead of the clicked one
$i=0
$buttons.Keys | ForEach-Object {
New-Variable -Name "button$i" -Value (New-Object System.Windows.Controls.Button) -Force
$button=(Get-Variable -Name button$i -ValueOnly) $button.Content=$_
$button.add_Click({ButtonClicked $button}) $leftMenu.Children.Add($button)
$i++
}
Thanks a lot !