Files

661 lines
26 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<title>Loading Thumbnails</title>
<link href="style.css" rel="stylesheet" type="text/css">
<link href="prettify.css" type="text/css" rel="stylesheet">
<script type="text/javascript" src="prettify.js"></script><script type="text/javascript" src="lang-vb.js"></script><link href="../resources/main.css" media="screen" rel="stylesheet" type="text/css">
</head>
<body onload="prettyPrint()"><div class="placing">
<br><table class="navigation"><tr>
<td class="navigation-previous"><a href="chapter-layout.html"><strong>
« Layout</strong></a></td>
<td class="navigation-index"><a href="../../../better-thumbnail-browser/documentation.html"><strong>Index</strong></a></td>
<td class="navigation-next"><a href="chapter-text-formatting.html"><strong>Text Formatting »
</strong></a></td>
</tr></table>
<br><h1>Loading Thumbnails</h1>
<div class="banner">
<a href="../../../better-thumbnail-browser.html"><img src="../resources/better-thumbnail-browser-overview.gif" alt="Better Thumbnail Browser" class="ss"></a>
<div class="inside">
<div class="text">Better Thumbnail Browser for .NET (C#, VB) - Image thumbnail viewing and loading control</div>
<span class="dbtn-c dbtn-hilight"><span class="dbtn-w"><a href="../../../betterthumbnailbrowser.exe" class="dbtn">Download</a></span></span>
<span class="dbtn-c"><span class="dbtn-w"><a href="../../../better-thumbnail-browser.html" class="dbtn">More Info</a></span></span>
</div>
</div>
<h2>Loading Images from a Folder</h2>
<p>You can populate Better Thumbnail Browser with auto-population in
case you want to display images from an image folder:</p>
<p><strong>C#</strong></p>
<pre class="prettyprint"><code class="lang-cs">thumbnailBrowser.Path = "c:\\images";</code></pre>
<p><strong>Visual Basic</strong></p>
<pre class="prettyprint"><code class="lang-vb">thumbnailBrowser.Path = "c:\images"</code></pre>
<p>This will automatically add items to Better Thumbnail Browser and
starts loading them using default image loading provider:</p>
<p class="images"><img src="using-auto-populate.png"></p>
<h2>Starting, Stopping and Restarting Loading</h2>
<p>You can control item loading by calling <span class="code">StartLoading()</span>,
<span class="code">StopLoading()</span> and <span class="code">RestartLoading()</span>
methods.</p>
<p>The <span class="code">StartLoading()</span> method is asynchronous, so the code
will continue after the call and the thumbnail items will be loaded on a
separate thread.</p>
<p>The <span class="code">StopLoading()</span> method is synchronous - it will wait
until current item finishes loading and then stops.</p>
<p>The <span class="code">RestartLoading()</span> method has an override with
<span class="code">LoadingRestartOptions</span> parameter. It can have the following
values:</p>
<ul style="list-style:none">
<li>
<span class="code">None</span><ul style="list-style:none"><li>
<p>Restart loading if already running. Do not start loading if
not running.</p>
</li></ul>
</li>
<li>
<span class="code">StopLoading</span><ul style="list-style:none"><li>
<p>Stop loading and then start from beginning.</p>
</li></ul>
</li>
<li>
<span class="code">CanStartLoading</span><ul style="list-style:none"><li>
<p>Restart loading if already running. Start loading even if not
running.</p>
</li></ul>
</li>
</ul>
<p>When calling <span class="code">RestartLoading()</span> method without
parameters, the <span class="code">LoadingRestartOptions.None</span> is used.</p>
<h2>Loading Events</h2>
<p>During the loading process, two events are raised by the Better
ThumbnailBrowser control:</p>
<ul style="list-style:none">
<li>
<span class="code">ItemLoaded</span><ul style="list-style:none"><li>
<p>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 <strong><em><a href="chapter-loading.html#multi-pass-loading">Multi-pass Loading</a></em></strong>) 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 <strong><em><a href="chapter-loading.html#custom-loading-order">Custom Loading
Order</a></em></strong>).</p>
</li></ul>
</li>
<li>
<span class="code">LoadingFinised</span><ul style="list-style:none"><li>
<p>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 (<span class="code">LoadingFinishReason</span>
enumeration), reason why the loading ended and
<span class="code">System.Exception</span> instance in case the loading failed
because exception occured on the loading thread.</p>
</li></ul>
</li>
</ul>
<h2>Loading Options</h2>
<h2>Refreshing Delay</h2>
<p>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 <span class="code">RefreshDelay</span> property. When you
set property value to <span class="code">0</span>, the control will be refreshed
after every item loaded.</p>
<p>Better Thumbnail Browser uses <span class="code">System.Threading.Timer</span>,
so its accurracy is in order of tens of milliseconds.</p>
<h2>Loading Thread Options</h2>
<p>You can setup thumbnail item loading thread by setting the
following properties:</p>
<ul style="list-style:none">
<li>
<span class="code">LoadingThreadIsBackground</span><ul style="list-style:none"><li>
<p>Specifies whether the item loading thread runs as a
background thread.</p>
</li></ul>
</li>
<li>
<span class="code">LoadingThreadPriority</span><ul style="list-style:none"><li>
<p>Specified item loading thread priority
(<span class="code">System.Threading.ThreadPriority</span>).</p>
</li></ul>
</li>
</ul>
<h2>Skipping Individual Items</h2>
<p>If you want <strong><em>not to</em></strong> load specific thumbnail
items, set <span class="code">BetterThumbnailBrowserItem.Skip</span> property to
<span class="code">true</span>.</p>
<h2>Loading Providers</h2>
<p>Thumbnail item loading is performed by so called <strong><em>loading
providers</em></strong> (instances of <span class="code">LoadingProvider</span>
class).</p>
<p>These instances are listed in the <span class="code">LoadingProviders</span>
property. In case there is more than one loading provider, a multiple
passes over the thumbnail items are done, one for each loading
provider.</p>
<p>Better Thumbnail Browser have two bases classes for deriving your own
loading providers:</p>
<ul style="list-style:none">
<li>
<span class="code">CustomLoadingProvider</span><ul style="list-style:none"><li>
<p>Intended for loading non-image data.</p>
</li></ul>
</li>
<li>
<span class="code">ImageLoadingProvider</span><ul style="list-style:none"><li>
<p>Intended for loading image thumbnails.</p>
</li></ul>
</li>
</ul>
<p>The both of these classes are derived from
<span class="code">LoadingProvider</span> class and implement a <span class="code">LoadItem</span>
method. In this method, the loader calls <span class="code">LoadItemAsync</span>
method, which have to be provided by the user as well as the
<span class="code">LoadItemSync</span> method. The difference between these two methods
is that <span class="code">LoadItemAsync</span> is called on the (background) loader
thread and the <span class="code">LoadItemSync</span> is then called on the UI
(foreground) thread.</p>
<p>Only <span class="code">LoadItemAsync</span> and <span class="code">LoadItemSync</span>
methods need to be implemented by the user.</p>
<p>There is also <span class="code">LoadItemsSync</span> method which is used by
<span class="code">LoadItem</span> when timer is used (see <span class="code">RefreshDelay</span>
property), in that case, the loading provider calls only
<span class="code">LoadItemAsync</span> and stores the result data, and then performs
synchronization and calls <span class="code">LoadItemSync</span> in a batch.</p>
<h2>Loading Image Thumbnails in Better Thumbnail Browser</h2>
<p>Thumbnail loading is designed to be fast and effective.</p>
<p>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.</p>
<p>You can still load images in full resolution. These images will be
present in <span class="code">BetterThumbnailBrowser.Image</span> property in full
resolution, but will be resized internally for viewing.</p>
<p>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 <span class="code">maximumImageSize</span> property in the loading method.
One provides loaded image and informs about the full resolution. You can
also set <span class="code">maximumImageSize</span> 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).</p>
<h2>Custom Image Loader</h2>
<p>We will illustrate how to implement a minimalist image loading
provider. It assumes that every item contains path to image file in its
<span class="code">Path</span> property:</p>
<p><strong>C#</strong></p>
<pre class="prettyprint"><code class="lang-cs">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;
}
}</code></pre>
<p><strong>Visual Basic</strong></p>
<pre class="prettyprint"><code class="lang-vb">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</code></pre>
<p>As you can see, the implementation is very simple.</p>
<p>The entire code of <span class="code">LoadItemAsync</span> consists of just
providing these three parameters:</p>
<ul style="list-style:none">
<li>
<span class="code">image</span><ul style="list-style:none"><li>
<p>Loaded thumbnail image.</p>
</li></ul>
</li>
<li>
<span class="code">maximumImageSize </span><ul style="list-style:none"><li>
<p>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.</p>
</li></ul>
</li>
<li>
<span class="code">data</span><ul style="list-style:none"><li>
<p>Custom non-image data we would like to pass to UI thread. We
can use any user types, they only need to implement trivial
<span class="code">ILoadingProviderData</span> interface.</p>
</li></ul>
</li>
</ul>
<p>The <span class="code">LoadItemSync</span> method need not to be provided by
the user since it is already implemented in the
<span class="code">ImageLoadingProvider</span>. The default implementation only takes
the <span class="code">image</span> and <span class="code">maximumImageSize</span> and sets it
into respective properties of item:
<span class="code">BetterThumbnailBrowserItem.Image</span> and
<span class="code">BetterThumbnailBrowserItem.MaximumImageSize</span>.</p>
<h2>Loading Non-image Data</h2>
<p>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.</p>
<p>This is where <span class="code">CustomLoadingProvider</span> comes into place.
It is more general, so it can be used for loading images as well. User
have to provide both <span class="code">LoadImageAsync</span> and
<span class="code">LoadItemSync</span> methods:</p>
<p><strong>C#</strong></p>
<pre class="prettyprint"><code class="lang-cs">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));
}
}</code></pre>
<p><strong>Visual Basic</strong></p>
<pre class="prettyprint"><code class="lang-vb">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</code></pre>
<p>As you can see, we have used custom type
<span class="code">MyLoadingProviderData</span> to hold loaded data, which is passed
to foreground thread to further processing. This type implements
<span class="code">ILoadingProviderData</span> - an empty interface - so its code is
very simple:</p>
<p><strong>C#</strong></p>
<pre class="prettyprint"><code class="lang-cs">class MyLoadingProviderData : ILoadingProviderData
{
public string Label
{
get;
set;
}
public string Description
{
get;
set;
}
}</code></pre>
<p><strong>Visual Basic</strong></p>
<pre class="prettyprint"><code class="lang-vb">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</code></pre>
<h2>
<a name="custom-loading-order" id="custom-loading-order"></a>Custom Loading
Order</h2>
<p>You can specify custom order in which the items will be loaded by
setting an <span class="code">IComparer&lt;BetterThumbnailBrowserItem&gt;</span>
instance in the <span class="code">LoadingProvider.ItemComparer</span> property. This
can result in loading images in order you want:</p>
<p></p>
<p class="images"><img src="loading-custom-order.png"></p>
<p>For example, we would like to load visible items first, then all
the others. This can be done with the following comparer:</p>
<p><strong>C#</strong></p>
<pre class="prettyprint"><code class="lang-cs">class CustomOrderItemComparer : IComparer&lt;BetterThumbnailBrowserItem&gt;
{
private readonly BetterThumbnailBrowser thumbnailBrowser;
public CustomOrderItemComparer(BetterThumbnailBrowser thumbnailBrowser)
{
this.thumbnailBrowser = thumbnailBrowser;
}
int IComparer&lt;BetterThumbnailBrowserItem&gt;.Compare(BetterThumbnailBrowserItem itemA, BetterThumbnailBrowserItem itemB)
{
// get put visible item indices in sorted array
ReadOnlyCollection&lt;BetterListViewItem&gt; visibleItems = this.thumbnailBrowser.VisibleItems;
int[] visibleIndices = new int[visibleItems.Count];
for (int indexItem = 0; indexItem &lt; visibleItems.Count; indexItem++)
{
visibleIndices[indexItem] = visibleItems[indexItem].Index;
}
Array.Sort(visibleIndices);
int valueA = ((Array.BinarySearch(visibleIndices, itemA.Index) &gt;= 0) ? 0 : 1);
int valueB = ((Array.BinarySearch(visibleIndices, itemB.Index) &gt;= 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);
}
}</code></pre>
<p><strong>Visual Basic</strong></p>
<pre class="prettyprint"><code class="lang-vb">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) &gt;= 0), 0, 1))
Dim valueB As Integer = (If((Array.BinarySearch(visibleIndices, itemB.Index) &gt;= 0), 0, 1))
' compare items according to their visibility
Dim result As Integer = valueA.CompareTo(valueB)
If result &lt;&gt; 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</code></pre>
<h2>Automatic Restaring on Scroll, Resize and Thumbnail Zoom</h2>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>This functionality is provided by Better Thumbnail Browser. You
only need to set the following boolean properties to
<span class="code">true</span>:</p>
<ul style="list-style:none">
<li>
<span class="code">LoadingProvider.RestartOnScroll</span><ul style="list-style:none"><li>
<p>Restart loading when the control is scrolled or
resized.</p>
</li></ul>
</li>
<li>
<span class="code">ImageLoadingProvider.RestartOnExpand</span><ul style="list-style:none"><li>
<p>Restart loading when thumbnails are enlarged in size.</p>
</li></ul>
</li>
</ul>
<h2>Safe Cross-threaded Operations</h2>
<p>Better Thumbnail Browser uses two mechanisms for thread
synchronization: <span class="code">Control.Invoke</span> and
<span class="code">Mutex</span>.</p>
<p>The <span class="code">Control.Invoke</span> is used in
<span class="code">LoadingProvider.LoadItem</span> method. Here a
<span class="code">BetterThumbnailBrowser</span> instance is used as an synchronization
object, then <span class="code">LoadItemSync</span> is called so that images and other
data can be set to items on UI thread.</p>
<p><span class="code">Mutex</span> is used whenever Better Thumbnail Browser works
with item data. When you work with items on both loader thread and UI
thread, use <span class="code">BetterThumbnailBrowserItem.SyncRoot</span> as the
synchronization object. Here is a sample of safely setting
<span class="code">Path</span> property of
<span class="code">BetterThumbnailBrowserItem</span>:</p>
<p><strong>C#</strong></p>
<pre class="prettyprint"><code class="lang-cs">lock (item.SyncRoot)
{
item.Path = path;
}</code></pre>
<p><strong>Visual Basic</strong></p>
<pre class="prettyprint"><code class="lang-vb">SyncLock item.SyncRoot
item.Path = path
End SyncLock</code></pre>
<h2>
<a name="multi-pass-loading" id="multi-pass-loading"></a>Multi-pass Loading</h2>
<p>In case you need to load items in multiple sweeps, you can set
multiple instances of <span class="code">LoadingProvider</span> in the
<span class="code">LoadingProviders</span> property. The
<strong><em>MultiPassLoadingSample</em></strong> 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:</p>
<p class="images"><img src="loading-multipass.png"></p>
<p>You can in which pass any item currently resides by reading the
<span class="code">BetterThumbnailBrowserItem.PassIndex</span> property. When item
loading is restarted, the items are loaded from their current pass. To
load all items again from scratch, set the <span class="code">PassIndex</span> property
to <span class="code">0</span> and then restart item loading.</p>
<br><div class="banner">
<a href="../../../better-thumbnail-browser.html"><img src="../resources/better-thumbnail-browser-overview.gif" alt="Better Thumbnail Browser" class="ss"></a>
<div class="inside">
<div class="text">Better Thumbnail Browser for .NET (C#, VB) - Image thumbnail viewing and loading control</div>
<span class="dbtn-c dbtn-hilight"><span class="dbtn-w"><a href="../../../betterthumbnailbrowser.exe" class="dbtn">Download</a></span></span>
<span class="dbtn-c"><span class="dbtn-w"><a href="../../../better-thumbnail-browser.html" class="dbtn">More Info</a></span></span>
</div>
</div>
<table class="navigation"><tr>
<td class="navigation-previous"><a href="chapter-layout.html"><strong>
« Layout</strong></a></td>
<td class="navigation-index"><a href="../../../better-thumbnail-browser/documentation.html"><strong>Index</strong></a></td>
<td class="navigation-next"><a href="chapter-text-formatting.html"><strong>Text Formatting »
</strong></a></td>
</tr></table>
<br><table class="footer"><tr>
<td class="footer-title">Better Thumbnail Browser Documentation
</td>
<td class="footer-copyright">
Copyright © 2010-2012  <a href="../../../index.html" target="_blank">ComponentOwl.com</a>
</td>
</tr></table>
</div></body>
</html>