Meet us at PRINT 19. Chicago, IL. Oct. 3 - 5.
This documentation is for the old version. Go to the latest Graphics Mill docs

Creating Custom List Items

The ThumbnailListView control allows not only working with file system-based items, but also with custom items as well. For example, you can easily create list items which wrap Aurigma.GraphicsMill.Bitmap class instances and display them in the control. This topic describes how to do that.

Interfaces to Implement

Graphics Mill for .NET has two interfaces that are implemented by list items:

IListItem Interface Overview

The IListItem interface exposes a number of methods, events, and properties which are called by the ThumbnailListView control to display thumbnails. Let us look at them more closely.

A large group of members relates to the item state: whether the item is selected, checked, or focused. They are:

Another group of members is responsible for item content. An item can store three kinds of content:

  • Icon (or telling more precisely, a key of the icon in the image list of the control)—returned using the GetIconKey(View) method. To check whether an icon is available, the HasIcon(View) method is used. When the icon is changed, the IconChanged event is raised. An item can have up to four icons (one per each view mode).
  • Textual data, that is information displayed in the Details view and possibly in an item caption in other views. It is accessed using the GetText(Int32) method. To verify whether an item contains specific piece of textual data, the HasText(Int32) method is used. When some data are updated, the TextChanged event is raised.
  • Custom data. You can associate with an item any other necessary data using the Tag property.

And at last, there is a member that stands apart—the Parent property. It stores a link to the parent ThumbnailListView control.

IQueueItem Interface Overview

The IQueueItem interface is optional, but it is strongly recommended to implement it for better usability of your application. If the item implements this interface, the ThumbnailListView control is able to display icons of items asynchronously. In other words, it allows the control to distribute the process of icon and textual data loading in time.

What happens when the control works in a synchronous mode? When an item is added to the control, this item builds four icons to represent itself in four view modes. Typically, this process takes quite a long time. This way, the user interface will freeze until the item icons are built. It may be OK, when few items are added, but if you add hundreds of elements, it becomes unacceptable.

The solution of this problem is an asynchronous mode. An item is added to the control without its icon (a default icon is displayed instead). The item icon is generated in a separate thread. When the icon is ready, the control refreshes and displays the icon for this item.

Now let us see how IQueueItem interface works. It has only three members:

  • EvaluateMethod(Int32) method does all job which is parallelized. The control can run more than one thread, that is, one thread may generate thumbnail and another can extract dimensions from the file. A number of operations (that will be performed in a thread) is passed to this method. This way you can organize a switch/case statement inside this method or something like this.
  • GetMethodState(Int32) method returns the current state of a job. Initially the state is NotStarted. In the beginning of the EvaluateMethod(Int32) execution it should be changed to Started. When the EvaluateMethod(Int32) execution finishes, the state should be changed to Finished. This way the control can check the status of its child items.
  • MethodCount property is a number of operations (methods) supported by the EvaluateMethod(Int32) method (the upper bound of its argument). This way the control determines how many threads should be launched.

If you implement this interface, you need to force the control to run threads using the QueueManager class (see the code example later).

Creating BitmapListItem Class

Now, implement a custom list item which will wrap Aurigma.GraphicsMill.Bitmap objects. Also, this item will hold three text fields: a description of the bitmap (specified in the code), its color space and pixel format (extracted from the Bitmap object).

The IListItem interface contains a lot of members, but fortunately there is no need to implement all of them. Graphics Mill for .NET provides a base implementation of this inteface in the ListItem class. So, just inherit from this class and reuse a larger part of its implementation. The only thing you need to implement is loading of the data into the item, namely:

  • Generating icons from a given bitmap for each view mode.
  • Storing text fields (in particular, extracting the color space and pixel format from the bitmap).

The best place to do it is the IQueueItem interface, or speaking more precisely, the EvaluateMethod(Int32) method. The only "heavy" operation is icon creation, and therefore this method will implement only one operation, or method in terms of the IQueueItem interface.

