# Loading Thumbnails

Better Thumbnail Browser for .NET (C#, VB) - Image thumbnail viewing and loading control
Download More Info

## Loading Images from a Folder

You can populate Better Thumbnail Browser with auto-population in case you want to display images from an image folder:

C#

    thumbnailBrowser.Path = "c:\\images";

Visual Basic

    thumbnailBrowser.Path = "c:\images"

This will automatically add items to Better Thumbnail Browser and starts loading them using default image loading provider:

## Starting, Stopping and Restarting Loading

You can control item loading by calling StartLoading(), StopLoading() and RestartLoading() methods.

The StartLoading() method is asynchronous, so the code will continue after the call and the thumbnail items will be loaded on a separate thread.

The StopLoading() method is synchronous - it will wait until current item finishes loading and then stops.

The RestartLoading() method has an override with LoadingRestartOptions parameter. It can have the following values:

- None

  - Restart loading if already running. Do not start loading if not running.

- StopLoading

  - Stop loading and then start from beginning.

- CanStartLoading

  - Restart loading if already running. Start loading even if not running.

When calling RestartLoading() method without parameters, the LoadingRestartOptions.None is used.

## Loading Events

During the loading process, two events are raised by the Better ThumbnailBrowser control:

- ItemLoaded

  - This event is raised whenever a single item is loaded by a loading provider. The event can be raised multiple times for a single item if a multi-pass loading (see section Multi-pass Loading) is used. Event data contains loaded item instance, pass index, total number of passes and order in which the item is loaded (the order can be different from the item index, see section Custom Loading Order).

- LoadingFinised

  - This event is raised when thumbnail item loading finishes for whatever reason (no more items to load, loading cancelled by the user, loading failed). Event data contains index of item on which the loading finished/stopped (LoadingFinishReason enumeration), reason why the loading ended and System.Exception instance in case the loading failed because exception occured on the loading thread.

## Loading Options

## Refreshing Delay

When large number of small thumbnails are loaded, or when loading each thumbnail is quick, it is inefficient to refresh control after every single item is loaded. Instead, a timer looks for loaded items in a predefined interval and refreshes the control only if any items get loaded. By default, the interval is set to 250 millisecons, but you can set your own value through RefreshDelay property. When you set property value to 0, the control will be refreshed after every item loaded.

Better Thumbnail Browser uses System.Threading.Timer, so its accurracy is in order of tens of milliseconds.

## Loading Thread Options

You can setup thumbnail item loading thread by setting the following properties:

- LoadingThreadIsBackground

  - Specifies whether the item loading thread runs as a background thread.

- LoadingThreadPriority

  - Specified item loading thread priority (System.Threading.ThreadPriority).

## Skipping Individual Items

If you want not to load specific thumbnail items, set BetterThumbnailBrowserItem.Skip property to true.

## Loading Providers

Thumbnail item loading is performed by so called loading providers (instances of LoadingProvider class).

These instances are listed in the LoadingProviders property. In case there is more than one loading provider, a multiple passes over the thumbnail items are done, one for each loading provider.

Better Thumbnail Browser have two bases classes for deriving your own loading providers:

- CustomLoadingProvider

  - Intended for loading non-image data.

- ImageLoadingProvider

  - Intended for loading image thumbnails.

The both of these classes are derived from LoadingProvider class and implement a LoadItem method. In this method, the loader calls LoadItemAsync method, which have to be provided by the user as well as the LoadItemSync method. The difference between these two methods is that LoadItemAsync is called on the (background) loader thread and the LoadItemSync is then called on the UI (foreground) thread.

Only LoadItemAsync and LoadItemSync methods need to be implemented by the user.

There is also LoadItemsSync method which is used by LoadItem when timer is used (see RefreshDelay property), in that case, the loading provider calls only LoadItemAsync and stores the result data, and then performs synchronization and calls LoadItemSync in a batch.

## Loading Image Thumbnails in Better Thumbnail Browser

Thumbnail loading is designed to be fast and effective.

First of all, images need not to be loaded in full resolution and resized. Instead, you can load image in lower resolution or simply get low resolution version already available. For example, the default image loading provider in Better Thumbnail Browser makes use of existing image thumbnails in JPEG image, avoiding resizing.

You can still load images in full resolution. These images will be present in BetterThumbnailBrowser.Image property in full resolution, but will be resized internally for viewing.

The thumbnail items can be zoomed and images may need to be reloaded in higher resolution. The loading mechanism of Better ThumbnailBrowser never reloads images if a full resolution is already loaded (smaller image gets centered in the thumbnail item instead). It also avoid reloading if user provided image of high-enough resolution. Of course, if you provide image in full resolution, then every thumbnail item gets loaded just once. To determine what "full resolution" means, there is a maximumImageSize property in the loading method. One provides loaded image and informs about the full resolution. You can also set maximumImageSize property to the same size as thumbnail image, but your image will not be reloaded in higher resolution if needed (and get centered in thumbnail items instead).

## Custom Image Loader

We will illustrate how to implement a minimalist image loading provider. It assumes that every item contains path to image file in its Path property:

C#

    class MyImageLoadingProvider : ImageLoadingProvider
    {
        public MyLoadingProvider(BetterThumbnailBrowser thumbnailBrowser)
            : base(thumbnailBrowser)
        {
        }

        protected override void LoadItemAsync(
            BetterThumbnailBrowserItem item,
            Size targetImageSize,
            out Image image,
            out Size maximumImageSize,
            out ILoadingProviderData data)
        {
            image = Image.FromFile(item.Path);
            maximumImageSize = image.Size;
            data = null;
        }
    }

Visual Basic

    Class MyImageLoadingProvider
        Inherits ImageLoadingProvider

        Public Sub New(thumbnailBrowser As BetterThumbnailBrowser)
            MyBase.New(thumbnailBrowser)
        End Sub

        Protected Overrides Sub LoadItemAsync(
            item As BetterThumbnailBrowserItem,
            targetImageSize As Size,
            ByRef image As Image,
            ByRef maximumImageSize As Size,
            ByRef data As ILoadingProviderData)

            image = Image.FromFile(item.Path)
            maximumImageSize = image.Size
            data = Nothing

        End Sub

    End Class

As you can see, the implementation is very simple.

The entire code of LoadItemAsync consists of just providing these three parameters:

- image

  - Loaded thumbnail image.

- maximumImageSize

  - Maximum allowed thumbnail size. This is actually size of a full resolution image, even if you have loaded smaller image (a thumbnail). This value informs loaded that there is a higher resolution available and it can re-load image in higher resolution if needed.

- data

  - Custom non-image data we would like to pass to UI thread. We can use any user types, they only need to implement trivial ILoadingProviderData interface.

The LoadItemSync method need not to be provided by the user since it is already implemented in the ImageLoadingProvider. The default implementation only takes the image and maximumImageSize and sets it into respective properties of item: BetterThumbnailBrowserItem.Image and BetterThumbnailBrowserItem.MaximumImageSize.

## Loading Non-image Data

Sometimes one would like to load other than image data - for example: metadata embedded in photos, file information, retrieve item info from remote database etc.

This is where CustomLoadingProvider comes into place. It is more general, so it can be used for loading images as well. User have to provide both LoadImageAsync and LoadItemSync methods:

C#

    class MyCustomLoadingProvider : CustomLoadingProvider
    {
        public MyCustomLoadingProvider(BetterThumbnailBrowser thumbnailBrowser)
            : base(thumbnailBrowser)
        {
        }

        protected override void LoadItemAsync(
            BetterThumbnailBrowserItem item,
            Size targetImageSize,
            out ILoadingProviderData data)
        {
            // obtain custom data (this runs on background thread)
            MyLoadingProviderData myLoadingProviderData = /* obtain the data here */;

            data = myLoadingProviderData;
        }

        protected override void LoadItemSync(BetterThumbnailBrowserItem item, ILoadingProviderData data)
        {
            MyLoadingProviderData myLoadingProviderData = (MyLoadingProviderData)data;

            // set loaded data to item (this runs on main thread)
            item.Text = myLoadingProviderData.Label;
            item.ToolTips.Add(new BetterListViewToolTipInfo(BetterListViewToolTipLocation.Image, myLoadingProviderData.Description));
        }
    }

Visual Basic

    Class MyCustomLoadingProvider
        Inherits CustomLoadingProvider

        Public Sub New(thumbnailBrowser As BetterThumbnailBrowser)
            MyBase.New(thumbnailBrowser)
        End Sub

        Protected Overrides Sub LoadItemAsync(item As BetterThumbnailBrowserItem, targetImageSize As Size, ByRef data As ILoadingProviderData)

            ' obtain custom data (this runs on background thread)
            Dim myLoadingProviderData As MyLoadingProviderData = ' obtain the data here

            data = myLoadingProviderData

        End Sub

        Protected Overrides Sub LoadItemSync(item As BetterThumbnailBrowserItem, data As ILoadingProviderData)

                Dim myLoadingProviderData As MyLoadingProviderData = DirectCast(data, MyLoadingProviderData)

                ' set loaded data to item (this runs on main thread)
                item.Text = myLoadingProviderData.Label
                item.ToolTips.Add(New BetterListViewToolTipInfo(BetterListViewToolTipLocation.Image, myLoadingProviderData.Description))

        End Sub

    End Class

As you can see, we have used custom type MyLoadingProviderData to hold loaded data, which is passed to foreground thread to further processing. This type implements ILoadingProviderData - an empty interface - so its code is very simple:

C#

    class MyLoadingProviderData : ILoadingProviderData
    {
        public string Label
        {
            get;
            set;
        }

        public string Description
        {
            get;
            set;
        }
    }

Visual Basic

    Class MyLoadingProviderData
        Implements ILoadingProviderData

        Public Property Label() As String
            Get
                Return label
            End Get
            Set
                label = Value
            End Set
        End Property

        Public Property Description() As String
            Get
                Return description
            End Get
            Set
                description = Value
            End Set
        End Property

        Private label As String
        Private description As String

    End Class

## Custom Loading Order

You can specify custom order in which the items will be loaded by setting an IComparer<BetterThumbnailBrowserItem> instance in the LoadingProvider.ItemComparer property. This can result in loading images in order you want:

For example, we would like to load visible items first, then all the others. This can be done with the following comparer:

C#

    class CustomOrderItemComparer : IComparer<BetterThumbnailBrowserItem>
    {
        private readonly BetterThumbnailBrowser thumbnailBrowser;

        public CustomOrderItemComparer(BetterThumbnailBrowser thumbnailBrowser)
        {
            this.thumbnailBrowser = thumbnailBrowser;
        }

        int IComparer<BetterThumbnailBrowserItem>.Compare(BetterThumbnailBrowserItem itemA, BetterThumbnailBrowserItem itemB)
        {
            // get put visible item indices in sorted array
            ReadOnlyCollection<BetterListViewItem> visibleItems = this.thumbnailBrowser.VisibleItems;
            int[] visibleIndices = new int[visibleItems.Count];

            for (int indexItem = 0; indexItem < visibleItems.Count; indexItem++)
            {
                visibleIndices[indexItem] = visibleItems[indexItem].Index;
            }

            Array.Sort(visibleIndices);

            int valueA = ((Array.BinarySearch(visibleIndices, itemA.Index) >= 0) ? 0 : 1);
            int valueB = ((Array.BinarySearch(visibleIndices, itemB.Index) >= 0) ? 0 : 1);

            // compare items according to their visibility
            int result = valueA.CompareTo(valueB);

            if (result != 0)
            {
                return result;
            }

            // compare items according to their indices
            valueA = itemA.Index;
            valueB = itemB.Index;

            return valueA.CompareTo(valueB);
        }
    }

Visual Basic

    Class CustomOrderItemComparer
        Implements IComparer(Of BetterThumbnailBrowserItem)

        Private ReadOnly thumbnailBrowser As BetterThumbnailBrowser

        Public Sub New(thumbnailBrowser As BetterThumbnailBrowser)
            Me.thumbnailBrowser = thumbnailBrowser
        End Sub

        Private Function IComparer_Compare(itemA As BetterThumbnailBrowserItem, itemB As BetterThumbnailBrowserItem) As Integer Implements IComparer(Of BetterThumbnailBrowserItem).Compare

                ' get put visible item indices in sorted array
                Dim visibleItems As ReadOnlyCollection(Of BetterListViewItem) = Me.thumbnailBrowser.VisibleItems
                Dim visibleIndices As Integer() = New Integer(visibleItems.Count - 1) {}

                For indexItem As Integer = 0 To visibleItems.Count - 1
                    visibleIndices(indexItem) = visibleItems(indexItem).Index
                Next

                Array.Sort(visibleIndices)

                Dim valueA As Integer = (If((Array.BinarySearch(visibleIndices, itemA.Index) >= 0), 0, 1))
                Dim valueB As Integer = (If((Array.BinarySearch(visibleIndices, itemB.Index) >= 0), 0, 1))

                ' compare items according to their visibility
                Dim result As Integer = valueA.CompareTo(valueB)

                If result <> 0 Then
                    Return result
                End If

                ' compare items according to their indices
                valueA = itemA.Index
                valueB = itemB.Index

                Return valueA.CompareTo(valueB)
        End Function

    End Class

## Automatic Restaring on Scroll, Resize and Thumbnail Zoom

Sometimes we need to restart loading because of scrolling or resizing the control, or when thumbnails are zoomed. In the above example, we have loaded visible items first. When user scrolls the control, this set of visible items changes and hence we would like to restart loading.

For example, we load thumbnails only at the necessary resolution. But when the user resizes the thumbnails, we need to reload them in higher resolution.

Another case is that we use custom item loading order such that visible items are loaded first. When user scrolls the control, the order is changed and we need to restart loading.

This functionality is provided by Better Thumbnail Browser. You only need to set the following boolean properties to true:

- LoadingProvider.RestartOnScroll

  - Restart loading when the control is scrolled or resized.

- ImageLoadingProvider.RestartOnExpand

  - Restart loading when thumbnails are enlarged in size.

## Safe Cross-threaded Operations

Better Thumbnail Browser uses two mechanisms for thread synchronization: Control.Invoke and Mutex.

The Control.Invoke is used in LoadingProvider.LoadItem method. Here a BetterThumbnailBrowser instance is used as an synchronization object, then LoadItemSync is called so that images and other data can be set to items on UI thread.

Mutex is used whenever Better Thumbnail Browser works with item data. When you work with items on both loader thread and UI thread, use BetterThumbnailBrowserItem.SyncRoot as the synchronization object. Here is a sample of safely setting Path property of BetterThumbnailBrowserItem:

C#

    lock (item.SyncRoot)
    {
        item.Path = path;
    }

Visual Basic

    SyncLock item.SyncRoot
        item.Path = path
    End SyncLock

## Multi-pass Loading

In case you need to load items in multiple sweeps, you can set multiple instances of LoadingProvider in the LoadingProviders property. The MultiPassLoadingSample uses several instances of a custom loading provider, each with different image quality setting. The result is that paimages are loaded in successively higher levels of detail:

You can in which pass any item currently resides by reading the BetterThumbnailBrowserItem.PassIndex property. When item loading is restarted, the items are loaded from their current pass. To load all items again from scratch, set the PassIndex property to 0 and then restart item loading.

Better Thumbnail Browser for .NET (C#, VB) - Image thumbnail viewing and loading control
Download More Info