none
(VB)Listboxにパスの代わりにサムネイル画像を表示する方法 RRS feed

  • 質問

  • はじめて質問しますVB初心者です。よろしくお願いします。

    今回タイトルの件について、自力では解決することができなかったため質問させていただきます。

    現在Windows フォーム アプリケーション(VB)の開発にてlistboxに、あるフォルダー内の画像ファイル(*.JPG)を取り込み、それぞれの絶対パスを表示しています。次にそれぞれのパスからD&Dによってpictureboxにパス先の画像を表示していく、といった仕組みのアプリケーションを制作しています。

    そこで質問なのですが、現在listboxに表示しているパスを、パスではなくパス先の画像をサムネイル画像として表示することはできるのでしょうか。可能だとしたら、その方法をご教授願います。

    不可能であればlistbox以外で代用できる別の方法を教えていただけると幸いです。

    2017年9月17日 5:21

回答

  • ほかのスレッド と同様に、VBA フォーラムですが、VB.NET という前提でよいですかね?

    目指しているのは、ListBox 内にファイル名一覧ではなく、サムネイル画像一覧にしたいということですか?
    たとえば、こういう自力の描画の応用になります。
    https://dobon.net/vb/dotnet/control/lbownerdraw.html

    個人的には ListView にした方が向いている気はするのですが…。
    http://www.atmarkit.co.jp/ait/articles/0508/12/news091.html

    • 回答としてマーク 熊テン 2017年9月18日 8:10
    2017年9月17日 7:17
  • DrawItemイベントの中で各アイテムに対応する画像を描画することができます。

    Public Class Form1
    
    	Private WithEvents listBox1 As New ListBox
    	Private WithEvents listBox2 As New ListBox
    	Private WithEvents listBox3 As New ListBox
    
    	''' <summary>ドラッグ元のリストボックス</summary>
    	Private sourceListbox As ListBox
    
    	Sub New()
    
    		' この呼び出しはデザイナーで必要です。
    		InitializeComponent()
    
    		' InitializeComponent() 呼び出しの後で初期化を追加します。
    
    		Dim table As New TableLayoutPanel
    		table.RowCount = 1
    		table.ColumnCount = 3
    		For i = 1 To table.ColumnCount
    			table.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / table.ColumnCount))
    		Next
    
    		table.Dock = DockStyle.Fill
    
    		Me.Controls.Add(table)
    
    		Dim listBoxies = New ListBox() {listBox1, listBox2, listBox3}
    
    		For i = 0 To 2
    			Dim listBox As ListBox = listBoxies(i)
    			listBox.Dock = DockStyle.Fill
    
    			table.SetColumn(listBox, i)
    			table.Controls.Add(listBox)
    
    			listBox.DrawMode = DrawMode.OwnerDrawFixed '固定高さの描画をする場合
    			listBox.ItemHeight = 100 '一要素の高さ
    			listBox.IntegralHeight = False 'ListBoxの高さが変化しないように
    
    			listBox.AllowDrop = True 'ドロップ許可
    		Next
    	End Sub
    
    	Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    		Dim dlg As New FolderBrowserDialog
    		dlg.Description = "最初に画像を読み込むディレクトリを選択してください"
    		If (dlg.ShowDialog() = Windows.Forms.DialogResult.OK) Then
    			LoadImages(dlg.SelectedPath, listBox1)
    		End If
    	End Sub
    
    	''' <summary>
    	''' ListBoxのアイテムを自分で描画する
    	''' </summary>
    	Private Sub listBox1_DrawItem(sender As Object, e As DrawItemEventArgs) Handles listBox1.DrawItem, listBox2.DrawItem, listBox3.DrawItem
    		Dim listBox As ListBox = sender
    		If (0 <= e.Index AndAlso e.Index < listBox.Items.Count) Then
    			Dim item As Item = listBox.Items(e.Index)
    			Dim filePath As String = item.FilePath
    			Dim img As Image = item.Image
    			Dim margin = 5
    
    			e.DrawBackground() '選択色のため背景描画
    
    			'描画の大きさ計算
    			Dim rect As Rectangle = New Rectangle(e.Bounds.Left + margin, e.Bounds.Top + margin, e.Bounds.Width - margin * 2, e.Bounds.Height - margin * 2)
    			Dim scale = img.Height / rect.Height
    			Dim w = img.Width / scale
    			rect.Width = w
    
    			'画像を描画
    			e.Graphics.DrawImage(img, rect)
    		End If
    	End Sub
    
    
    	Private Sub listBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles listBox1.MouseDown, listBox2.MouseDown, listBox3.MouseDown
    		Dim listBox As ListBox = sender
    		'マウス位置からアイテムを探す
    		Dim index As Integer = listBox.IndexFromPoint(e.Location)
    		If (0 <= index AndAlso index < listBox.Items.Count) Then
    			'ListBoxのアイテムが選択された場合ドラッグを開始
    			Dim item = listBox.Items(index)
    			Dim items As New List(Of Item) '将来の複数移動に備えてドラッグ中のアイテムをListに入れてみる
    			For Each item In listBox.SelectedItems
    				items.Add(item)
    			Next
    
    			sourceListbox = listBox	'ドラッグ開始したListBoxを登録
    			listBox.DoDragDrop(items, DragDropEffects.Move)	'選択されたアイテムをドラッグ開始
    
    		End If
    	End Sub
    
    	''' <summary>
    	''' ドロップできるかどうかの判定
    	''' </summary>
    	Private Sub listBox1_DragEnter(sender As Object, e As DragEventArgs) Handles listBox1.DragEnter, listBox2.DragEnter, listBox3.DragEnter
    		Dim targetListBox As ListBox = sender
    		If (e.Data.GetDataPresent(GetType(List(Of Item)))) Then
    			'Itemがドラッグされている場合
    			If targetListBox Is sourceListbox Then 'ドラッグ開始とは別のListBoxにドロップしようとしているなら
    				e.Effect = DragDropEffects.None '移動不許可
    			Else
    				e.Effect = DragDropEffects.Move '移動を許可
    			End If
    		ElseIf (e.Data.GetDataPresent(DataFormats.FileDrop)) Then
    			'エクスプローラーなどからファイルがドラッグされている場合
    			e.Effect = DragDropEffects.Copy
    		Else
    			e.Effect = DragDropEffects.None
    		End If
    
    	End Sub
    
    	Private Sub listBox_DragDrop(sender As Object, e As DragEventArgs) Handles listBox1.DragDrop, listBox2.DragDrop, listBox3.DragDrop
    		Dim targetListBox As ListBox = sender
    		If (e.Data.GetDataPresent(GetType(List(Of Item)))) Then
    			If (sourceListbox IsNot Nothing) Then
    				Dim items As List(Of Item) = CType(e.Data.GetData(GetType(List(Of Item))), List(Of Item))
    				If (items IsNot Nothing AndAlso items.Count > 0) Then
    					'スクリーン座標からListBoxの座標に直してマウスカーソルの位置のアイテムを探す
    					Dim p As Point = targetListBox.PointToClient(Control.MousePosition)
    					Dim targetIndex = targetListBox.IndexFromPoint(p)
    					If (0 <= targetIndex AndAlso targetIndex < targetListBox.Items.Count) Then
    						'ドロップ先のアイテムがある場合
    						Dim targetItem As Item = targetListBox.Items(targetIndex)
    
    						'相手と入れ替え
    						targetListBox.Items.RemoveAt(targetIndex)
    						targetListBox.Items.Insert(targetIndex, items(0))
    
    						Dim sourceIndex As Integer = sourceListbox.SelectedIndex
    						sourceListbox.Items.RemoveAt(sourceIndex)
    						sourceListbox.Items.Insert(sourceIndex, targetItem)
    
    					Else
    						'ドロップ先にアイテムが無い場合
    						For Each item In items
    							'ドラッグ元から消してドラッグ先に追加
    							sourceListbox.Items.Remove(item)
    							targetListBox.Items.Add(item)
    						Next
    					End If
    
    					items.Clear()
    				End If
    			End If
    
    		ElseIf (e.Data.GetDataPresent(DataFormats.FileDrop)) Then
    			'エクスプローラーなどからファイルがドラッグされている場合
    			'ディレクトリの画像を読み直す
    			Dim files As String() = CType(e.Data.GetData(DataFormats.FileDrop), String())
    			If (files.Count = 1 AndAlso System.IO.Directory.Exists(files(0))) Then
    				LoadImages(files(0), targetListBox)
    			End If
    		End If
    	End Sub
    
    
    
    	Private Shared Sub LoadImages(ByVal dir As String, ByVal listBox As ListBox)
    		listBox.Items.Clear()
    
    
    		For Each filePath As String In System.IO.Directory.GetFiles(dir)
    			Try
    				'ListBoxにファイルとその画像をまとめおいて登録する
    				Dim item As New Item
    				item.FilePath = filePath
    				item.Image = System.Drawing.Image.FromFile(filePath)
    
    				listBox.Items.Add(item)
    			Catch ex As OutOfMemoryException
    			Catch ex As System.IO.FileNotFoundException
    			Catch ex As ArgumentException
    			End Try
    		Next
    	End Sub
    
    
    
    End Class
    
    ''' <summary>
    ''' ListBoxに入れておくファイルのパスと画像を保持するためのクラス
    ''' </summary>
    ''' <remarks></remarks>
    Class Item
    	Public FilePath As String
    	Public Image As System.Drawing.Image
    End Class


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年9月17日 8:26
  • 個人的に、フォルダを「folderbrowserdialog」で選び、そのフォルダ内の「*.jpg」を取ってきて同様に表示したいのですがどのようにすればいいのでしょうか。

    「〇〇したいけど、どのようにするのでしょうか?」というやり方の質問を繰り返すスタイルだと、時間がかかる割に伸びませんよ。
    調べてみる、サンプルをたくさん読んでみるとか、試行錯誤してコツをつかんでいかないといけません。

    たとえば、フォルダー以下の *.jpg を探すなら DOBON.NET さんのコンテンツにありますよね。
    https://dobon.net/vb/dotnet/file/getfiles.html

    DOBON.NET さんのページを見出しだけでも一通り読んでいく、必要そうかもしれないものは中身も読んでサンプルを試してみると言うだけでも結果は変わってくると思います。

    2017年9月17日 12:06
  • 回答ありがとうございます。おっしゃる通りです。しかしそのページも何度か試していますがやりたいことができませんでした。

    何を期待して、どのようなことをして、結果はどうなったかを書いていれば、助言がもらえることがあります。
    今回の場合は、目的と書いたコードと、結果(エラーになったのか、実行できたが異なる結果となったか、例外が起きたのか)ですかね。

    ただし、あまり突飛なものだったり、言動に問題があったりした場合は、助言が得られないこともありますのでご留意ください。

    2017年9月17日 12:26

すべての返信

  • ほかのスレッド と同様に、VBA フォーラムですが、VB.NET という前提でよいですかね?

    目指しているのは、ListBox 内にファイル名一覧ではなく、サムネイル画像一覧にしたいということですか?
    たとえば、こういう自力の描画の応用になります。
    https://dobon.net/vb/dotnet/control/lbownerdraw.html

    個人的には ListView にした方が向いている気はするのですが…。
    http://www.atmarkit.co.jp/ait/articles/0508/12/news091.html

    • 回答としてマーク 熊テン 2017年9月18日 8:10
    2017年9月17日 7:17
  • DrawItemイベントの中で各アイテムに対応する画像を描画することができます。

    Public Class Form1
    
    	Private WithEvents listBox1 As New ListBox
    	Private WithEvents listBox2 As New ListBox
    	Private WithEvents listBox3 As New ListBox
    
    	''' <summary>ドラッグ元のリストボックス</summary>
    	Private sourceListbox As ListBox
    
    	Sub New()
    
    		' この呼び出しはデザイナーで必要です。
    		InitializeComponent()
    
    		' InitializeComponent() 呼び出しの後で初期化を追加します。
    
    		Dim table As New TableLayoutPanel
    		table.RowCount = 1
    		table.ColumnCount = 3
    		For i = 1 To table.ColumnCount
    			table.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / table.ColumnCount))
    		Next
    
    		table.Dock = DockStyle.Fill
    
    		Me.Controls.Add(table)
    
    		Dim listBoxies = New ListBox() {listBox1, listBox2, listBox3}
    
    		For i = 0 To 2
    			Dim listBox As ListBox = listBoxies(i)
    			listBox.Dock = DockStyle.Fill
    
    			table.SetColumn(listBox, i)
    			table.Controls.Add(listBox)
    
    			listBox.DrawMode = DrawMode.OwnerDrawFixed '固定高さの描画をする場合
    			listBox.ItemHeight = 100 '一要素の高さ
    			listBox.IntegralHeight = False 'ListBoxの高さが変化しないように
    
    			listBox.AllowDrop = True 'ドロップ許可
    		Next
    	End Sub
    
    	Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    		Dim dlg As New FolderBrowserDialog
    		dlg.Description = "最初に画像を読み込むディレクトリを選択してください"
    		If (dlg.ShowDialog() = Windows.Forms.DialogResult.OK) Then
    			LoadImages(dlg.SelectedPath, listBox1)
    		End If
    	End Sub
    
    	''' <summary>
    	''' ListBoxのアイテムを自分で描画する
    	''' </summary>
    	Private Sub listBox1_DrawItem(sender As Object, e As DrawItemEventArgs) Handles listBox1.DrawItem, listBox2.DrawItem, listBox3.DrawItem
    		Dim listBox As ListBox = sender
    		If (0 <= e.Index AndAlso e.Index < listBox.Items.Count) Then
    			Dim item As Item = listBox.Items(e.Index)
    			Dim filePath As String = item.FilePath
    			Dim img As Image = item.Image
    			Dim margin = 5
    
    			e.DrawBackground() '選択色のため背景描画
    
    			'描画の大きさ計算
    			Dim rect As Rectangle = New Rectangle(e.Bounds.Left + margin, e.Bounds.Top + margin, e.Bounds.Width - margin * 2, e.Bounds.Height - margin * 2)
    			Dim scale = img.Height / rect.Height
    			Dim w = img.Width / scale
    			rect.Width = w
    
    			'画像を描画
    			e.Graphics.DrawImage(img, rect)
    		End If
    	End Sub
    
    
    	Private Sub listBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles listBox1.MouseDown, listBox2.MouseDown, listBox3.MouseDown
    		Dim listBox As ListBox = sender
    		'マウス位置からアイテムを探す
    		Dim index As Integer = listBox.IndexFromPoint(e.Location)
    		If (0 <= index AndAlso index < listBox.Items.Count) Then
    			'ListBoxのアイテムが選択された場合ドラッグを開始
    			Dim item = listBox.Items(index)
    			Dim items As New List(Of Item) '将来の複数移動に備えてドラッグ中のアイテムをListに入れてみる
    			For Each item In listBox.SelectedItems
    				items.Add(item)
    			Next
    
    			sourceListbox = listBox	'ドラッグ開始したListBoxを登録
    			listBox.DoDragDrop(items, DragDropEffects.Move)	'選択されたアイテムをドラッグ開始
    
    		End If
    	End Sub
    
    	''' <summary>
    	''' ドロップできるかどうかの判定
    	''' </summary>
    	Private Sub listBox1_DragEnter(sender As Object, e As DragEventArgs) Handles listBox1.DragEnter, listBox2.DragEnter, listBox3.DragEnter
    		Dim targetListBox As ListBox = sender
    		If (e.Data.GetDataPresent(GetType(List(Of Item)))) Then
    			'Itemがドラッグされている場合
    			If targetListBox Is sourceListbox Then 'ドラッグ開始とは別のListBoxにドロップしようとしているなら
    				e.Effect = DragDropEffects.None '移動不許可
    			Else
    				e.Effect = DragDropEffects.Move '移動を許可
    			End If
    		ElseIf (e.Data.GetDataPresent(DataFormats.FileDrop)) Then
    			'エクスプローラーなどからファイルがドラッグされている場合
    			e.Effect = DragDropEffects.Copy
    		Else
    			e.Effect = DragDropEffects.None
    		End If
    
    	End Sub
    
    	Private Sub listBox_DragDrop(sender As Object, e As DragEventArgs) Handles listBox1.DragDrop, listBox2.DragDrop, listBox3.DragDrop
    		Dim targetListBox As ListBox = sender
    		If (e.Data.GetDataPresent(GetType(List(Of Item)))) Then
    			If (sourceListbox IsNot Nothing) Then
    				Dim items As List(Of Item) = CType(e.Data.GetData(GetType(List(Of Item))), List(Of Item))
    				If (items IsNot Nothing AndAlso items.Count > 0) Then
    					'スクリーン座標からListBoxの座標に直してマウスカーソルの位置のアイテムを探す
    					Dim p As Point = targetListBox.PointToClient(Control.MousePosition)
    					Dim targetIndex = targetListBox.IndexFromPoint(p)
    					If (0 <= targetIndex AndAlso targetIndex < targetListBox.Items.Count) Then
    						'ドロップ先のアイテムがある場合
    						Dim targetItem As Item = targetListBox.Items(targetIndex)
    
    						'相手と入れ替え
    						targetListBox.Items.RemoveAt(targetIndex)
    						targetListBox.Items.Insert(targetIndex, items(0))
    
    						Dim sourceIndex As Integer = sourceListbox.SelectedIndex
    						sourceListbox.Items.RemoveAt(sourceIndex)
    						sourceListbox.Items.Insert(sourceIndex, targetItem)
    
    					Else
    						'ドロップ先にアイテムが無い場合
    						For Each item In items
    							'ドラッグ元から消してドラッグ先に追加
    							sourceListbox.Items.Remove(item)
    							targetListBox.Items.Add(item)
    						Next
    					End If
    
    					items.Clear()
    				End If
    			End If
    
    		ElseIf (e.Data.GetDataPresent(DataFormats.FileDrop)) Then
    			'エクスプローラーなどからファイルがドラッグされている場合
    			'ディレクトリの画像を読み直す
    			Dim files As String() = CType(e.Data.GetData(DataFormats.FileDrop), String())
    			If (files.Count = 1 AndAlso System.IO.Directory.Exists(files(0))) Then
    				LoadImages(files(0), targetListBox)
    			End If
    		End If
    	End Sub
    
    
    
    	Private Shared Sub LoadImages(ByVal dir As String, ByVal listBox As ListBox)
    		listBox.Items.Clear()
    
    
    		For Each filePath As String In System.IO.Directory.GetFiles(dir)
    			Try
    				'ListBoxにファイルとその画像をまとめおいて登録する
    				Dim item As New Item
    				item.FilePath = filePath
    				item.Image = System.Drawing.Image.FromFile(filePath)
    
    				listBox.Items.Add(item)
    			Catch ex As OutOfMemoryException
    			Catch ex As System.IO.FileNotFoundException
    			Catch ex As ArgumentException
    			End Try
    		Next
    	End Sub
    
    
    
    End Class
    
    ''' <summary>
    ''' ListBoxに入れておくファイルのパスと画像を保持するためのクラス
    ''' </summary>
    ''' <remarks></remarks>
    Class Item
    	Public FilePath As String
    	Public Image As System.Drawing.Image
    End Class


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年9月17日 8:26
  • 回答していただきありがとうございます。おっしゃる通り、ListBox 内にファイル名一覧ではなく、サムネイル画像一覧にすることということを目指しています。

    Listviewにした場合、Listviewからpictureboxに画像を持っていくことは可能でしょうか。

    貼っていただいた2つのリンクをもとに再度勉強します。

    2017年9月17日 9:41
  • 回答いただきありがとうございます。サンプルまで作っていただき感謝いたします。

    このサンプルを活用すると、Listbox1(2、3)について、同じForm内に同じ名前のメンバーが多種類存在するためListbox1(2、3)が曖昧だとエラーがでました…。どうすれば解消ができるのでしょうか。

    始めたてでございまして、何もかもお聞きして恐縮です。申し訳ございません。

    2017年9月17日 9:46
  • 連投失礼します。

    この貼っていただいた下のサイトの内容は実装できました。

    個人的に、フォルダを「folderbrowserdialog」で選び、そのフォルダ内の「*.jpg」を取ってきて同様に表示したいのですがどのようにすればいいのでしょうか。

    ボタンを押すとfbdが表示されて、OKを押すとこのサイトのような結果になるようにしたいです。

    2017年9月17日 11:59
  • 個人的に、フォルダを「folderbrowserdialog」で選び、そのフォルダ内の「*.jpg」を取ってきて同様に表示したいのですがどのようにすればいいのでしょうか。

    「〇〇したいけど、どのようにするのでしょうか?」というやり方の質問を繰り返すスタイルだと、時間がかかる割に伸びませんよ。
    調べてみる、サンプルをたくさん読んでみるとか、試行錯誤してコツをつかんでいかないといけません。

    たとえば、フォルダー以下の *.jpg を探すなら DOBON.NET さんのコンテンツにありますよね。
    https://dobon.net/vb/dotnet/file/getfiles.html

    DOBON.NET さんのページを見出しだけでも一通り読んでいく、必要そうかもしれないものは中身も読んでサンプルを試してみると言うだけでも結果は変わってくると思います。

    2017年9月17日 12:06
  • 回答ありがとうございます。おっしゃる通りです。しかしそのページも何度か試していますがやりたいことができませんでした。

    コツも何もかもさっぱりなので答えそのものを教えてもらいたいと甘んじていました。もう少し頑張ってみます!

    2017年9月17日 12:18
  • 回答ありがとうございます。おっしゃる通りです。しかしそのページも何度か試していますがやりたいことができませんでした。

    何を期待して、どのようなことをして、結果はどうなったかを書いていれば、助言がもらえることがあります。
    今回の場合は、目的と書いたコードと、結果(エラーになったのか、実行できたが異なる結果となったか、例外が起きたのか)ですかね。

    ただし、あまり突飛なものだったり、言動に問題があったりした場合は、助言が得られないこともありますのでご留意ください。

    2017年9月17日 12:26
  • 参考にさせていただきます。教えていただきありがとうございます。できなかった場合再度失礼のないよう投稿します。
    2017年9月17日 12:30