🐶
PowerShellにも設定画面がほしい
何言ってやがんだとか思われるんでしょーがおっさんは本気です。
テキストで設定シコシコ書くのはさ、もーDOS時代にとっくに飽きてんだわ。おっさん。
ってわけで面倒事はPropertyGridに全部放り投げます。おいコラ、働けよ、計算機。
ちなみにPropertyGridってのはソフト屋なら某IDEで絶対見たことあるコレのコト
本題
いやもーここ数年弄ってない動的フォーム生成とか中々脳にキますな
function ShowSettingDialog {
param (
[Parameter(Mandatory = $true)] [string] $Title,
[Parameter(Mandatory = $true)] [System.Object] $Setting
)
begin {}
process {
# フォーム生成
$frmMain = New-Object System.Windows.Forms.Form -Property @{
Text = $Title # タイトル
StartPosition = 'CenterScreen' # 表示位置
Size = New-Object System.Drawing.Size(480,320)
Padding = New-Object System.Windows.Forms.Padding(5)
}
$tlpMain = New-Object System.Windows.Forms.TableLayoutPanel -Property @{
Dock = [System.Windows.Forms.DockStyle]::Fill
RowCount = 2
}
$pnlBody = New-Object System.Windows.Forms.Panel -Property @{
Dock = [System.Windows.Forms.DockStyle]::Fill
}
$grdProp = New-Object System.Windows.Forms.PropertyGrid -Property @{
Dock = [System.Windows.Forms.DockStyle]::Fill
SelectedObject = $Setting
}
$pnlTail = New-Object System.Windows.Forms.Panel -Property @{
Dock = [System.Windows.Forms.DockStyle]::Fill
}
$btnOK = New-Object System.Windows.Forms.Button -Property @{
Dock = [System.Windows.Forms.DockStyle]::Right
Size = New-Object System.Drawing.Size(128, 0) # ボタン巾のみ指定可能
Text = "OK"
UseVisualStyleBackColor = $true
DialogResult = [Windows.Forms.DialogResult]::OK
}
$btnCancel = New-Object System.Windows.Forms.Button -Property @{
Dock = [System.Windows.Forms.DockStyle]::Right
Size = New-Object System.Drawing.Size(128, 0) # ボタン巾のみ指定可能
Text = "Cancel"
UseVisualStyleBackColor = $true
DialogResult = [Windows.Forms.DialogResult]::Cancel
}
$null = $pnlBody.Controls.Add($grdProp)
$null = $pnlTail.Controls.Add($btnOK)
$null = $pnlTail.Controls.Add($btnCancel)
$null = $tlpMain.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 100)))
$null = $tlpMain.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 50))) # ボタン高さはコレ
$null = $tlpMain.Controls.Add($pnlBody, 0, 0)
$null = $tlpMain.Controls.Add($pnlTail, 0, 1)
$null = $frmMain.Controls.Add($tlpMain)
# フォーム表示
$null = $frmMain.ShowDialog()
return $frmMain.DialogResult
}
end {}
}
使い方
で、まぁコレをこんな感じで使うんですわ
class AppSettings {
[System.ComponentModel.Description("名前")]
[string]$AppName
[int]$Version
[bool]$AutoUpdate
[string]$LogFilePath
[System.Diagnostics.SourceLevels]$LogLevel
}
$settings = [AppSettings]@{
AppName = "My Application"
Version = 1
AutoUpdate = $true
LogFilePath = "C:\app.log"
LogLevel = [System.Diagnostics.SourceLevels]::Information
}
$ret = ShowSettingDialog "Title" $settings
if ($ret -eq "OK") {
$settings
}
ってわけでコレが冒頭のアレになります
つまりクラス定義して変数ブチ込んであとは.Net様に全てお任せ
説明
まんま使える属性はこの辺見てChatGPTなり何なりにお願いすれば大体オッケーです
ただしカスタム属性は単発利用では苦労に見合わない感じなんで避けたほうがいいかと
ところでコイツ、現実的にはConvertFrom-Jsonで取ってきた設定を編集すんですが
何も考えずConvertFrom-Jsonすると戻り値はPSCustomObjectになってますんで
そのまま突っ込まずに一度クラスに変換してください
面倒なんでおっさんはとりあえず以下のように強引にリフレクションで変換してますが
多分この話と同じでコンストラクタ作っちゃえばいいんでしょうねぇ...
何時か試す
function ConvertFromPSCO {
param (
[Parameter(Mandatory = $true)] [System.Type] $Type,
[Parameter(Mandatory = $true)] [PSCustomObject] $Data
)
begin {}
process {
$inst = New-Object -TypeName $Type.FullName
$Data.PSObject.Properties | ForEach-Object {
$PSCOName = $_.Name
$PSCOData = $_.Value
$InstProp = $Type.GetProperty($PSCOName)
if ($null -ne $InstProp) {
if ($InstProp.PropertyType.IsPrimitive) {
$inst.($InstProp.Name) = $PSCOData
} elseif (($InstProp.PropertyType.IsEnum)) {
$inst.($InstProp.Name) = $PSCOData
} elseif (($InstProp.PropertyType.Name -eq "string") -or ($InstProp.PropertyType.Name -eq "datetime") -or ($InstProp.PropertyType.Name -eq "decimal")) {
$inst.($InstProp.Name) = $PSCOData
} elseif ($InstProp.PropertyType.IsArray) {
$list = @()
foreach ($elm in $PSCOData) {
if ($InstProp.PropertyType.GetElementType().IsPrimitive) {
$list += $elm
} elseif (($InstProp.PropertyType.GetElementType().IsEnum)) {
$list += $elm
} elseif (($InstProp.PropertyType.GetElementType().Name -eq "string") -or ($InstProp.PropertyType.GetElementType().Name -eq "datetime") -or ($InstProp.PropertyType.GetElementType().Name -eq "decimal")) {
$list += $elm
} else {
$list += ConvertFromPSCO -Type $InstProp.PropertyType.GetElementType() -Data $elm
}
}
$inst.($InstProp.Name) = $list
} else {
$inst.($InstProp.Name) = ConvertFromPSCO -Type $InstProp.PropertyType.GetElementType() -Data $PSCOData
}
}
}
return $inst
}
end {}
}
Discussion