This documentation is for the old version. Go to the latest Graphics Mill docs

How to Load Items in the ThumbnailListView Asynchronously

The major use of the ThumbnailListView control is to create file galleries. However if your gallery is sufficiently large (i.e. it contains 500 or more files) you may encounter some problems with application interactivity. When you load a big number of items in the ThumbnailListView control at the same time your user interface may freeze until the operation is completed.

Files are loaded in the control in two stages: create a PIDL for the file and then add this PIDL to the ThumbnailListView (read more detailed information about PIDLs in the Using the ThumbnailListView Control as a File Browser topic). Each stage requires some time to complete. For example, before a PIDL is created the system checks whether the specified file exists. After this PIDL is added to the control some metadata such as file size, time stamp, etc. is extracted from the corresponded file. Thus, if we add a big number of files to the control, both stages described above will be applied to each of them. It will require sufficient time to complete this operation.

To avoid this problem we could load items in a separate worker thread. In that case we will make the user-interface thread free to handle user input events, but it will still take some time to complete. This approach is considered in the Creating Multithreaded GUI topic.

This article discusses one more approach to solve this problem. The main idea is to divide all files into packages of 100-200 files and process them in the additional worker thread. This technique is represented by the AddFiles method being executed in the worker thread. It accepts file names of the current package, creates PIDLs for these files and adds them to the ThumbnailListView thread safely using the System.Windows.Forms.Control.BeginInvoke method. See the Safe, Simple Multithreading in Windows Forms, Part 1 article which described how to do thread-safe calls to Windows Forms controls. In this approach PIDLs are prepared in a separate worker thread and added to the control portionwise. It allows the system to use time intervals between processing packages to handle user input events. To accelerate this process we can load several packages of files simultaneously using a pool of threads provided by the System.Threading.ThreadPool class. That allows us to queue calls to AddFiles method instead of creating a new System.Threading.Thread instance to process each package. To familiarize with basic principles of developing multithreaded applications you can read the Threads and Threading topic.

This approach is demonstrated below:

Visual Basic
' Default package size. 
Dim _defaultPackageSize As Integer = 100

' Delegate for the UpdateThumbnailListView method.
Delegate Sub UpdateThumbnailListViewDelegate(ByVal listItems As Aurigma.GraphicsMill.WinControls.ThumbnailListItem())

Private Sub AddFiles(ByVal filesPackage As Object)
    Dim filesPackageStr() As String = CType(filesPackage, String())

    ' Index of the current file.
    Dim fileIndex As Integer = 0
    ' Total number of files in the package.
    Dim fileCount As Integer = filesPackageStr.Length
    Dim i As Integer

    ' Create and fill a new array with PIDLs for the package.
    Dim filePidls(fileCount - 1) As Aurigma.GraphicsMill.WinControls.Pidl

    For i = 0 To fileCount - 1
        filePidls(i) = Aurigma.GraphicsMill.WinControls.Pidl.Create(filesPackageStr(fileIndex))
        fileIndex += 1
    Next

    ' Create an array of ThumbnailListItems.
    Dim listItems() As Aurigma.GraphicsMill.WinControls.ThumbnailListItem = _
            Aurigma.GraphicsMill.WinControls.ThumbnailListItem.Create(filePidls)

    Dim params() As Object = {listItems}

    ' Call the UpdateThumbnailListView method from the worker thread to execute it in the user interface thread.
    _thumbnailListView.BeginInvoke(New UpdateThumbnailListViewDelegate(AddressOf UpdateThumbnailListView), _
            params)
End Sub

Private Sub UpdateThumbnailListView(ByVal listItems() As Aurigma.GraphicsMill.WinControls.ThumbnailListItem)
    _thumbnailListView.Items.Add(listItems)
    _thumbnailListView.Refresh()
    _fileCountLbl.Text = _thumbnailListView.Items.Count.ToString()
End Sub

