Various ways to display multiple photographs

You can use an array of PictureBox controls to display multiple photographs or other images. The sample below subclasses the PictureBox control and puts an array of them on a form. You need to change the PixPath to point to a directory with your digital photos.

(For an easy way to run the sample, see Use temporary projects in Visual Studio)

For years, I’ve had the capability to display an array of images on my Fox custom photo viewer application. When I ran the initial version of the code below, it was painfully slow. Even changing it to use dynamic thumbnails was much slower than the Fox version. Upon further investigation, the Fox version uses a separate related table of thumbnails that have already been generated.

You can use Spy++ from Visual studio to see that the PictureBox indeed has its own hWnd, and thus is a Windows Window.

An even faster alternative is to subdivide the Form’s hWnd into rectangles and draw the images in each rect. (next blog post)

The code also shows how to use lots of memory by using the ImageLocation property directly for the photo, or to create a thumbnail first, which is much faster and memory efficient.

I have all my 25,000 digital photos in a table (the size of which is about 2 photos), which can be queried easily using the VFP OleDB driver.

See also Sharing Digital Pictures of your friend's

Imports System.Data.OleDb

Module Myvars

    Public dt As New DataTable

    Public fUseDBF As Boolean = False

    Public fPixPathPrefix As String = "d:\pictures\"

    Public fUseLotsOfMemory As Boolean = False

End Module

Public Class Form1

    Dim xpix As Integer = 8

    Dim ypix As Integer = 8

    Dim aPic(xpix, ypix) As PictureBox

    'Change pixPath to point to a dir tree with many pictures

    Dim PixPath As String = "d:\pictures\2006\08"

    'Dim PixPath As String = "c:\windows\help\tours\htmltour"

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        'This code puts an array (xpix by ypix) of PictureBoxes on form

        Dim nRow, nCol As Integer

        If fUseDBF Then

            Dim da As New OleDb.OleDbDataAdapter("Select 'd:\pictures\'+fullname from mypix where 'tyler'$lower(notes) and '.JPG'$fullname", "Provider=VFPOLEDB.1;Data Source=d:\pictures\mypix.dbf")

            da.Fill(dt)

        Else

            dt.Columns.Add(New DataColumn("path", GetType(String)))

            For Each sFile As String In My.Computer.FileSystem.GetFiles(PixPath, FileIO.SearchOption.SearchAllSubDirectories, "*.jpg")

                dt.Rows.Add(sFile)

            Next

        End If

        Me.Text = dt.Rows.Count.ToString

        Me.WindowState = FormWindowState.Maximized

        For nCol = 0 To ypix - 1

            For nRow = 0 To xpix - 1

                Dim xx As Integer

                xx = Me.Width / xpix.ToString

                aPic(nRow, nCol) = New MyPictureBox '(Int(Me.Width / xpix), Int(Me.Height / ypix))

                With aPic(nRow, nCol)

                    .Visible = True

                    .Height = Me.Height / ypix

                    .Width = Me.Width / xpix

                    .Top = nCol * .Height

                    .Left = nRow * .Width

                    .SizeMode = PictureBoxSizeMode.StretchImage

                    .Tag = nCol * ypix + nRow ' store the index in the tag

                    Me.Controls.Add(aPic(nRow, nCol)) ' add the control to the form

                End With

            Next

        Next

    End Sub

End Class

Class MyPictureBox

    Inherits PictureBox

    Private Sub MyPictureBox_MouseEnter(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.MouseEnter

        If fUseLotsOfMemory Then

            Me.FindForm.Text = Me.ImageLocation

        Else

            Me.FindForm.Text = dt.Rows(Me.Tag).Item(0).ToString.Trim

        End If

    End Sub

    Private Sub MyPictureBox_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint

        Dim nIndex As Integer = Me.Tag

        If nIndex < dt.Rows.Count Then

            If fUseLotsOfMemory Then ' GDI+ uses LOTS of memory!

                If Me.ImageLocation Is Nothing Then

   Me.ImageLocation = dt.Rows(nIndex).Item(0).ToString.Trim

                End If

            Else

                If Me.Image Is Nothing Then ' use much less memory with dynamically created thumbnails

                    Dim imgBig As Image = New Bitmap(dt.Rows(nIndex).Item(0).ToString.Trim)

                    Me.Image = imgBig.GetThumbnailImage(Me.Width, Me.Height, Nothing, Nothing)

                    imgBig.Dispose() ' release the memory

                End If

            End If

        End If

    End Sub

End Class