« Layout | Index | Text Formatting » |
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:
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:
Restart loading if already running. Do not start loading if not running.
Stop loading and then start from beginning.
Restart loading if already running. Start loading even if not running.
When calling RestartLoading() method without parameters, the LoadingRestartOptions.None is used.
During the loading process, two events are raised by the Better ThumbnailBrowser control:
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).
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.
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.
You can setup thumbnail item loading thread by setting the following properties:
Specifies whether the item loading thread runs as a background thread.
Specified item loading thread priority (System.Threading.ThreadPriority).
If you want not to load specific thumbnail items, set BetterThumbnailBrowserItem.Skip property to true.
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:
Intended for loading non-image data.
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.
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).
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:
Loaded thumbnail image.
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.
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.
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
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
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:
Restart loading when the control is scrolled or resized.
Restart loading when thumbnails are enlarged in size.
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
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.
« Layout | Index | Text Formatting » |
Better Thumbnail Browser Documentation | Copyright © 2010-2012 ComponentOwl.com |