The ListItem class has several protected members and one of them will be widely used in the code example below. It is the Texts property. This property is a hash table that keeps all pieces of textual data. The ID of the text string is used as a key of this hash table. When the item is initialized, all pieces of textual information are simply put into this property. If the process of fetching this information takes quite a long time (for example, if it is fetched from a database), you can use the second queue and add appropriate code to the EvaluateMethod(Int32) method.

Take a look at the implementation code:

Visual Basic
Public Class BitmapListItem
    Inherits Aurigma.GraphicsMill.WinControls.ListItem
    Implements Aurigma.GraphicsMill.WinControls.IQueueItem

    ' Stores the state of item data loading.
    Protected _queueMethodState _
  As Aurigma.GraphicsMill.WinControls.QueueItemMethodState

    ' Stores the bitmap represented by this item.
    Protected _bitmap As Aurigma.GraphicsMill.Bitmap
    
    'Stores the unique key for bitmap thumbnails.
    Protected _key As Long

    ' Updates the bitmap represented by this item.
    Private Sub UpdateBitmap(ByVal bitmap As Aurigma.GraphicsMill.Bitmap)

        ' Update the bitmap itself.
        If Not _bitmap Is Nothing Then
            RemoveHandler _bitmap.Changed, AddressOf _bitmap_Changed
        End If
        _bitmap = bitmap

        ' Subscribe the Changed event so that we could update icons 
        ' if the bitmap is modified outside.
        AddHandler _bitmap.Changed, AddressOf _bitmap_Changed

        ' Reset icons.
        _queueMethodState = _
         Aurigma.GraphicsMill.WinControls.QueueItemMethodState.NotStarted

        ' Update text information which depends on bitmap.
        UpdateText(TextInfoColorSpaceId, bitmap.ColorSpace.ToString())
        UpdateText(TextInfoPixelFormatId, bitmap.PixelFormat.ToString())

        ' If the item is already associated with some control, force it to 
        ' update an icon.
        If Not Parent Is Nothing Then
            Parent.QueueManager.MoveToHead(Me, 0)
            Parent.QueueManager.StartQueues()
        End If
    End Sub

    ' Updates text information.
    Private Sub UpdateText(ByVal textInfoId As Integer, ByVal text As String)
        Texts(textInfoId) = text
        OnTextChanged(textInfoId)
    End Sub

    ' Changed event handler for the _bitmap.
    Private Sub _bitmap_Changed(ByVal sender As Object, _
      ByVal e As Aurigma.GraphicsMill.BitmapChangedEventArgs)
        UpdateBitmap(_bitmap)
    End Sub


    ' Constructor.
    Public Sub New(ByVal bitmap As Aurigma.GraphicsMill.Bitmap, _
       ByVal description As String)
            UpdateBitmap(bitmap)
            UpdateText(TextInfoDescriptionId, description)
            _key = Id
            Id = Id + 1
    End Sub

    ' Unique number for item
    Private Shared Id As Integer = 0

    ' Constants of the text data which can be extracted from the item 
    ' (displayed in the Details view mode). It is recommended to use 
    ' values which differs from standard text IDs (used in ThumbnailListItem).
    Public Const TextInfoDescriptionId As Integer = 10
    Public Const TextInfoColorSpaceId As Integer = 11
    Public Const TextInfoPixelFormatId As Integer = 12

    ' Returns the bitmap represented by this item.
    Public Property Bitmap() As Aurigma.GraphicsMill.Bitmap
        Get
            Return _bitmap
        End Get
        Set(ByVal Value As Aurigma.GraphicsMill.Bitmap)
            UpdateBitmap(Value)
        End Set
    End Property

    ' IQueueItem implementation
    ' Runs the process of item data extraction. This implementation
    ' supports only one queue and methodIndex should be always equal to 0.
    Public Sub EvaluateMethod(ByVal methodIndex As Integer) _
     Implements Aurigma.GraphicsMill.WinControls.IQueueItem.EvaluateMethod

        If methodIndex = 0 Then

            ' Indicate that method is in process.
            _queueMethodState = _
             Aurigma.GraphicsMill.WinControls.QueueItemMethodState.Started

            ' Create an icon for Thumbnails view. We will use the resized  
            ' version of the bitmap for this.

            ' Prepare the Resize object.
            Dim resize As New Aurigma.GraphicsMill.Transforms.Resize
            resize.Width = Parent.ThumbnailSize.Width
            resize.Height = Parent.ThumbnailSize.Height
            resize.InterpolationMode = _
             Aurigma.GraphicsMill.Transforms.InterpolationMode.HighQuality
            resize.ResizeMode = _
             Aurigma.GraphicsMill.Transforms.ResizeMode.Fit

            ' Apply the resize and return the result to a separate bitmap. 
            Dim thumbnail As New Aurigma.GraphicsMill.Bitmap
            resize.ApplyTransform(_bitmap, thumbnail)

            ' Put the icon (thumbnail) to the image list of the control 
            ' and store the icon index.
            Dim listView As Aurigma.GraphicsMill.WinControls.IImageList
            listView = Parent.GetImageList( _
             Aurigma.GraphicsMill.WinControls.View.Thumbnails)
            listView.AddImage(thumbnail, _key)
            OnIconChanged(Aurigma.GraphicsMill.WinControls.View.Thumbnails)

            ' Create an icon for Icon view. 32x32 version of the 
            ' bitmap will be used.

            ' Everything is the same as for Thumbnails image list.
            resize.Width = 32
            resize.Height = 32
            resize.ApplyTransform(_bitmap, thumbnail)
            listView = Parent.GetImageList( _
             Aurigma.GraphicsMill.WinControls.View.Icons)
            listView.AddImage(thumbnail, _key)
            OnIconChanged(Aurigma.GraphicsMill.WinControls.View.Icons)

            ' Create an icon for List and Details views. 16x16 version 
            ' of the bitmap will be used. 
            ' The same icon will be reused for both view modes.
            resize.Width = 16
            resize.Height = 16
            resize.ApplyTransform(_bitmap, thumbnail)
            listView = Parent.GetImageList( _
             Aurigma.GraphicsMill.WinControls.View.List)
            listView.AddImage(thumbnail, _key)
            OnIconChanged(Aurigma.GraphicsMill.WinControls.View.List)
            listView = Parent.GetImageList( _
             Aurigma.GraphicsMill.WinControls.View.Details)
            listView.AddImage(thumbnail, _key)
            OnIconChanged(Aurigma.GraphicsMill.WinControls.View.Details)

            ' Indicate that the method has been finished.
            _queueMethodState = _
             Aurigma.GraphicsMill.WinControls.QueueItemMethodState.Finished

            ' Cleanup
            thumbnail.Dispose()
        Else
            Throw New System.NotImplementedException(Me.GetType().ToString() & _
             "class supports only one method queue. Make sure that when " & _
             "you call EvaluateMethod method, methodIndex equals to 0. ")
        End If
    End Sub

    ' Returns the current state of the data extraction process.
    Public Function GetMethodState(ByVal methodIndex As Integer) _
     As Aurigma.GraphicsMill.WinControls.QueueItemMethodState _
     Implements Aurigma.GraphicsMill.WinControls.IQueueItem.GetMethodState

        Return _queueMethodState
    End Function

    ' Returns a number of supported method queues (currently = 1).
    Public ReadOnly Property MethodCount() As Integer _
     Implements Aurigma.GraphicsMill.WinControls.IQueueItem.MethodCount
        Get
            Return 1
        End Get
    End Property

    ' IListItem interface partial implementation. Just override 
    ' GetIconIndex method which will start bitmap loading if icon 
    ' is not avaialble. All the rest implementation is inherited from  
    ' ListItem class.
    Public Overrides Function GetIconKey(ByVal view As _
     Aurigma.GraphicsMill.WinControls.View) _
     As Object
        If (Not HasIcon(view)) Then
            ' If no icon available, we need to move the current item 
            ' to the head of the first (and the single) queue and re-start 
            ' queue processing.
            Parent.QueueManager.MoveToHead(Me, 0)
            Parent.QueueManager.StartQueues()

            ' Indicate that the icon is not available yet. 
            Return Nothing
        Else
            ' If icon is available, just return it. 
            Return _key
        End If
    End Function
    
    Public Overrides Function HasIcon(ByVal view As _
     Aurigma.GraphicsMill.WinControls.View) As Boolean
        Return Parent.GetImageList(view).ContainsKey(_key)
    End Function