Private Sub _openButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _openButton.Click
    If _folderBrowserDialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
        ' Array of filenames from the specified folder.
        Dim filenames() As String = System.IO.Directory.GetFiles(_folderBrowserDialog.SelectedPath)

        ' Index of the current file.
        Dim fileIndex As Integer = 0
        ' Total number of files in the folder.
        Dim fileCount As Integer = filenames.Length
        ' Size of the current package.
        Dim packageSize As Integer

        ' Iterate through all the files in the folder.
        While fileCount >= 1
            ' Determine the size of the current package.
            packageSize = IIf(fileCount >= _defaultPackageSize, _defaultPackageSize, fileCount Mod _defaultPackageSize)

            ' Create and fill a new array with filenames for the current package.                  
            Dim filesPackage(packageSize - 1) As String
            Array.Copy(filenames, filesPackage, packageSize - 1)
            fileIndex += packageSize

            ' Queue AddFiles method for execution.
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf AddFiles), filesPackage)
            fileCount -= packageSize
        End While
    Else
        Return
    End If
End Sub
C#
// Default package size. 
int _defaultPackageSize = 100;

// Delegate for the UpdateThumbnailListView method.
delegate void UpdateThumbnailListViewDelegate(Aurigma.GraphicsMill.WinControls.ThumbnailListItem[] listItems);

private void AddFiles(object filesPackage)
{
    string[] filesPackageStr = (string[])filesPackage;

    // Index of the current file.
    int fileIndex = 0;
    // Total number of files in the package.
    int fileCount = filesPackageStr.Length;
    int i;

    // Create and fill a new array with PIDLs for the package.
    Aurigma.GraphicsMill.WinControls.Pidl[] filePidls =
        new Aurigma.GraphicsMill.WinControls.Pidl[fileCount];
    for (i = 0; i < fileCount; i++)
    {
        filePidls[i] = Aurigma.GraphicsMill.WinControls.Pidl.Create(filesPackageStr[fileIndex]);
        fileIndex++;
    }

    // Create an array of ThumbnailListItems.
    Aurigma.GraphicsMill.WinControls.ThumbnailListItem[] listItems =
        Aurigma.GraphicsMill.WinControls.ThumbnailListItem.Create(filePidls);

    // Call the UpdateThumbnailListView method from the worker thread to execute it in the user interface thread.
    _thumbnailListView.BeginInvoke(new UpdateThumbnailListViewDelegate(UpdateThumbnailListView),
        new object[] { listItems });
}

private void UpdateThumbnailListView(Aurigma.GraphicsMill.WinControls.ThumbnailListItem[] listItems)
{
    _thumbnailListView.Items.Add(listItems);
    _thumbnailListView.Refresh();
    _fileCountLbl.Text = _thumbnailListView.Items.Count.ToString();
}

private void _openButton_Click(object sender, EventArgs e)
{
    if (_folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
    {
        // Array of filenames from the specified folder.
        string[] filenames = System.IO.Directory.GetFiles(_folderBrowserDialog.SelectedPath);

        // Index of the current file.
        int fileIndex = 0;
        // Total number of files in the folder.
        int fileCount = filenames.Length;
        // Size of the current package.
        int packageSize;

        // Iterate through all the files in the folder.
        while (fileCount >= 1)
        {
            // Determine the size of the current package.
            packageSize = fileCount >= _defaultPackageSize ? _defaultPackageSize :
                fileCount % _defaultPackageSize;
            
            // Create and fill a new array with filenames for the current package.                  
            string[] filesPackage = new string[packageSize];
            Array.Copy(filenames, filesPackage, packageSize);
            fileIndex += packageSize;

            // Queue AddFiles method for execution.
            ThreadPool.QueueUserWorkItem(new WaitCallback(AddFiles), filesPackage);

            fileCount -= packageSize;
        }
    }
    else
    {
        return;
    }
}

See Also

Reference

Manual