tech.guitarrapc.cóm

Technical updates

PowerShellでWPFしたい Part2 - XAML編

記事を作っておきながら時間が経ちました。

今回はPowerShellでWPFの第二弾記事です。 前回の記事はこちら

PowerShellでWPFしたい Part1 – WPK編

さてPowerShell 3.0では、実はなんちゃってXAMLも書けちゃいます。 なんちゃってなのは、私が未熟なだけですが…とにかくWinFormsなんかと比べるなってぐらい扱いが楽です。 前回のWPKとは一風趣が変わりますがよろしければ… ====

Xaml実行にはとりあえず何が必要?

まずは、Add-Typeでpresentationframeworkを読み込みが必須です。

Add-Type -AssemblyName presentationframework

続いてXAML本文ですね!

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Test XAML / Show System Time" 
    FontFamily="consolas"
    Height="350" 
    Width="400"
    Margin="5"
    >
    <StackPanel>
        <Button 
            Name="buttonGet" 
            Content="Click me to show System time!!"
            FontSize="20"
            Height="60" 
            Margin="5"
        />
    </StackPanel>
</Window>

System.Xml.XmlNodeReaderでxamlを読み込んで…!

$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$window=[Windows.Markup.XamlReader]::Load( $reader )

コントロールを探して…。

$buttonGet = $window.FindName("buttonGet")

イベント動作を記述します。

$buttonGet_clicked = $buttonGet.add_Click
        $buttonGet_clicked.Invoke({ 
            $textboxTime.Text = Get-Date -Format "yyyy/MM/dd hh:mm:ss"
        })

簡単ですね!

サンプル

サクッと簡単にやってみます。

  1. buttonコントロールを押すとTextBoxに現在時刻が表示
  2. ListBoxにはItem aとItem bを追加
  3. OKを推すと、XAMLで描いた画面が終了
  4. 終了時にTextBoxに入力されている時刻、選択されているListBoxアイテムがPowershellにオブジェクトとして帰ってくる

ではサンプルを順番に見てみます。 XAMLを書いて変数に入れておきます。

$loadXaml =@'
        <Window 
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Test XAML / Show System Time" 
            FontFamily="consolas"
            Height="350" 
            Width="400"
            Margin="5"
        >
            <StackPanel>
                <Button 
                    Name="buttonGet" 
                    Content="Click me to show System time!!"
                    FontSize="20"
                    Height="60" 
                    Margin="5"
                />
                <Label 
                    Name="textboxLabel" 
                    Content="↓ System Time ↓" 
                    FontSize="15"
                    Height="30" 
                    Margin="3"
                    HorizontalAlignment="Center"
                />
                <TextBox 
                    Name="textboxTime" 
                    Text="" 
                    FontSize="20"
                    Background="#00AA88"
                    Height="25" 
                    Margin="5"
                />
                <ListBox 
                    Name="listbox" 
                    FontSize="20"
                    Background="#AAAA88"
                    Height="60" 
                    Margin="5"
                    SelectionMode="Single"
                />
                <Button
                    Name="buttonOK" 
                    Content="OK" 
                    FontSize="14"
                    Foreground="#FFFFFF"
                    Background="#505050"
                    Height="40" 
                    Margin="5"
                />
            </StackPanel>
        </Window>
'@

続いて、Xamlに対応する要素を制御するためのfunctionをツラツラっと

function Show-XAMLSystemTime{

    [CmdletBinding(  
        SupportsShouldProcess   = $false,
        ConfirmImpact       = "none",
        DefaultParameterSetName = ""
    )]

    param
    (
        [Parameter(
        HelpMessage = "Load binding Xaml you defined beforehands",
        Position = 0,
        Mandatory = $true,
        ValueFromPipeline = $true,
        ValueFromPipelineByPropertyName = $true
        )]
        [ValidateNotNullOrEmpty()]
        [string]
        $loadXaml,

        [Alias("PassThru")]
        [switch]
        $PassThrough
    )

    begin
    {
        try
        {
            # Add-Typeでアセンブリをロードする
            Add-Type -AssemblyName presentationframework
        }
        catch
        {
            #Alread Type added.
        }

        try
        {
            [xml]$xaml = $loadXaml
        }
        catch
        {
        }
    }

    process
    {
        $reader=(New-Object System.Xml.XmlNodeReader $xaml)
        $window=[Windows.Markup.XamlReader]::Load( $reader )
 
        $buttonGet = $window.FindName("buttonGet")
        $textboxTime = $window.FindName("textboxTime")
        $listBoxItem = $window.FindName("listbox")
        $buttonOK = $window.FindName("buttonOK")
 
        #buttonGet event to show current datetime
        $buttonGet_clicked = $buttonGet.add_Click
        $buttonGet_clicked.Invoke({ 
            $textboxTime.Text = Get-Date -Format "yyyy/MM/dd hh:mm:ss"
        })

        #ListBox event
        [void]$listBoxItem.Items.Add("Item a")
        [void]$listBoxItem.Items.Add("Item b")

        #buttonOK event to close window
        $buttonOK_clicked = $buttonOK.add_Click
        $buttonOK_clicked.Invoke({ 
        $Window.close()})
    }

    end
    {
        $window.ShowDialog() | out-null

        #return to Powershell
        return [PSCustomObject]@{
            time=$textboxTime.Text;
            listItem=$listboxItem.SelectedItem
        }

    }
}