End Class
C#
public class BitmapListItem:
    Aurigma.GraphicsMill.WinControls.ListItem,
    Aurigma.GraphicsMill.WinControls.IQueueItem
{

    // Stores the state of item data loading.
    protected Aurigma.GraphicsMill.WinControls.QueueItemMethodState 
        _queueMethodState;

    // Stores the bitmap represented by this item.
    protected Aurigma.GraphicsMill.Bitmap _bitmap;

    // Stores the unique key for bitmap thumbnails.
    protected long _key;

    // Updates the bitmap represented by this item.
    private void UpdateBitmap(Aurigma.GraphicsMill.Bitmap bitmap)
    {
        // Update the bitmap itself.
        if (_bitmap != null)
        {
            _bitmap.Changed -= 
                new Aurigma.GraphicsMill.BitmapChangedEventHandler(
                    _bitmap_Changed);
         }
        _bitmap = bitmap;

        // Subscribe the Changed event so that we could update icons 
        // if the bitmap is modified outside.
        _bitmap.Changed += 
            new Aurigma.GraphicsMill.BitmapChangedEventHandler(
                _bitmap_Changed);

        // Reset icons.
        _queueMethodState = 
            Aurigma.GraphicsMill.WinControls.QueueItemMethodState.NotStarted;

        // Update text information which depends on bitmap.
        UpdateText(TextInfoColorSpaceId, bitmap.ColorSpace.ToString());
        UpdateText(TextInfoPixelFormatId, bitmap.PixelFormat.ToString());

        // If the item is already associated with some control, force it to 
        // update an icon.
        if (Parent != null)
        {
            Parent.QueueManager.MoveToHead(this, 0);
            Parent.QueueManager.StartQueues();
        }
    }

    // Updates text information.
    private void UpdateText(int textInfoId, string text)
    {
        Texts[textInfoId] = text;
        OnTextChanged(textInfoId);
    }

    // Changed event handler for the _bitmap.
    private void _bitmap_Changed(object sender, 
        Aurigma.GraphicsMill.BitmapChangedEventArgs e)
    {
        UpdateBitmap(_bitmap);
    }

    // Constructor.
    public BitmapListItem(Aurigma.GraphicsMill.Bitmap bitmap,
            string description)
    {
            UpdateBitmap(bitmap);
            UpdateText(TextInfoDescriptionId, description);
            _key = Id++;
    }

    // Unique number for item
    private static int Id = 0;

    // Constants of the text data which can be extracted from the item 
    // (displayed in the Details view mode). It is recommended to use 
    // values which differs from standard text IDs (used in ThumbnailListItem).
    public const int TextInfoDescriptionId = 10;
    public const int TextInfoColorSpaceId = 11;
    public const int TextInfoPixelFormatId = 12;

    // Returns the bitmap represented by this item.
    public Aurigma.GraphicsMill.Bitmap Bitmap
    {
        get
        {
            return _bitmap;
        }
        set
        {
            UpdateBitmap(value);
        }
    }

    // IQueueItem implementation
    // Runs the process of item data extraction. This implementation
    // supports only one queue and methodIndex should be always equal to 0.
    public void EvaluateMethod(int methodIndex)
    {
        if (methodIndex == 0)
        {
            // Indicate that method is in process.
            _queueMethodState = 
                Aurigma.GraphicsMill.WinControls.QueueItemMethodState.Started;
            
            // Create an icon for Thumbnails view. We will use the resized 
            // version of the bitmap for this.
            
            // Prepare the Resize object.
            Aurigma.GraphicsMill.Transforms.Resize resize = 
                new Aurigma.GraphicsMill.Transforms.Resize();
            resize.Width = Parent.ThumbnailSize.Width;
            resize.Height = Parent.ThumbnailSize.Height;
            resize.InterpolationMode = 
                Aurigma.GraphicsMill.Transforms.InterpolationMode.HighQuality;
            resize.ResizeMode = 
                Aurigma.GraphicsMill.Transforms.ResizeMode.Fit;

            // Apply the resize and return the result to a separate bitmap. 
            Aurigma.GraphicsMill.Bitmap thumbnail = 
                new Aurigma.GraphicsMill.Bitmap();
            resize.ApplyTransform(_bitmap, thumbnail);

            // Put the icon (thumbnail) to the image list of the control 
            // and store the icon index.
            Aurigma.GraphicsMill.WinControls.IImageList listView = 
                Parent.GetImageList(
                    Aurigma.GraphicsMill.WinControls.View.Thumbnails);
            listView.AddImage(thumbnail, _key);
            OnIconChanged(Aurigma.GraphicsMill.WinControls.View.Thumbnails);

            // Create an icon for Icon view. 32x32 version of the bitmap 
            // will be used.

            // Everything is the same as for Thumbnails image list.
            resize.Width = 32;
            resize.Height = 32;
            resize.ApplyTransform(_bitmap, thumbnail);                
            listView = Parent.GetImageList(
                Aurigma.GraphicsMill.WinControls.View.Icons);
            listView.AddImage(thumbnail, _key);
            OnIconChanged(Aurigma.GraphicsMill.WinControls.View.Icons);
            
            // Create an icon for List and Details views. 16x16 version of 
            // the bitmap will be used. 
            // The same icon will be reused for both view modes.

            resize.Width = 16;
            resize.Height = 16;
            resize.ApplyTransform(_bitmap, thumbnail);
            listView = Parent.GetImageList(
                Aurigma.GraphicsMill.WinControls.View.List);
            listView.AddImage(thumbnail, _key);
            OnIconChanged(Aurigma.GraphicsMill.WinControls.View.List);
            listView = Parent.GetImageList(
                Aurigma.GraphicsMill.WinControls.View.Details);
            listView.AddImage(thumbnail, _key);
            OnIconChanged(Aurigma.GraphicsMill.WinControls.View.Details);

            // Indicate that the method has been finished.
            _queueMethodState = 
                Aurigma.GraphicsMill.WinControls.QueueItemMethodState.Finished;
            
            // Cleanup.
            thumbnail.Dispose();
        }
        else
        {
            throw new System.NotImplementedException(this.GetType().ToString() + 
                "class supports only one method queue. Make sure that when " +
                "you call EvaluateMethod method, methodIndex equals to 0. ");
        }
    }

    // Returns the current state of the data extraction process.
    public Aurigma.GraphicsMill.WinControls.QueueItemMethodState 
        GetMethodState(int methodIndex)
    {
        return _queueMethodState;
    }

    // Returns a number of supported method queues (currently = 1).
    public int MethodCount
    {
        get
        {
            return 1;
        }
    }
    // IListItem interface partial implementation. Just override 
    // GetIconKey method which will start bitmap loading if icon 
    // is not avaialble. All the rest implementation is inherited from 
    // ListItem class.
    public override object GetIconKey(
        Aurigma.GraphicsMill.WinControls.View view)
    {
        if (!HasIcon(view))
        {
            // If no icon available, we need to move the current item 
            // to the head of the first (and the single) queue and re-start 
            // queue processing.
            Parent.QueueManager.MoveToHead(this, 0);
            Parent.QueueManager.StartQueues();

            // Indicate that the icon is not available yet. 
            return null;
        }
        else
        {
            // If icon is available, just return the key. 
            return _key;
        }
    }

    public override bool HasIcon(Aurigma.GraphicsMill.WinControls.View view)
    {
        return Parent.GetImageList(view).ContainsKey(_key);
    }

}

