【PowerShellでGUI 7】DataGridView 応用編2 画像表示列
前回に引き続き、、PowerShellで作成するWindwos Formアプリの部品データグリッドビューの応用編で、今回はデータグリッドビューの中に
・画像を表示する列
を設定する方法です。
DataGridViewImageColumn
今回のサンプルアプリの画面イメージはこちら。
今回も、前々回作成した簡易なエクスプローラーライクなものをベースにしています。

そして上記画面のソースコードがこちら
using namespace System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
[Application]::EnableVisualStyles()
# フォーム
$frame = New-Object Form -Property @{
Text = 'Sample App'
Size = New-Object Drawing.Size(650, 600)
MaximizeBox = $false
FormBorderStyle = 'FixedDialog'
Font = New-Object Drawing.Font('Meiryo UI', 8.5)
}
# 表示対象フォルダー設定用テキストボックス
$tbxFolder = New-Object TextBox -Property @{
Location = New-Object Drawing.Point(20, 20)
Width = 400
ReadOnly = $True
}
$frame.Controls.Add($tbxFolder)
# フォルダー選択ダイアログ表示ボタン
$btnRef = New-Object Button -Property @{
Location = New-Object Drawing.Point(425, 20)
Text = '...'
Width = 30
}
$frame.Controls.Add($btnRef)
$fbdRef = New-Object FolderBrowserDialog -Property @{
ShowNewFolderButton = $false
}
# データグリッドビュー
$DataGridV = New-Object DataGridView -Property @{
Location = New-Object Drawing.Point(20, 50)
Size = New-Object Drawing.Size(600, 500)
AutoSizeColumnsMode = "AllCells"
ReadOnly = $True
AllowUserToAddRows = $false
ColumnCount = 3
RowHeadersVisible = $false
MultiSelect = $false
SelectionMode = 'FullRowSelect'
}
$DataGridV.ColumnHeadersDefaultCellStyle.Alignment = 'MiddleCenter'
$DataGridV.RowTemplate.Height = 150
# 各列の情報設定
$DataGridV.Columns[0].Name = "名前"
$DataGridV.Columns[1].Name = "更新日時"
$DataGridV.Columns[1].DefaultCellStyle.Format = 'yyyy/MM/dd HH:mm:ss'
$DataGridV.Columns[2].Name = "サイズ"
$DataGridV.Columns[2].DefaultCellStyle.Format = '#,##0 KB'
$DataGridV.Columns[2].DefaultCellStyle.Alignment = 'MiddleRight'
# 画像イメージ列
$colImage = New-Object DataGridViewImageColumn -Property @{
Name = "Image"
ImageLayout = 'Zoom'
Width = 250
AutoSizeMode = 'Fill'
}
$colImage.DefaultCellStyle.NullValue = $null
$colImage.DefaultCellStyle.Padding = New-Object Padding(3)
$colImage.DefaultCellStyle.Alignment = 'MiddleCenter'
[void]$DataGridV.Columns.Add($colImage)
# 表示可能な画像形式の拡張子一覧を配列に格納
$ImageExts = @()
foreach ($exts in ([System.Drawing.Imaging.ImageCodecInfo]::GetImageDecoders()).FilenameExtension){
$ImageExts += ($exts.Replace('*','') -split ';')
}
# DataGridViewに値を設定する処理
$scrbSetGrid = {
$DataGridV.Rows.Clear()
$files = (Get-ChildItem $tbxFolder.Text | Where-Object { $_.Extension -in $ImageExts })
foreach ($fi in $files){
switch ($fi.Length) {
{$_ -lt 1024} { $size = 1 }
Default {$size = ($_ / 1024)}
}
# 画像の回転情報を取得
$bmporg = New-Object System.Drawing.Bitmap($fi.FullName)
$rpi = ($bmporg.PropertyItems | Where-Object { $_.ID -eq 0x0112 })
$rft = [System.Drawing.RotateFlipType]::RotateNoneFlipNone
if (($rpi | Measure-Object).Count -gt 0){
switch ($rpi.Value[0]) {
3 { $rft = [System.Drawing.RotateFlipType]::Rotate180FlipNone }
6 { $rft = [System.Drawing.RotateFlipType]::Rotate90FlipNone }
8 { $rft = [System.Drawing.RotateFlipType]::Rotate270FlipNone }
}
}
$bmporg.Dispose()
# 開いたセッションを残さないために、FileStreamで読み込んで最後にClose
$fs = New-Object System.IO.FileStream($fi.FullName, 'Open', 'Read')
$bmpclone = New-Object System.Drawing.Bitmap($fs)
$bmpclone.RotateFlip($rft)
$fs.Close()
$DataGridV.Rows.Add($fi.Name, $fi.LastWriteTime, $size, $bmpclone)
}
}
$btnRef.Add_Click({
if ($fbdRef.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK){
$tbxFolder.Text = $fbdRef.SelectedPath
. $scrbSetGrid
}
})
# ダブルクリック時の処理
$DataGridV.Add_CellDoubleClick({
if ($_.RowIndex -ge 0){
Start-Process -FilePath (Join-Path $tbxFolder.Text $DataGridV.Rows[$_.RowIndex].Cells[0].Value)
}
})
$frame.Controls.Add($DataGridV)
$frame.ShowDialog()
解説
60~69行目が画像を表示する列の定義をしている部分になります。
$colImage = New-Object DataGridViewImageColumn -Property @{
Name = "Image"
ImageLayout = 'Zoom'
Width = 250
AutoSizeMode = 'Fill'
}
$colImage.DefaultCellStyle.NullValue = $null
$colImage.DefaultCellStyle.Padding = New-Object Padding(3)
$colImage.DefaultCellStyle.Alignment = 'MiddleCenter'
[void]$DataGridV.Columns.Add($colImage)
列のオブジェクトをDataGridViewImageColumnクラスとして定義し、定義したオブジェクトをデータグリッドビューに追加するという流れになります。
設定しているプロパティについて、特徴的なものを以下に記載しておきます。
| ImageLayout | 画像の表示の仕方の設定で、既定値(Nomal)だと等倍表示されてしまうため、セルの大きさに合わせて自動的に拡大/縮小されて表示されるようにZoomを設定しています。 |
| AutoSizeMode | 列幅について、他のセルの幅は表示内容によって自動調整ように、DataGridViewのプロパティで設定していますが、画像表示をする列幅は、DataGridViewの幅いっぱいいっぱいを使って表示できるように、Fillで設定しています。 |
| DefaultCellStyle.Padding | 画像を表示したときに、上下のセルとの間に多少は余白が欲しかったので、設定しています。 |
72~75行目では、DataGridViewImageColumnに表示可能なフォーマット(拡張子)の一覧を取得しています。
GetImageDecodersでは、FilenameExtensionが
*.BMP;*.DIB;*.RLE *.JPG;*.JPEG;*.JPE;*.JFIF *.GIF *.EMF *.WMF *.TIF;*.TIFF *.PNG *.ICO
といった形式で取得されてきますが、 Get-ChildItem の結果の Extension の項目に対して、-in 演算子で絞り込みを行いたいので、取得結果から’*’は削除(Replace('*',''))し、’;’で配列に分割(-split ';')したものを配列に格納しています。
86行目~102行目がグリッドビューに画像を表示させるための処理になってて、まずは前半の86~96行目が、EXIFのOrientationタグの情報を取得し、後半でRotateFlipにより画像を回転させるための設定値を設定している部分です。
$bmporg = New-Object System.Drawing.Bitmap($fi.FullName)
$rpi = ($bmporg.PropertyItems | Where-Object { $_.ID -eq 0x0112 })
$rft = [System.Drawing.RotateFlipType]::RotateNoneFlipNone
if (($rpi | Measure-Object).Count -gt 0){
switch ($rpi.Value[0]) {
3 { $rft = [System.Drawing.RotateFlipType]::Rotate180FlipNone }
6 { $rft = [System.Drawing.RotateFlipType]::Rotate90FlipNone }
8 { $rft = [System.Drawing.RotateFlipType]::Rotate270FlipNone }
}
}
$bmporg.Dispose()
後半の99行目~102行目がDataGridViewImageColumnに表示する画像を設定している部分になります。
$fs = New-Object System.IO.FileStream($fi.FullName, 'Open', 'Read')
$bmpclone = New-Object System.Drawing.Bitmap($fs)
$bmpclone.RotateFlip($rft)
$fs.Close()
DataGridViewImageColumnに紐づけるSystem.Drawing.Bitmapオブジェクトの定義の仕方の部分がポイントです。
前半と同じようにファイル名を指定してSystem.Drawing.Bitmapオブジェクトを定義すると、Disposeで破棄するまで、対象の画像ファイルをロックしてしまうのですが、System.Drawing.BitmapオブジェクトをDisposeしてしまうと、データグリッドビューで画像が表示できなくなってしまいます。
そこで、画像ファイルを一旦FileStreamに読み込んで、読み込んだFileStreamを使ってSystem.Drawing.Bitmapオブジェクトを定義し、その後、FileStreamはCloseすることで、画像ファイルへのロックが残らずにデータグリッドビューに画像が表示されることになります。
今回はここまで。
次回何を記載しようかはちょっと悩み中です。。。
以上、参考になれば幸いです。

