/// <summary>
        /// Recalculates the control layout.
        /// <param name="forceUpdate">true to force an update; otherwise false.</param>
        /// </summary>
        public void Update(bool forceUpdate)
        {
            if (mImageListView.ClientRectangle.Width == 0 || mImageListView.ClientRectangle.Height == 0)
                return;

            // If only item order is changed, just update visible items.
            if (!forceUpdate && !UpdateRequired && mImageListView.Items.collectionModified)
            {
                UpdateGroups();
                UpdateVisibleItems();
                return;
            }

            if (!forceUpdate && !UpdateRequired)
                return;

            // Get the item size from the renderer
            mItemSize = mImageListView.mRenderer.MeasureItem(mImageListView.View);
            cachedItemMargin = mImageListView.mRenderer.MeasureItemMargin(mImageListView.View);
            mItemSizeWithMargin = mItemSize + cachedItemMargin;

            // Cache current properties to determine if we will need an update later
            bool viewChanged = (cachedView != mImageListView.View);
            cachedView = mImageListView.View;
            cachedViewOffset = mImageListView.ViewOffset;
            cachedSize = mImageListView.ClientSize;
            cachedItemCount = mImageListView.Items.Count;
            cachedIntegralScroll = mImageListView.IntegralScroll;
            cachedItemSize = mItemSize;
            cachedGroupHeaderHeight = mImageListView.mRenderer.MeasureGroupHeaderHeight();
            cachedColumnHeaderHeight = mImageListView.mRenderer.MeasureColumnHeaderHeight();
            cachedPaneWidth = mImageListView.PaneWidth;
            cachedScrollBars = mImageListView.ScrollBars;
            mImageListView.Items.collectionModified = false;
            mImageListView.groups.collectionModified = false;

            // Calculate item area bounds
            if (!UpdateItemArea())
                return;

            // Let the calculated bounds modified by the renderer
            LayoutEventArgs eLayout = new LayoutEventArgs(mItemAreaBounds);
            mImageListView.mRenderer.OnLayout(eLayout);
            mItemAreaBounds = eLayout.ItemAreaBounds;
            if (mItemAreaBounds.Width <= 0 || mItemAreaBounds.Height <= 0)
                return;

            // Calculate the number of rows and columns
            CalculateGrid();

            // Update groups
            UpdateGroups();

            // Check if we need the scroll bars.
            // Recalculate the layout if scroll bar visibility changes.
            if (CheckScrollBars())
            {
                Update(true);
                return;
            }

            // Update scroll range
            UpdateScrollBars();

            // Cache visible items
            UpdateVisibleItems();

            // Recalculate the layout if view mode was changed
            if (viewChanged)
                Update();
        }
        /// <summary>
        /// Recalculates the control layout.
        /// </summary>
        public void Update(bool forceUpdate)
        {
            if (mImageListView.ClientRectangle.Width == 0 || mImageListView.ClientRectangle.Height == 0) return;
            if (!forceUpdate && !UpdateRequired) return;
            // Cache current properties to determine if we will need an update later
            cachedView = mImageListView.View;
            cachedViewOffset = mImageListView.ViewOffset;
            cachedSize = mImageListView.ClientSize;
            cachedItemCount = mImageListView.Items.Count;
            cachedItemSize = mImageListView.mRenderer.MeasureItem(mImageListView.View);
            cachedHeaderHeight = mImageListView.mRenderer.MeasureColumnHeaderHeight();
            cachedItemMargin = mImageListView.mRenderer.MeasureItemMargin(mImageListView.View);
            cachedPaneWidth = mImageListView.PaneWidth;
            cachedScrollBars = mImageListView.ScrollBars;
            cachedVisibleItems.Clear();

            // Calculate drawing area
            mClientArea = mImageListView.ClientRectangle;
            mItemAreaBounds = mImageListView.ClientRectangle;

            // Item size
            mItemSize = cachedItemSize;
            mItemSizeWithMargin = mItemSize + cachedItemMargin;

            // Allocate space for scrollbars
            if (mImageListView.hScrollBar.Visible)
            {
                mClientArea.Height -= mImageListView.hScrollBar.Height;
                mItemAreaBounds.Height -= mImageListView.hScrollBar.Height;
            }
            if (mImageListView.vScrollBar.Visible)
            {
                mClientArea.Width -= mImageListView.vScrollBar.Width;
                mItemAreaBounds.Width -= mImageListView.vScrollBar.Width;
            }

            // Allocate space for column headers
            if (mImageListView.View == View.Details)
            {
                int headerHeight = cachedHeaderHeight;

                // Location of the column headers
                mColumnHeaderBounds.X = mClientArea.Left - mImageListView.ViewOffset.X;
                mColumnHeaderBounds.Y = mClientArea.Top;
                mColumnHeaderBounds.Height = headerHeight;
                mColumnHeaderBounds.Width = mClientArea.Width + mImageListView.ViewOffset.X;

                mItemAreaBounds.Y += headerHeight;
                mItemAreaBounds.Height -= headerHeight;
            }
            else
            {
                mColumnHeaderBounds = Rectangle.Empty;
            }
            // Modify item area for the gallery view mode
            if (mImageListView.View == View.Gallery)
            {
                mItemAreaBounds.Height = mItemSizeWithMargin.Height;
                mItemAreaBounds.Y = mClientArea.Bottom - mItemSizeWithMargin.Height;
            }
            // Modify item area for the pane view mode
            if (mImageListView.View == View.Pane)
            {
                mItemAreaBounds.Width -= cachedPaneWidth;
                mItemAreaBounds.X += cachedPaneWidth;
            }

            if (mItemAreaBounds.Width < 1 || mItemAreaBounds.Height < 1) return;

            // Let the calculated bounds modified by the renderer
            LayoutEventArgs eLayout = new LayoutEventArgs(mItemAreaBounds);
            mImageListView.mRenderer.OnLayout(eLayout);
            mItemAreaBounds = eLayout.ItemAreaBounds;

            // Maximum number of rows and columns that can be fully displayed
            mCols = (int)System.Math.Floor((float)mItemAreaBounds.Width / (float)mItemSizeWithMargin.Width);
            mRows = (int)System.Math.Floor((float)mItemAreaBounds.Height / (float)mItemSizeWithMargin.Height);
            if (mImageListView.View == View.Details) mCols = 1;
            if (mImageListView.View == View.Gallery) mRows = 1;
            if (mCols < 1) mCols = 1;
            if (mRows < 1) mRows = 1;

            // Check if we need the horizontal scroll bar
            bool hScrollRequired = false;
            if (mImageListView.ScrollBars)
            {
                if (mImageListView.View == View.Gallery)
                    hScrollRequired = (mImageListView.Items.Count > 0) && (mCols * mRows < mImageListView.Items.Count);
                else
                    hScrollRequired = (mImageListView.Items.Count > 0) && (mItemAreaBounds.Width < mCols * mItemSizeWithMargin.Width);
            }
            if (hScrollRequired != hScrollVisible)
            {
                hScrollVisible = hScrollRequired;
                mImageListView.hScrollBar.Visible = hScrollRequired;
                Update(true);
                return;
            }

            // Check if we need the vertical scroll bar
            bool vScrollRequired = false;
            if (mImageListView.ScrollBars)
            {
                if (mImageListView.View == View.Gallery)
                    vScrollRequired = (mImageListView.Items.Count > 0) && (mItemAreaBounds.Height < mRows * mItemSizeWithMargin.Height);
                else
                    vScrollRequired = (mImageListView.Items.Count > 0) && (mCols * mRows < mImageListView.Items.Count);
            }
            if (vScrollRequired != vScrollVisible)
            {
                vScrollVisible = vScrollRequired;
                mImageListView.vScrollBar.Visible = vScrollRequired;
                Update(true);
                return;
            }

            // Horizontal scroll range
            if (mImageListView.View == View.Gallery)
            {
                mImageListView.hScrollBar.SmallChange = mItemSizeWithMargin.Width;
                mImageListView.hScrollBar.LargeChange = mItemSizeWithMargin.Width * (mItemAreaBounds.Width / mItemSizeWithMargin.Width);
                mImageListView.hScrollBar.Minimum = 0;
                mImageListView.hScrollBar.Maximum = Math.Max(0, (int)System.Math.Ceiling((float)mImageListView.Items.Count / (float)mRows) * mItemSizeWithMargin.Width - 1);
            }
            else
            {
                mImageListView.hScrollBar.SmallChange = 1;
                mImageListView.hScrollBar.LargeChange = mItemAreaBounds.Width;
                mImageListView.hScrollBar.Minimum = 0;
                mImageListView.hScrollBar.Maximum = mCols * mItemSizeWithMargin.Width;
            }
            if (mImageListView.ViewOffset.X > mImageListView.hScrollBar.Maximum - mImageListView.hScrollBar.LargeChange + 1)
            {
                mImageListView.hScrollBar.Value = mImageListView.hScrollBar.Maximum - mImageListView.hScrollBar.LargeChange + 1;
                mImageListView.ViewOffset = new Point(mImageListView.hScrollBar.Value, mImageListView.ViewOffset.Y);
            }

            // Vertical scroll range
            if (mImageListView.View == View.Gallery)
            {
                mImageListView.vScrollBar.SmallChange = 1;
                mImageListView.vScrollBar.LargeChange = mItemAreaBounds.Height;
                mImageListView.vScrollBar.Minimum = 0;
                mImageListView.vScrollBar.Maximum = mRows * mItemSizeWithMargin.Height;
            }
            else
            {
                mImageListView.vScrollBar.SmallChange = mItemSizeWithMargin.Height;
                mImageListView.vScrollBar.LargeChange = mItemSizeWithMargin.Height * (mItemAreaBounds.Height / mItemSizeWithMargin.Height);
                mImageListView.vScrollBar.Minimum = 0;
                mImageListView.vScrollBar.Maximum = Math.Max(0, (int)System.Math.Ceiling((float)mImageListView.Items.Count / (float)mCols) * mItemSizeWithMargin.Height - 1);
            }
            if (mImageListView.ViewOffset.Y > mImageListView.vScrollBar.Maximum - mImageListView.vScrollBar.LargeChange + 1)
            {
                mImageListView.vScrollBar.Value = mImageListView.vScrollBar.Maximum - mImageListView.vScrollBar.LargeChange + 1;
                mImageListView.ViewOffset = new Point(mImageListView.ViewOffset.X, mImageListView.vScrollBar.Value);
            }

            // Zero out the scrollbars if we don't have any items
            if (mImageListView.Items.Count == 0)
            {
                mImageListView.hScrollBar.Minimum = 0;
                mImageListView.hScrollBar.Maximum = 0;
                mImageListView.hScrollBar.Value = 0;
                mImageListView.vScrollBar.Minimum = 0;
                mImageListView.vScrollBar.Maximum = 0;
                mImageListView.vScrollBar.Value = 0;
                mImageListView.ViewOffset = new Point(0, 0);
            }

            // Horizontal scrollbar position
            mImageListView.hScrollBar.Left = 0;
            mImageListView.hScrollBar.Top = mImageListView.ClientRectangle.Bottom - mImageListView.hScrollBar.Height;
            mImageListView.hScrollBar.Width = mImageListView.ClientRectangle.Width - (mImageListView.vScrollBar.Visible ? mImageListView.vScrollBar.Width : 0);
            // Vertical scrollbar position
            mImageListView.vScrollBar.Left = mImageListView.ClientRectangle.Right - mImageListView.vScrollBar.Width;
            mImageListView.vScrollBar.Top = 0;
            mImageListView.vScrollBar.Height = mImageListView.ClientRectangle.Height - (mImageListView.hScrollBar.Visible ? mImageListView.hScrollBar.Height : 0);

            // Find the first and last partially visible items
            if (mImageListView.View == View.Gallery)
            {
                mFirstPartiallyVisible = (int)System.Math.Floor((float)mImageListView.ViewOffset.X / (float)mItemSizeWithMargin.Width) * mRows;
                mLastPartiallyVisible = (int)System.Math.Ceiling((float)(mImageListView.ViewOffset.X + mItemAreaBounds.Width) / (float)mItemSizeWithMargin.Width) * mRows - 1;
            }
            else
            {
                mFirstPartiallyVisible = (int)System.Math.Floor((float)mImageListView.ViewOffset.Y / (float)mItemSizeWithMargin.Height) * mCols;
                mLastPartiallyVisible = (int)System.Math.Ceiling((float)(mImageListView.ViewOffset.Y + mItemAreaBounds.Height) / (float)mItemSizeWithMargin.Height) * mCols - 1;
            }
            if (mFirstPartiallyVisible < 0) mFirstPartiallyVisible = 0;
            if (mFirstPartiallyVisible > mImageListView.Items.Count - 1) mFirstPartiallyVisible = mImageListView.Items.Count - 1;
            if (mLastPartiallyVisible < 0) mLastPartiallyVisible = 0;
            if (mLastPartiallyVisible > mImageListView.Items.Count - 1) mLastPartiallyVisible = mImageListView.Items.Count - 1;

            // Find the first and last visible items
            if (mImageListView.View == View.Gallery)
            {
                mFirstVisible = (int)System.Math.Ceiling((float)mImageListView.ViewOffset.X / (float)mItemSizeWithMargin.Width) * mRows;
                mLastVisible = (int)System.Math.Floor((float)(mImageListView.ViewOffset.X + mItemAreaBounds.Width) / (float)mItemSizeWithMargin.Width) * mRows - 1;
            }
            else
            {
                mFirstVisible = (int)System.Math.Ceiling((float)mImageListView.ViewOffset.Y / (float)mItemSizeWithMargin.Height) * mCols;
                mLastVisible = (int)System.Math.Floor((float)(mImageListView.ViewOffset.Y + mItemAreaBounds.Height) / (float)mItemSizeWithMargin.Height) * mCols - 1;
            }
            if (mFirstVisible < 0) mFirstVisible = 0;
            if (mFirstVisible > mImageListView.Items.Count - 1) mFirstVisible = mImageListView.Items.Count - 1;
            if (mLastVisible < 0) mLastVisible = 0;
            if (mLastVisible > mImageListView.Items.Count - 1) mLastVisible = mImageListView.Items.Count - 1;

            // Cache visible items
            if (mFirstPartiallyVisible >= 0 &&
                mLastPartiallyVisible >= 0 &&
                mFirstPartiallyVisible <= mImageListView.Items.Count - 1 &&
                mLastPartiallyVisible <= mImageListView.Items.Count - 1)
            {
                for (int i = mFirstPartiallyVisible; i <= mLastPartiallyVisible; i++)
                    cachedVisibleItems.Add(mImageListView.Items[i].Guid, false);
            }
        }