The usage of this class is very similar to the one of ThumbnailListItem. Here is how items are added to the control and how the columns are configured for the detals list.

Visual Basic
Dim item1 As BitmapListItem, item2 As BitmapListItem

' This item represents a bitmap object loaded from file.
item1 = New BitmapListItem( _
 New Aurigma.GraphicsMill.Bitmap("C:\[Test Files]\mk-319.jpg"), _
 "Microphone photo loaded from file")


' This item represents a bitmap object created from a scratch. 
' Here we create grayscale bitmap and draw an ellipse.
item2 = New BitmapListItem( _
  New Aurigma.GraphicsMill.Bitmap(Aurigma.GraphicsMill.RgbColor.Aqua, _
   320, 240, _
   Aurigma.GraphicsMill.PixelFormat.Format8bppGrayScale, _
   Nothing), _
  "Self-created bitmap")
Dim graphics As Aurigma.GraphicsMill.Drawing.GdiGraphics = _
  item2.Bitmap.GetGdiGraphics()

graphics.FillEllipse(New Aurigma.GraphicsMill.Drawing.SolidBrush, _
  50, 50, 200, 150)
graphics.Dispose()

' Put items to the control.
ThumbnailListView1.Items.Add(item1)
ThumbnailListView1.Items.Add(item2)

