Некоторое время назад в компании была развернута терминальная ферма.
Первым делом в неё были выселены пользователи некой желтой программы.
После чего отдел поддержки желтой программы спросил меня, можно ли отсылать сообщения пользователям фермы всем сразу. XaocCPS посоветовал мне играться в сторону WPF. Нужный скрипт был написан, но его работой я неудовлетворился:
1. Надо ставить внешний компонент PowerShellPack.
2. Компонент ставиться на сервера фермы (х64) отказался.
3. Распространять такое решение из за пункта 1 всем желающим не очень удобно.
Xaegr подсказал что я могу избавиться от прослойки WPF.
Писать можно, можно даже писать красиво. Скрипт выполняется везде где есть .Net Framework — XP, Win7 и скорее всего пойдет даже на х64 серверах фермы.
Как писать — под катом.
UPD по просьбам скрипт выложен на SkyDrive, ссылка в конце
И так — ТЗ, полученное от отдела поддержки было «отправка сообщений всем пользователям терминальной фермы».
Мною ТЗ было расширено:
1. Выбор серверов.
2. Выбор пользователей.
3. Ввод текста сообщения.
4. Вставка подписи — идентификатор отправившего сообщения пользователя — виден в заголовке окна, но на него внимание обращают мало, выбрал вариант из нескольких предустановленных вариантов.
5. Защита от нечаянной отправки.
Предварительно было найдено, что в Win7 и 2008* есть команда msg, с помощью которой данные сообщения и будут отправляться, но прием сообщений надо разрешить, что и сделал простеньким regedit файлом:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server]
"AllowRemoteRPC"=dword:00000001
К сожалению, если пользователь из списка не подключен к серверу — скрипт много и сильно ругается. Я пока не придумал как получить вывод msg, и его обрабатывать.
Скрипт по максимуму комментирован.
~15kb
# Подключаем то, с помощью чего будем рисовать нашу форму
Add-Type -assembly System.Windows.Forms
# Все просто, массивы из списка серверов, списка пользоватей, и варианты подписи - чтобы использовать могла не только поддержка.
$Server = @("rdp1-1, rdp1-2, rdp1-3, rdp1-4", "rdp2-1, rdp2-2, rdp2-3, rdp2-4")
$User = @("*", "1c-admin1, 1c-admin2")
$Message = "Умолчательное сообщение"
# По хорошему, надо бы сделать так, чтобы если умолчательное сообщение не менялось и шло нажатие на кнопку отправить, то выводилось бы предупреждение.
# Так же по аналогии с тем что есть, тут тоже можно повесить список со стандартными сообщениями
# Подписываемся
$Sign = @("С уважением, администраторы 1С", "С уважением, системные администраторы")
# Повелось, что все функции описываются до их использования.
# Функция отправки сообщения. Принимаем параметры, проверяем, если чекбокс включен (а он включен по умолчанию), устанавливаем параметры отправки только на консоль локалхоста, иначе проходим по списку серверов, внутри каждого сервера инициируем цикл по списку пользователей с предварительно отрезанными пробелами до и после текста(помните формат: "сервер1, сервер2, сервер3" для красивой читаемости текста), вызываем сам msg для отправки сообщения и после чего вызываем диалогов окно с подтверждением отправки и закрытием программы.
Function SendMessage {
param ($Server, $User, $Message, $Sign)
# Write-Host $Server, $User, $Message, $Sign
If ($TestRunCheckBox.Checked -eq 1 ) { Write-Host $TestRunCheckBox.Checked; $Server="localhost"; $User = "Console" }
ForEach ($Item in $Server) {
ForEach ($UserX in $User) {
$UserTrim = $UserX.Trim()
$ServerTrim = $Item.Trim()
$MsgTrim = $Message.Trim()
$SignTrim = $SignX.Trim()
# Отладочный вывод, оставлен на всякий случай.
# Write-Host "User: $UserTrim; Server: $ServerTrim; Message: $MsgTrim; Signature: $SignTrim"
c:\windows\system32\msg.exe $UserTrim /Server:$ServerTrim $MsgTrim $SignTrim
}
}
Confirm
}
# Вывод окна с подтверждением
Function Confirm {
$ConfirmWin = New-Object System.Windows.Forms.Form
$ConfirmWin.StartPosition = "CenterScreen"
$ConfirmWin.Text = "Подтверждение отправки"
$ConfirmWin.Width = 200
$ConfirmWin.Height = 120
$ConfirmWin.ControlBox = 0
$ConfirmWinOKButton = New-Object System.Windows.Forms.Button
$ConfirmWinOKButton.add_click({ $MainSendWindow.Close(); $ConfirmWin.Close() })
$ConfirmWinOKButton.Text = "Закрыть"
$ConfirmWinOKButton.AutoSize = 1
$ConfirmWinOKButton.Location = New-Object System.Drawing.Point(50,50)
$ConfirmLabel = New-Object System.Windows.Forms.Label
$ConfirmLabel.Text = "Сообщение было отправлено"
$ConfirmLabel.AutoSize = 1
$ConfirmLabel.Location = New-Object System.Drawing.Point(10,10)
$ConfirmWin.Controls.Add($ConfirmLabel)
$ConfirmWin.Controls.Add($ConfirmWinOKButton)
$ConfirmWin.ShowDialog() | Out-Null
}
# Главное окно, по хорошему тоже стоило бы оформить в виде функции
$MainSendWindow = New-Object System.Windows.Forms.Form
$ToolTip = New-Object System.Windows.Forms.ToolTip
$ToolTip.BackColor = [System.Drawing.Color]::LightGoldenrodYellow
$ToolTip.IsBalloon = $true
# $ToolTip.InitialDelay = 500
# $ToolTip.ReshowDelay = 500
# Инициализация контролов формы
# Кнопки и чекбокс
$SendButton = New-Object System.Windows.Forms.Button
$CloseButton = New-Object System.Windows.Forms.Button
$TestRunCheckBox = New-Object System.Windows.Forms.CheckBox
# Текстовые поля и списки
$ServerTextBox = New-Object System.Windows.Forms.ComboBox
$UserTextBox = New-Object System.Windows.Forms.ComboBox
$MessageTextBox = New-Object System.Windows.Forms.TextBox
$SignTextBox = New-Object System.Windows.Forms.ComboBox
# Подписи
$ServerTextBoxLabel = New-Object System.Windows.Forms.Label
$UserTextBoxLabel = New-Object System.Windows.Forms.Label
$MessageTextBoxLabel = New-Object System.Windows.Forms.Label
$SignTextBoxLabel = New-Object System.Windows.Forms.Label
# Описываем свойства (комментариями я еще добавлю несколько нагугленных
# интересных свойств для общего развития и чтобы далеко не бегать ;))
# Главная форма
$MainSendWindow.StartPosition = "CenterScreen"
$MainSendWindow.Text = "Отправка сообщения пользователям"
$MainSendWindow.Width = 470
$MainSendWindow.Height = 220
# несколько плюшек и обещанных красивостей
#$Win.ControlBox = 0 # отключить кнопки свернуть, минимизацию и закрытие.
# $Win.ShowIcon = 0
# $Win.ShowInTaskbar = 0
# $Win.HelpButton = 1
# авторазмер может отрабатывать если вся форма - к примеру одна кнопка "Сделать хорошо"
# $Win.Autosize = 1
# $Win.AutoSizeMode = "GrowAndShrink"
# стиль обрамления и шрифт.
# $Win.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Fixed3D
# $Win.Font = New-Object System.Drawing.Font("Verdana",32)
# Подписи к текстовым полям
$ServerTextBoxLabel.Location = New-Object System.Drawing.Point(10,12)
$ServerTextBoxLabel.Text = "Список серверов"
$ServerTextBoxLabel.Autosize = 1
$UserTextBoxLabel.Location = New-Object System.Drawing.Point(10,42)
$UserTextBoxLabel.Text = "Список пользователей"
$UserTextBoxLabel.Autosize = 1
$MessageTextBoxLabel.Location = New-Object System.Drawing.Point(10,73)
$MessageTextBoxLabel.Text = "Сообщение"
$MessageTextBoxLabel.Autosize = 1
# Плюшка в виде красивой подсказки, делается другим методом вызова, поэтому идет к каждому обьекту в блоке, чтобы не теряться.
$ToolTip.SetToolTip($MessageTextBoxLabel, "Надо подписаться, а то в заголовке окна с сообщениями не видно")
$SignTextBoxLabel.Location = New-Object System.Drawing.Point(10,103)
$SignTextBoxLabel.Text = "Подпись"
$SignTextBoxLabel.Autosize = 1
$ToolTip.SetToolTip($SignTextBoxLabel, "Надо подписаться, а то в заголовке окна с сообщениями не видно")
# Описание текстбокса
# Позиция
$ServerTextBox.Location = New-Object System.Drawing.Point(140,10)
# Источник данных
$ServerTextBox.DataSource = $Server
# Размер
$ServerTextBox.Width = 300
# Обработка события - при смене текста в поле, присваиваем переменной новое полученное значение.
$ServerTextBox.add_TextChanged({ $Server = $ServerTextBox.Text })
# индекс порядка перехода по Tab
$ServerTextBox.TabIndex = 1
$ToolTip.SetToolTip($ServerTextBox, "Укажите список серверов")
$UserTextBox.Location = New-Object System.Drawing.Point(140,40)
$UserTextBox.DataSource = $User
# Не забываем про массив
$UserTextBox.Text = $User[1]
$UserTextBox.add_TextChanged({ $User = $UserTextBox.Text })
$UserTextBox.Width = 300
$UserTextBox.TabIndex = 2
$ToolTip.SetToolTip($UserTextBox, "Кому отправлять будем? (* для *всех* пользователей, по умолчанию)")
# Поле сообщения
$MessageTextBox.Location = New-Object System.Drawing.Point(140,70)
$MessageTextBox.Text = $Message
# По клику в поле ввода - автоматически выделяем весь текст, чтобы не надо было
# нажимать удаление
$MessageTextBox.add_click({ $MessageTextBox.SelectAll() })
$MessageTextBox.add_TextChanged( { $Message = $MessageTextBox.Text })
$MessageTextBox.Width = 300
$MessageTextBox.TabIndex = 3
$ToolTip.SetToolTip($MessageTextBox, "И шо мы таки хотим сказать?")
# Поле подписи - отправляемая переменная уже другая
$SignTextBox.Location = New-Object System.Drawing.Point(140,103)
# Источник текста для подписи
$SignTextBox.DataSource = $Sign
# А мы помним, что там массив?:)
$SignTextBox.Text = $Sign[1]
$SignTextBox.add_TextChanged({ $SignX = $SignTextBox.Text })
$SignTextBox.Width = 300
$SignTextBox.TabIndex = 4
$ToolTip.SetToolTip($SignTextBox, "Страна должна знать своих героев")
# Нопка отправки.
$SendButton.Location = New-Object System.Drawing.Point(10,150)
$SendButton.Text = "Отправить сообщение"
# Выполняем разделения строк на массивы с разделителем запятая, вызываем функцию отправки сообщения
$SendButton.add_click({ $User = $UserTextBox.Text.Split(","); $Server = $ServerTextBox.Text.Split(","); $SignX = $SignTextBox.Text; SendMessage $Server $User $Message $SignX} )
$SendButton.Autosize = 1
$SendButton.TabIndex = 5
$ToolTip.SetToolTip($SendButton, "Тыцни пимпочку")
# Прописываем блокировочный чекбокс
$TestRunCheckBox.Location = New-Object System.Drawing.Point(200,150)
$TestRunCheckBox.Text = "Тест"
$TestRunCheckBox.Checked = 1
$TestRunCheckBox.AutoSize = 1
$TestRunCheckBox.TabIndex = 6
$ToolTip.SetToolTip($TestRunCheckBox, "Сними меня, а то работать не будет")
# Кнопочка выхода, по событию вызывает метод закрытия
$CloseButton.Location = New-Object System.Drawing.Point(315,150)
$CloseButton.Text = "Выйти из программы"
$CloseButton.add_click({ $MainSendWindow.Close() })
$CloseButton.Autosize = 1
$CloseButton.TabIndex = 7
$ToolTip.SetToolTip($CloseButton, "Пойдем ка отсюда")
# Добавляем контролы в форму и вызываем её запуск
$MainSendWindow.Controls.Add($SendButton)
$MainSendWindow.Controls.Add($TestRunCheckBox)
$MainSendWindow.Controls.Add($CloseButton)
$MainSendWindow.Controls.Add($ServerTextBox)
$MainSendWindow.Controls.Add($UserTextBox)
$MainSendWindow.Controls.Add($MessageTextBox)
$MainSendWindow.Controls.Add($SignTextBox)
$MainSendWindow.Controls.Add($ServerTextBoxLabel)
$MainSendWindow.Controls.Add($UserTextBoxLabel)
$MainSendWindow.Controls.Add($MessageTextBoxLabel)
$MainSendWindow.Controls.Add($SignTextBoxLabel)
$MainSendWindow.ShowDialog() | Out-Null
В результате получилась вот такая вот красивость
Скрипт могу выложить, при желании читателей, куда-то.
Не уверен, что хватит кармы публировать в профильный, поэтому выложил сюда
Исходник скрипта на GitHub’е
In a previous article, you learned how to create a prompt inside of PowerShell by recreating the action prompt for the Remove-Item cmdlet. PowerShell can also create a graphical or GUI input prompt using .NET Framework form-building features available since PowerShell v3. In this tutorial, learn how to create a simple yes/no GUI input prompt to delete a file.
The Code
This time around, I’m going to display the entire code sample, then I’ll walk through each part and explain what is happening. Here I decided to split the PowerShell code out into two functions: the code for Remove-MyItem is its own function and calls the New-YesNoPrompt function that actually creates the GUI input prompt.
Creating the Form Window
Let’s start with the New-YesNoPrompt function. The function takes in two parameters: PromptTitle and PromptMessage. The title is displayed at the top of the window in the title bar, and the message is displayed in the box to the user, ideally what action is about to be approved or denied. First, outside of my two functions at the top of my .ps1 file, I need to import two .NET Framework classes using Add-Type in order to create my form:
- System.Windows.Forms
- System.Drawing
Back inside the function, I need to create the $form object to start adding controls to:
# Create the form title, size, and starting position $form = New-Object System.Windows.Forms.Form $form.Text = $PromptTitle $form.Size = New-Object System.Drawing.Size(300, 200) $form.StartPosition = 'CenterScreen'
The PowerShell code contains the basic information on displaying the GUI input form using different properties, such as:
– Text: the title goes here and is displayed in the title bar of the window
– Size: creates a new object using the System.Drawing.Size namespace; this is defined in an (x, y) format, or (width, height). The box created here is 300 pixels wide by 200 pixels tall.
– StartPosition: this determines where the window is displayed; currently set to CenterScreen to display in the middle, but other options include:
- CenterParent: the form is centered within its parent form
- Manual: position is determined by the Location property, which is the of the upper-left corner of the window
- WindowsDefaultBounds: the form is positioned at the Windows default location and has the bounds determined by Windows default
- WindowDefaultLocation: the form is positioned at the Windows default location and has the dimensions specified in the form’s size
With the basic windows form created, let’s move onto create action buttons for the user to respond to.
Creating the Buttons
For our form, I need options for the user to select from. These options will be presented as Yes and No buttons to indicate if I want to delete the file or not:
# Create the Yes button and its properties $yesButton = New-Object System.Windows.Forms.Button $yesButton.Location = New-Object System.Drawing.Point(60, 120) $yesButton.Size = New-Object System.Drawing.Size(75, 23) $yesButton.Text = 'Yes' $yesButton.DialogResult = [System.Windows.Forms.DialogResult]::Yes $form.AcceptButton = $yesButton $form.Controls.Add($yesButton) # Create the No button and its properties $noButton = New-Object System.Windows.Forms.Button $noButton.Location = New-Object System.Drawing.Point(165, 120) $noButton.Size = New-Object System.Drawing.Size(75, 23) $noButton.Text = 'No' $noButton.DialogResult = [System.Windows.Forms.DialogResult]::No $form.CancelButton = $noButton $form.Controls.Add($noButton)
First, I create an object based on the Button class of the Forms namespace. I then set the Location of the button within the form using (x,y) coordinates, or the distance from the upper left of the form window in pixels. This is the same idea as setting the Location of the form itself on the monitor. This will probably take some experimentation to get it just right.
Next, I set the Size of the button just like setting the size of the windows form using (width, height) in pixels. After the size is setting the Text of what will appear on the button, here the words Yes and No to indicate if I want to delete the file or not.
Notice the difference in each button’s Location property. The Y values are both the same at 120, meaning they will be horizontally aligned, but the X values are not. The Yes button is 60 pixels from the left while the No button is 165 pixels from the left, meaning they will be next to each other. You’ll see this later in our final screenshot.
The DialogResult property gets or sets the returned value when the button is clicked. The DialogResult property is set to an identifier that indicates the return value of the dialog box, and for each of our buttons this is Yes or No. Other options for DialogResults include: Abort, Cancel, Ignore, None, OK, and Retry.
Along with each button, I set the AcceptButton to $yesButton and CancelButton to $noButton for the form itself. The AcceptButton is activated when the user presses the Enter key on the form, and the CancelButton is activated when the user presses the Escape key. So for my form, if the user presses Enter to the prompt, this is the same as clicking the Yes button, and pressing Escape is the same as pressing the No button. Finally, I add the button to the form itself under the Controls property.
Finalizing the Form
At last, I need to display the value of the $PromptMessage parameter onto the form. This is going to be the question I ask the user to respond to, and this takes shape using a Label. Like our other objects so far, it has a Location, Size, and Text property to create how it looks.
Also, like the buttons, I add it to the Controls property of the form. The last property I set is TopMost, which just puts the form on top of all others when it is displayed. With the form created, I return the form object back to whatever called my form function.
Remove-MyItem Function
By separating the logic to create the graphical prompt out of the Remove-MyItem function, the code here is greatly reduced. First I create a $params object with the window title and message that I want to display in the graphical prompt window. I pass this object to my prompt function using splatting (I always forget splatting exists, so I wanted to use it here) along with using the ShowDialog() method on my form. This will display the form to the user, and the result of which button is selected is saved to $response.
Just like the examples from last week, I use a switch statement to perform different actions based on the option the user has chosen. The condition is to use the System.Windows.Forms.DialogResult type we specified for each button in the other function. For this example, I just outputted text to show whether the action would remove the file or leave it alone.
Final Results
Here is the final result of creating my GUI input prompt to remove a file when calling my PowerShell function Remove-MyFile and selecting the Yes button in the prompt:
Final Thoughts
The method above is not intended to replace other options for confirming changes or testing commands using ShouldProcess, which adds -WhatIf and -Confirm functionality to a function. I received some valid criticism from the previous post on creating console prompts. The article here is to simple show how it is possible to create a graphical prompt, not to replace this other important functionality. Please check out my repository for this on GitHub for the code used in this post plus any additional examples I come up with in the future.
Questions or comments? If so, drop me a note below or find me on Twitter or LinkedIn to discuss further.
PowerShell GUI with buttons, textbox and combobox
GUI makes
life easier, but of course command line has a power of its own.
How to add a
form in PowerShell with Buttons, TextBox and ComboBox?
Adding GUI
forms in PowerShell must be done manually by code.
It’s not
that hard, you just need to love PowerShell and see what it can do to automate
IT administration and makes your life easier.
Anyway,
code below introduces how to add GUI to PowerShell and it also illustrates how
to make use of those GUI buttons and send a command to remote computers.
Code to add buttons, textbox and combobox in PowerShell, and how to execute a command after the button is clicked.
#initialize
the main form
$form = new-object Windows.forms.form
$form.text = «Server
Selection Form»
$form.minimumSize = New-Object System.Drawing.Size(600,300)
$form.maximumSize = New-Object System.Drawing.Size(600,300)
#add a button to the form
$button = new-object windows.forms.button
$button.text = «Close
Me»
#action that the button will execute after it has been clicked
$button.add_click({$form.close()})
#add a textbox to the form
$theTbox = New-Object system.Windows.Forms.Textbox
$theTbox.location = New-Object system.Drawing.Size(0,50)
$theTbox.size = New-Object System.Drawing.Size(100,20)
#add a drop down combobox to the form
$the_combo = New-Object system.Windows.Forms.ComboBox
$the_combo.location = New-Object system.Drawing.Size(0,90)
$the_combo.size = New-Object System.Drawing.Size(200,20)
$the_combo.DropDownStyle = «Dropdownlist»
#add another button to the form
$Go_button = new-object windows.forms.button
$Go_button.text = «Do
the Action»
$Go_button.location = New-Object system.Drawing.Size(0,190)
$Go_button.size = New-Object System.Drawing.Size(110,30)
#You can customized the command on this button
#Example below will shutdown the selected
computer
#Invoke-command for remote computer can also be used (imagination is your
friend 😊 )
$Go_button.add_click({Stop-Computer -ComputerName $theTbox.text})
#the “@” symbol will create an array out
of the text file contents
$ComboList_Items = Get-Content -Path @(«C:\dev\server_list.txt»)
#Loop thru the text file or the array
#and add the contents to the combobox for
selection
ForEach ($Server in $ComboList_Items) {
$the_combo.Items.Add($Server)
}
$form.controls.add($theTbox)
$form.controls.add($button)
$form.controls.add($Go_button)
$form.controls.add($the_combo)
$form.Add_shown({$form.Activate()})
#action that will capture every time a
value is selected on the combobox
$the_combo_SelectedIndexChanged=
{
$theTbox.text= $the_combo.text
}
$the_combo.add_SelectedIndexChanged($the_combo_SelectedIndexChanged)
$form.ShowDialog()
#===== CODE ends here ======
#The contents of the text file just look
like this:
Server-1
Server-2
Server-3
Server-4
Server-5
Server-6
Server-7
Localhost
Above
contents is the one that is being read by this line below, change the path and
the filename as necessary. It’s just for example purposes.
Cheers..till next time.
PowerShell Forms GUI allows users to create graphical user interfaces for their scripts, making it easier for users to interact with PowerShell commands through visual elements rather than the command line.
Here’s a simple code snippet to create a basic form in PowerShell:
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object System.Windows.Forms.Form
$form.Text = 'My PowerShell GUI'
$form.Size = New-Object System.Drawing.Size(300,200)
$button = New-Object System.Windows.Forms.Button
$button.Location = New-Object System.Drawing.Point(100,70)
$button.Size = New-Object System.Drawing.Size(100,30)
$button.Text = 'Click Me'
$button.Add_Click({ [System.Windows.Forms.MessageBox]::Show('Hello, World!') })
$form.Controls.Add($button)
$form.ShowDialog()
Understanding Windows Forms
What is Windows Forms?
Windows Forms is a graphical (GUI) class library included as a part of the .NET Framework, allowing developers to create rich desktop applications. It provides a straightforward way to build user interfaces by offering a wide array of pre-defined controls. Since its introduction, Windows Forms has evolved to become a staple for developers looking to create traditional desktop applications for Windows.
Creating a Basic Windows Form
To get started with PowerShell Forms GUI, you first need to utilize the System.Windows.Forms namespace, which contains classes for building forms and controls. Here’s a simple code snippet to create a basic form:
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object System.Windows.Forms.Form
$form.Text = "Hello World Form"
$form.ShowDialog()
This code initializes a basic Windows Form that simply displays a dialog with the title «Hello World Form.» The `ShowDialog()` method displays the form as a modal dialog box, blocking input to other windows until the form is closed.
Mastering PowerShell Format for Effortless Command Crafting
Designing a PowerShell GUI
Essential GUI Components
Common Controls are the building blocks of any GUI. Here’s a brief overview of some significant controls you’ll often use:
- Buttons: Triggers actions when clicked, such as executing commands or submitting forms.
- TextBoxes: Allows users to input text data.
- Labels: Displays static text or information that is read-only.
- ListBoxes: Displays a list of items from which users can select.
Layout Management
Effective layout management is crucial for usability. Various layout options like `FlowLayoutPanel` and `TableLayoutPanel` can help organize your controls neatly within the form. These layouts adjust dynamically based on the size and position of individual controls, improving the overall user experience.
Example: Building a Simple Calculator
Initial Setup
To illustrate how to create a more complex GUI, we’ll build a simple calculator. Here’s how to set up the initial form:
$form = New-Object System.Windows.Forms.Form
$form.Text = "Simple Calculator"
$form.Size = New-Object System.Drawing.Size(250, 200)
# Additional setup code will go here
Adding Controls
Next, we can add buttons and text boxes for our calculator functionality. For example, we’ll add two text boxes for input and a button for the addition operation.
$num1 = New-Object System.Windows.Forms.TextBox
$num2 = New-Object System.Windows.Forms.TextBox
$result = New-Object System.Windows.Forms.TextBox
$button = New-Object System.Windows.Forms.Button
$num1.Location = New-Object System.Drawing.Point(20, 20)
$num2.Location = New-Object System.Drawing.Point(20, 60)
$result.Location = New-Object System.Drawing.Point(20, 140)
$button.Location = New-Object System.Drawing.Point(20, 100)
$button.Text = "+"
$form.Controls.Add($num1)
$form.Controls.Add($num2)
$form.Controls.Add($result)
$form.Controls.Add($button)
Event Handling
Event handling is where PowerShell Forms really shines. You can make your application interactive by responding to user actions. Here’s how to handle the button click event:
$button.Add_Click({
$result.Text = [int]$num1.Text + [int]$num2.Text
})
This code will take the text from the two input text boxes, convert them into integers, and display the sum in the result text box when the button is clicked.
Customizing the Form
Styling and Appearance are important aspects of any GUI. You can enhance your form’s appearance by adjusting control properties, such as size, colors, and fonts:
$form.BackColor = [System.Drawing.Color]::LightGray
$button.BackColor = [System.Drawing.Color]::Green
$form.Size = New-Object System.Drawing.Size(300, 250)
Using Icons and Images is another way to make your application visually appealing. Setting an icon for your form can provide a professional touch:
$form.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon("app.ico")
Mastering the PowerShell Formatter: A Quick Guide
Advanced Techniques
Integrating PowerShell Scripts with GUI
One of the most powerful features of a PowerShell Forms GUI is the capability to execute scripts from the GUI. For instance, you can run a PowerShell script when a specific action occurs, such as a button click.
Populating Data into Controls
Another advanced feature is dynamic data binding. You can populate a ListBox or ComboBox control with dynamic data from arrays or other data sources. Here’s how you would populate a ComboBox:
$comboBox = New-Object System.Windows.Forms.ComboBox
$comboBox.Location = New-Object System.Drawing.Point(20, 180)
$comboBox.Items.AddRange(@("Option 1", "Option 2", "Option 3"))
$form.Controls.Add($comboBox)
Validating User Input
Input validation is essential for maintaining the integrity of your application. When collecting user data, it’s important to ensure that the data entered meets the expected criteria:
if (-not $num1.Text -or -not $num2.Text) {
[System.Windows.Forms.MessageBox]::Show("Both fields must be filled!")
}
Using MessageBox to display alerts can help inform users about input errors effectively.
PowerShell Format Number: Essentials Made Easy
Debugging and Error Handling
Common Errors in PowerShell GUI
While working with PowerShell Forms, you might encounter typical pitfalls, such as control placement issues or incorrect data types. Understanding these issues can save you time during development.
Implementing Error Handling
To create a smooth user experience, implementing error handling using try-catch blocks is crucial:
try {
# Code that may cause an exception
$result.Text = [int]$num1.Text + [int]$num2.Text
} catch {
[System.Windows.Forms.MessageBox]::Show("An error occurred: $_")
}
This ensures that your application handles unexpected issues gracefully, providing valuable feedback to users rather than crashing.
Mastering PowerShell ToString: Quick Conversion Guide
Conclusion
In summary, creating a PowerShell Forms GUI allows you to develop rich, interactive applications that make use of PowerShell’s powerful scripting capabilities. From creating simple forms and controls to implementing advanced features like event handling and dynamic data binding, the possibilities are vast.
As you experiment with these techniques, don’t be afraid to think creatively and challenge yourself. Each project you undertake builds your skills further. For ongoing learning, explore the wealth of resources available such as books, tutorials, and online communities centered around PowerShell.
By starting today, you can create beautiful and functional GUIs that not only enhance your automation scripts but also broaden your capabilities as a developer in the PowerShell ecosystem. Happy scripting!
PowerShell
Recently, I was tasked to provide a GUI for a PowerShell script. Okay, I think I tasked myself, but it was an interesting foray into the .Net [System.Windows.Forms] class…One of the common threads I noticed in the handful of scripts I found was that they really didn’t offer options for parameters. I’ve been a big proponent for creating tools, aka functions, since I first began writing PowerShell code. So, I set out gathering some tools that I thought I would need.
Introduction
Recently, I was tasked to provide a GUI for a PowerShell script. Okay, I think I tasked myself, but it was an interesting
foray into the .Net [System.Windows.Forms]
class.
As one does to find script inspiration — some might call this a starting point — I took to my favorite search engine and
found numerous scripts built with SAPIEN Technologies PowerShell Studio
as well as manually coded scripts on GitHub.
Since a requirement for my task was that I could not use any external application, I was forced to use the manually
coded option.
NOTE
It’s been too long since I’ve posted any content and I wanted to get something out to let you know that I’m still here.
Functions
One of the common threads I noticed in the handful of scripts I found was that they really didn’t offer options for
parameters. I’ve been a big proponent for creating tools, aka functions, since I first began writing PowerShell code. So,
I set out gathering some tools that I thought I would need. The functions I created are by no means complete, nor is
the list comprehensive.
Basically, my GUI script needed to be able to the following:
- Create a form
- Display some controls
- Header, used as a section label
- Buttons, which must performs some actions
- Display columnar data in a grid
- Highlight certain rows based on a value of a cell
- Display the current status in the status bar of the form
Create a form
The first thing I needed to do was instantiate a new form object. I wrote New-WindowsForm
to handle this. At minimum, I needed
to provide the title for the form (which is displayed in the title bar of the form) and the height and width (in pixels).
I decided to also add a switch (-NoIcon
) that would hide the default icon in the title bar. By default, hard-coded that
is, the form will autosize and provide scrollbars.
I then wrote Set-WindowsForm
that allows me to add an array of labels, an array of buttons, a data grid view, a status
strip, and a script block for the on load event.
Display some controls
I wrote New-FormLabel
and New-FormButton
both with text to display, height, width, x-axis draw starting point, and
y-axis draw starting point. For New-FormButton
, I also included a parameter for action (a scriptblock) and an
anchorstyle (this lets the close button always be on the right side connected to the edge of the form).
The button’s action could be a very complex scriptblock that can load a file to use, set the filename for a log, update
the data, and update the status bar.
Display columnar data
The [System.Windows.Forms.DataGridView]
class was used to display my data and to highlight the rows that needed it. I
wrote New-DataGridView
to instantiate an instance of the class. With Update-DataGridView
, I’m able to pass in the
data, a DataGridView object, and a [hashtable]
that I use for to determine how to highlight the row. This part was
very tricky.
|
|
Then in the Update-DataGridView
, I have this code:
|
|
Actually, this function is the only one that provides Verbose output at the moment. If I find myself using this ad hoc
module often, I’ll spiffy it up with plenty Write-Verbose
and Write-Warning
statements.
Display the current status in the status bar of the form
Refreshing the data would take some time. Loading a file or writing a file would take some time. I wanted to be able to
tell the user (myself, at this point) that things were happening. Enter the [System.Windows.Forms.StatusStrip]
class.
In a similar fashion as the form, I created New-StatusStrip
and a Set-StatusStrip
functions. The first creates a,
more-or-less, empty object. The latter function does all of the heavy lifting. It will display the operation, progress,
and the progress values.
The Module and Example Script
Now that we have the tools you need to create a quick GUI, let’s create the script that will use them. This simple script
will display the form, load specific processes highlighting them based on what we want, and provide a way to refresh the
data.
Here is the module and example script.
In Action
Here is a demonstration on how this works.
Bonus
Something else that most forms provide is a way to open a file and to save a file. I have included Get-OpenFileDialog
and Set-SaveFileDialog
to do just that. These are currently very basic and could use some long-term care (more
parameters).
Next Steps
If I use this ad hoc module more, I would need to convert it to fully formed module, via plaster template. I know many
improvements can be made on accepting more properties for the various components. Again, this was a quick proof-of-concept.
Summary
And that’s how I came to write an ad hoc (not fully baked, developed, bare-boned, or whatever you want to call it) module
for displaying a GUI using the [System.Windows.Forms]
class.
I hope you’ve found this interesting or informative. If you have any comments or questions, please post them below.
Thanks for reading!