Itsukaraの日記

最新IT技術を勉強・実践中。最近はDeep Learningに注力。

PowerShellでGUI: 可変個数RadioButton実現方法(別法)

VisualStudioでPowerShell用GUIを作成する方法 - Itsukaraの日記」という記事で、PowerShellGUI作成用にVisualStudioを使う方法を紹介し、可変個数のRadioButtonを表示する方法も書きました。しかし、可変個数のRadioButtonは、もっと良い実現方法を考えたので、紹介します。

とても簡単で、GUIの構造を表すXAML文字列を書き換えるだけです。下記にプログラム例を示します。下記の「add_items_to_xaml」という関数が、XAMLを書き換える処理です。汎用性があるので、RadioButton以外でも使えると思います。

  • 可変個数のRadioButtonを作る例:
$ErrorActionPreference = "stop"
Set-PSDebug -Strict
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="xaml test #2"
    Width="170pt" Height="170pt">
  <StackPanel>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
      <StackPanel x:Name="country">
      </StackPanel>
      <Label Content="  " FontSize="10pt"/>
      <StackPanel x:Name="fruit">
      </StackPanel>
    </StackPanel>
    <Label x:Name="msg"  Content="  " FontSize="10pt"/>
    <Button x:Name="btn" Content="Push Me"  FontSize="10pt" />
  </StackPanel>
</Window>
'@

# 項目一覧を$xamlに追加
# $head : 置換前文字列
# $add  : 置換後文字列のひな型
# $placeholder: $add中のプレスホルダー文字列
# $items: 項目一覧(空白で区切られたワード列)
function add_items_to_xaml($head, $add, $placeholder, $items) {
    $list = $items.split(" ")
    [array]::Reverse($list)
    foreach ($item in $list) {
        $add_item = $add.Replace($placeholder, $item)
        $global:xaml = $global:xaml.Replace($head, $add_item)
    }
}

# countryへの項目追加
$country_head = @'
      <StackPanel x:Name="country">
'@
$country_add = @'
      <StackPanel x:Name="country">
        <RadioButton Content="PlaceHolder"></RadioButton>
'@
$countries = "U.S.A China Japan Germany France England Russia"
add_items_to_xaml $country_head $country_add "PlaceHolder" $countries

# fruitへの項目追加
$fruit_head = @'
      <StackPanel x:Name="fruit">
'@
$fruit_add = @'
      <StackPanel x:Name="fruit">
        <RadioButton Content="PlaceHolder"></RadioButton>
'@
$fruits = "Apple Orange Grape"
add_items_to_xaml $fruit_head $fruit_add "PlaceHolder" $fruits

$xaml

$xaml = [xml]$xaml
$reader = New-Object System.Xml.XmlNodeReader $xaml
$frm = [System.Windows.Markup.XamlReader]::Load($reader)
$country = $frm.FindName("country")
$fruit = $frm.FindName("fruit")
$msg = $frm.FindName("msg")

# 選択された項目を返す
function getChecked($parent) {
    $checked = ""
    foreach ($child in $parent.Children) {
        if ($child.isChecked) {
            $checked += $child.Content
        }
    }
    return $checked
}

# ボタンクリック時の処理
function clicked {
    $selectedCountry = getChecked $country
    $selectedfruit  = getChecked $fruit
    $msg.content = "Country=" + $selectedCountry + "`n" + "Fruit=" +  $selectedfruit
}

$btn = $frm.FindName("btn")
$btn.Add_Click({clicked})
$result = $frm.ShowDialog()
  • 書換後のXAML(上記プログラムで出力したもの)
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="xaml test #2"
    Width="170pt" Height="170pt">
  <StackPanel>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
      <StackPanel x:Name="country">
        <RadioButton Content="U.S.A"></RadioButton>
        <RadioButton Content="China"></RadioButton>
        <RadioButton Content="Japan"></RadioButton>
        <RadioButton Content="Germany"></RadioButton>
        <RadioButton Content="France"></RadioButton>
        <RadioButton Content="England"></RadioButton>
        <RadioButton Content="Russia"></RadioButton>
      </StackPanel>
      <Label Content="  " FontSize="10pt"/>
      <StackPanel x:Name="fruit">
        <RadioButton Content="Apple"></RadioButton>
        <RadioButton Content="Orange"></RadioButton>
        <RadioButton Content="Grape"></RadioButton>
      </StackPanel>
    </StackPanel>
    <Label x:Name="msg"  Content="  " FontSize="10pt"/>
    <Button x:Name="btn" Content="Push Me"  FontSize="10pt" />
  </StackPanel>
</Window>
  • 画面表示例:

f:id:Itsukara:20170324210102p:plain

あとがき

今回の件に関して、Webで色々と見つけたのですが、よい方法が見つからず、結局、自分で考えました。でも、後から考えると当たり前の内容であり、Webで色々調べた労力が無駄でした。