' Configure columns for the Details view and caption for other views.
ThumbnailListView1.Columns.Add( _
 New Aurigma.GraphicsMill.WinControls.ListColumn( _
  BitmapListItem.TextInfoDescriptionId, _
  "Description", 200, _
  System.Windows.Forms.HorizontalAlignment.Left))

ThumbnailListView1.Columns.Add( _
 New Aurigma.GraphicsMill.WinControls.ListColumn( _
  BitmapListItem.TextInfoColorSpaceId, _
  "Color Space", 100, _
  System.Windows.Forms.HorizontalAlignment.Left))

ThumbnailListView1.Columns.Add( _
 New Aurigma.GraphicsMill.WinControls.ListColumn( _
  BitmapListItem.TextInfoPixelFormatId, _
  "Pixel Format", 150, _
  System.Windows.Forms.HorizontalAlignment.Left))

ThumbnailListView1.IconicViewTextInfoId = _
 BitmapListItem.TextInfoDescriptionId
C#
BitmapListItem item1, item2;

// This item represents a bitmap object loaded from file.
item1 = new BitmapListItem(
    new Aurigma.GraphicsMill.Bitmap(@"C:\[Test Files]\mk-319.jpg"), 
    "Microphone photo loaded from file");

// This item represents a bitmap object created from a scratch. 
// Here we create grayscale bitmap and draw an ellipse.
item2 = new BitmapListItem(
    new Aurigma.GraphicsMill.Bitmap(Aurigma.GraphicsMill.RgbColor.Aqua, 
        320, 
        240, 
        Aurigma.GraphicsMill.PixelFormat.Format8bppGrayScale, 
        null), 
    "Self-created bitmap");