あとは、Xamlをfunctionに渡して実行します。

Show-XAMLSystemTime -loadXaml $loadXaml

ででーんと、XAMLが読み込まれ、このような画面が現れます。

Buttonをクリックすると…TextBoxに時間が表示されます。 ListBox Itemもハイライト選択できますね。

 

OKで完了すると、PowerShellホストにオブジェクトが返ってきます。

time                              listItem
----                              --------
2013/03/15 04:23:27               Item a

勿論、funtion実行時に変数へ代入しておけば後から再利用もできますね!

$WPFresult = Show-XAMLSystemTime -loadXaml $loadXaml

$WPFresult

$WPFresult.listItem

$WPFresult.time

キッチリ変数に入ってますね。

time                              listItem
----                              --------
2013/03/15 04:23:27               Item a

Item a

2013/03/15 04:23:27

 

まとめ

どうでしょうか。 WPKや次回のShow-UIに比べるとPowerShellらしくない = パイプを多用する記述ではありませんね。

ただ、プロパティにアクセスしてコントロールするのは楽です。 個人的には、結構好きですよ!ざむるざむる

おまけ : コード全文

function Show-XAMLSystemTime{

    [CmdletBinding(  
        SupportsShouldProcess   = $false,
        ConfirmImpact       = "none",
        DefaultParameterSetName = ""
    )]

    param
    (
        [Parameter(
        HelpMessage = "Enter tbind xaml @`...`@ to load.",
        Position = 0,
        Mandatory = $true,
        ValueFromPipeline = $true,
        ValueFromPipelineByPropertyName = $true
        )]
        [ValidateNotNullOrEmpty()]
        [string]
        $loadXaml,
    )

    begin
    {
        try
        {
            # Add-Typeでアセンブリをロードする
            Add-Type -AssemblyName presentationframework
        }
        catch
        {
            #Alread Type added.
        }

        try
        {
            [xml]$xaml = $loadXaml
        }
        catch
        {
        }
    }

    process
    {
        $reader=(New-Object System.Xml.XmlNodeReader $xaml)
        $window=[Windows.Markup.XamlReader]::Load( $reader )
 
        $buttonGet = $window.FindName("buttonGet")
        $textboxTime = $window.FindName("textboxTime")
        $listBoxItem = $window.FindName("listbox")
        $buttonOK = $window.FindName("buttonOK")
 
        #buttonGet event to show current datetime
        $buttonGet_clicked = $buttonGet.add_Click
        $buttonGet_clicked.Invoke({ 
            $textboxTime.Text = Get-Date -Format "yyyy/MM/dd hh:mm:ss"
        })

        #ListBox event
        [void]$listBoxItem.Items.Add("Item a")
        [void]$listBoxItem.Items.Add("Item b")

        #buttonOK event to close window
        $buttonOK_clicked = $buttonOK.add_Click
        $buttonOK_clicked.Invoke({ 
        $Window.close()})
    }

    end
    {
        $window.ShowDialog() | out-null

        #return to Powershell
        return [PSCustomObject]@{
            time=$textboxTime.Text;
            listItem=$listboxItem.SelectedItem
        }

    }
}


$loadXaml =@'
        <Window 
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Test XAML / Show System Time" 
            FontFamily="consolas"
            Height="350" 
            Width="400"
            Margin="5"
        >
            <StackPanel>
                <Button 
                    Name="buttonGet" 
                    Content="Click me to show System time!!"
                    FontSize="20"
                    Height="60" 
                    Margin="5"
                />
                <Label 
                    Name="textboxLabel" 
                    Content="↓ System Time ↓" 
                    FontSize="15"
                    Height="30" 
                    Margin="3"
                    HorizontalAlignment="Center"
                />
                <TextBox 
                    Name="textboxTime" 
                    Text="" 
                    FontSize="20"
                    Background="#00AA88"
                    Height="25" 
                    Margin="5"
                />
                <ListBox 
                    Name="listbox" 
                    FontSize="20"
                    Background="#AAAA88"
                    Height="60" 
                    Margin="5"
                    SelectionMode="Single"
                />
                <Button
                    Name="buttonOK" 
                    Content="OK" 
                    FontSize="14"
                    Foreground="#FFFFFF"
                    Background="#505050"
                    Height="40" 
                    Margin="5"
                />
            </StackPanel>
        </Window>
'@


$WPFresult = Show-XAMLSystemTime -loadXaml $loadXaml
$WPFresult
$WPFresult.listItem
$WPFresult.time