WPF формы для PowerShell скриптов

imageИногда, при написании PowerShell скриптов, появляется необходимость отобразить некую форму для ввода каких-нибудь параметров или наоборот для отображения результатов скрипта. Ранее для этого я использовал Windows Forms, но у этого метода есть очевидные недостатки – при создании формы необходимо описывать в коде скрипта каждый элемент формы и его свойства. Недавно натолкнулся на несколько статей от “Hey, Scripting Guy”, по использованию форм WPF в скриптах PowerShell. В одной из них описывается способ использования форм WPF, реализованный Крисом Конте. Суть его сводится к использованию отдельного скрипта-загрузчика формы и отдельного файла с xaml-описанием формы. В данной статье я хочу показать как легко создавать и использовать формы WPF в своих скриптах.

Необходимые инструменты

Для создания скриптов с формами WPF нам понадобится:

Создаем форму

Для создания формы очень удобно пользоваться Visual Studio Express 2013/2014 for Windows Desktop. Создаем новый проект Visual C# –> Windows –> WPF Application и рисуем там форму с необходимыми нам контролами. Я создал для примера такую:

1

Изменения, которые я внес:

  1. У самой формы я изменил заголовок (свойство Title) и размеры (свойства Height и Width)
  2. На форму поместил Label в свойство Content которой поместил текст “Это форма запущенная из под Powershell”, а так же изменил размер текста на 24pt
  3. Кнопка Button с текстом “Change” в свойстве Content
  4. Кнопка Button с текстом “Exit” в свойстве Content

В итоге мы получаем XAML текст с нашей формой

<Window x:Class="test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Форма Powershell" Height="150" Width="525">
    <Grid>
        <Label Content="Это форма запущеная из под Powershell" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="459" FontSize="24"/>
        <Button Content="Change" HorizontalAlignment="Left" Margin="352,89,0,0" VerticalAlignment="Top" Width="75"/>
        <Button Content="Exit" HorizontalAlignment="Left" Margin="432,89,0,0" VerticalAlignment="Top" Width="75"/>
    </Grid>
</Window>

Это практически готовый XAML для использования его в нашем скрипте, единственное что нужно сделать – это удалить x:Class=“test.MainWindow” и сохранить его отдельном файле с расширением .xaml (например MyForm.xaml).

 

Загрузка формы

Для того что бы загрузить нашу форму Крис написал отличный скрипт который я публикую ниже.

[CmdletBinding()]
Param(
    [Parameter(Mandatory=$True,Position=1)]
    [string]$XamlPath
)

[xml]$Global:xmlWPF = Get-Content -Path $XamlPath

try{
    Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase,system.windows.forms
} catch {
    Throw "Failed to load Windows Presentation Framework assemblies."
}

$Global:xamGUI = [Windows.Markup.XamlReader]::Load((new-object System.Xml.XmlNodeReader $xmlWPF))

$xmlWPF.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | %{
    Set-Variable -Name ($_.Name) -Value $xamGUI.FindName($_.Name) -Scope Global
}

Скрипт принимает в качестве параметра $XamlPath путь к файлу XAML. Помещает его содержимое в глобальную переменную $xmlWPF. В блоке try-catch призводится попытка загрузить сборки WPF и в случае ошибки сообщается о невозможности загрузить данные сборки. После чего в глобальную переменную $xamGUI загружается форма из переменной $xmlWPF c помощью сборки Windows.Markup.XamlReader. О последнем блоке ($xmlWPF.SelectNodes…) я расскажу чуть позже. Сохраним данный скрипт под именем loadForm.ps1 и поместим вместе с файлом MyForm.xaml.

 

Использование скрипта загрузки формы

Теперь нам осталось только воспользоваться плодами нашей работы. Создаем в Windows Powershell ISE (или другой IDE разработки, например PowerGUI) новый скрипт powershell и помещаем его в ту же папку, где и предыдущие файлы. Назовем его к примеру Main.ps1. Воспользуемся скриптом Криса что бы загрузить нашу форму:

.\loadForm.ps1 -XamlPath '.\MyForm.xaml'

$xamGUI.ShowDialog() | Out-Null

Теперь если мы запустим это скрипт то увидим что наша форма появилась на экране.

2

Программирование событий формы

Мы смогли запустить нашу форму, однако пока что толку он нее очень мало, так как она мало функциональна. Нам нужна возможность определять события элементов формы (нажатие кнопок, и т.д.). Однако что бы как то обратиться к элементам формы нам нужны переменные со ссылками на объекты. Тут то нам и пригодится последний блок скрипта loadForm.ps1:

$xmlWPF.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | %{
    Set-Variable -Name ($_.Name) -Value $xamGUI.FindName($_.Name) -Scope Global
}

Этот блок делает обход всего дерева xaml и при нахождении аргументов типа Name создает переменные со ссылками на объекты (контролы) нашей формы.

Следовательно нам при создании формы нужно указывать имена (свойство Name) тех контролов, с которыми мы хотим в дальнейшем так или иначе взаимодействовать. Откроем файл нашей формы в текстовом редакторе, ISE или Visual Studio и добавим контролам имена, я дал например такие:

<Window 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Форма Powershell" Height="150" Width="525">
    <Grid>
        <Label x:Name="Label1" Content="Это форма запущеная из под Powershell" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="459" FontSize="24"/>
        <Button x:Name="btnChange" Content="Change" HorizontalAlignment="Left" Margin="352,89,0,0" VerticalAlignment="Top" Width="75"/>
        <Button x:Name="btnExit" Content="Exit" HorizontalAlignment="Left" Margin="432,89,0,0" VerticalAlignment="Top" Width="75"/>

    </Grid>
</Window>

Самой форме при этом давать имя не нужно, мы можем обращаться к форме через глобальную переменную $xamGUI.

Сохранив наш новый код XAML в файл MyForm.xaml мы можем перезапустить наш скрипт Main.ps1, после чего увидим, что скрипт loadForm.ps1 создал переменные $btnChange, $btnExit.

3

Обратите внимание на тип переменной $btnChange – это [Button], так что это действительно ссылки на объекты формы. Теперь мы можем добавить код для событий наших контролов. Например для того что бы корректно работала кнопка Exit, мы должны закрывать форму при нажатии на кнопку. Это достигается через добавление кода для события Click:

.\loadForm.ps1 -XamlPath '.\MyForm.xaml'

$btnExit.Add_Click({
    $xamGUI.Close()
})

$xamGUI.ShowDialog() | Out-Null

Ну а что бы кнопка $btnChange меняла текст контрола Label1 добавим и ей код для события Click:

.\loadForm.ps1 -XamlPath '.\MyForm.xaml'

$btnExit.Add_Click({
    $xamGUI.Close()
})

$btnChange.Add_Click({
    $Label1.Content = "Этот текст изменен кнопкой "+$btnChange.Name
})

$xamGUI.ShowDialog() | Out-Null

Теперь запустив наш скрипт мы можем убедиться в том, что наши кнопки теперь работают.

4

Собственно это все на сегодня. Если будет интерес к этой теме – буду рад написать еще несколько статей.

Всего комментариев: 3 Комментировать

  1. Alexander /

    Круто, спасибо за статью.очень позновательно,буду разбираться,хотелось бы кое какие рутинные операции автоматизировать вы дали толчок к этому!

  2. Ant /

    Sapien делает просто шикарную вещь - PowerShell Studio, правда она денег стоит.
    http://www.sapien.com/software/powershell_studio

  3. vladimir /

    Как обработать закрытие окна по крестику вверху? При закрытии формы чтобы отрабатывалось определенное действие не использовать конпку EXIT?

Добавить комментарий