Aurigma.GraphicsMill.Drawing.GdiGraphics graphics =
    item2.Bitmap.GetGdiGraphics();
graphics.FillEllipse(new Aurigma.GraphicsMill.Drawing.SolidBrush(),
    50, 50, 200, 150);
graphics.Dispose();

// Put items to the control.
thumbnailListView1.Items.Add(item1);
thumbnailListView1.Items.Add(item2);

// Configure columns for the Details view and caption for other views.
thumbnailListView1.Columns.Add(
    new Aurigma.GraphicsMill.WinControls.ListColumn(
        BitmapListItem.TextInfoDescriptionId, 
        "Description", 200, 
        System.Windows.Forms.HorizontalAlignment.Left));
thumbnailListView1.Columns.Add(
    new Aurigma.GraphicsMill.WinControls.ListColumn(
        BitmapListItem.TextInfoColorSpaceId, 
        "Color Space", 100, 
        System.Windows.Forms.HorizontalAlignment.Left));
thumbnailListView1.Columns.Add(
    new Aurigma.GraphicsMill.WinControls.ListColumn(
        BitmapListItem.TextInfoPixelFormatId, 
        "Pixel Format", 150, 
        System.Windows.Forms.HorizontalAlignment.Left));
thumbnailListView1.IconicViewTextInfoId = 
    BitmapListItem.TextInfoDescriptionId;            

As a result, your bitmaps will be displayed in the control as follows:

Thumbnails view Icons view
View = Thumbnails View = Icons
Simple list view Detailed list view
View = List View = Details

See Also

Reference