Exemple #1
0
        private int GetScrollValue (ScrollBarInfo info)
        {
            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo ();
            si.fMask = NativeMethods.SIF_ALL;
            si.cbSize = Marshal.SizeOf (si.GetType ());

            if (!Misc.GetScrollInfo(_hwnd, _sbFlag, ref si))
            {
                return 0;
            }

            switch (info)
            {
                case ScrollBarInfo.CurrentPosition:
                    //
                    // Builds prior to Vista 5359 failed to correctly account for RTL scrollbar layouts.
                    //
                    if ((Environment.OSVersion.Version.Major < 6) && (_sbFlag == NativeMethods.SB_HORZ) && (Misc.IsControlRTL(_parent._hwnd)))
                    {
                        return GetScrollMaxValue(si) - si.nPos;
                    }
                    return si.nPos;

                case ScrollBarInfo.MaximumPosition:
                    return GetScrollMaxValue(si);

                case ScrollBarInfo.MinimumPosition:
                    return si.nMin;

                case ScrollBarInfo.PageSize:
                    return si.nPage;

                case ScrollBarInfo.TrackPosition:
                    return si.nTrackPos;

                case ScrollBarInfo.LargeChange:
                    return si.nPage;

                case ScrollBarInfo.SmallChange:
                    return 1;
            }

            return 0;
        }
Exemple #2
0
        private void SetScrollValue (int val)
        {
            // Check if the window is disabled
            if (!SafeNativeMethods.IsWindowEnabled (_hwnd))
            {
                throw new ElementNotEnabledException();
            }

            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo ();
            si.fMask = NativeMethods.SIF_ALL;
            si.cbSize = Marshal.SizeOf (si.GetType ());

            if (!Misc.GetScrollInfo(_hwnd, _sbFlag, ref si))
            {
                throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed));
            }

            // No move, exit
            if (val == si.nPos)
            {
                return;
            }

            // NOTE:
            // Proportional scrollbars have a few key values: min, max, page, and current.
            // 
            // Min and max represent the endpoints of the scrollbar; page is the side of the thumb,
            // and current is the position of the leading edge of the thumb (top for a vert scrollbar).
            // 
            // Because of this arrangment, current can't be any value between min and max, it's actually
            // confied to min...(max-page)+1. That +1 is a quirk of the scrollbar's internal logic.
            // 
            // For example, in an edit in notepad, these might be:
            // min = 0
            // max = 33 (~total lines in file)
            // current = { any value from 0 .. 12 inclusive }
            // page = 22
            // 
            // Most controls just let the scrollbar do all the proportional logic: they pass the incoming
            // values from the scroll messages (eg as a reuslt of dragging with a mouse or from UIA's SetValue)
            // straight down to the scrollbar APIs.
            // 
            // RichEdit is different: it does its own extra 'validation', and limits the current value to
            // (max-page) - without that +1.
            //
            // The end result of this is that it's not possible using UIA to scroll a richedit to the max value:
            // the richedit is exposing (implicitly through the scrollbar APIs) a max value one higher than the
            // max value that it will actually allow.

            int max;
            string classname = Misc.GetClassName(_hwnd);
            if (classname.ToLower(System.Globalization.CultureInfo.InvariantCulture).Contains("richedit"))
            {
                max = si.nMax - si.nPage;
            }
            else
            {
                max = (si.nMax - si.nPage) + (si.nPage > 0 ? 1 : 0);
            }

            // Explicit comparisions are made to the MaxPercentage and MinPercentage,
            // so that we dont miss out the fractions
            if (val > max )
            {
                throw new ArgumentOutOfRangeException("value", val, SR.Get(SRID.RangeValueMax));
            }
            else if (val < si.nMin)
            {
                throw new ArgumentOutOfRangeException("value", val, SR.Get(SRID.RangeValueMin));
            }

            if (_sbFlag == NativeMethods.SB_CTL)
            {
                Misc.SetScrollPos(_hwnd, _sbFlag, val, true);
            }
            else
            {
                // Determine the msg from the style.
                int msg =
                    IsScrollBarVertical(_hwnd, _sbFlag) ? NativeMethods.WM_VSCROLL : NativeMethods.WM_HSCROLL;

                // An application is generally programmed to process either the
                // SB_THUMBTRACK or SB_THUMBPOSITION request code.
                int wParam = NativeMethods.Util.MAKELONG ((short) NativeMethods.SB_THUMBPOSITION, (short) val);
                Misc.ProxySendMessage(_hwnd, msg, (IntPtr)wParam, IntPtr.Zero);
                wParam = NativeMethods.Util.MAKELONG ((short) NativeMethods.SB_THUMBTRACK, (short) val);
                Misc.ProxySendMessage(_hwnd, msg, (IntPtr)wParam, IntPtr.Zero);
            }
        }
        // ------------------------------------------------------
        //
        // Private Methods
        //
        // ------------------------------------------------------

        #region Private Methods

        // Scroll by a given amount
        private void Scroll (ScrollAmount amount, int style)
        {
            IntPtr parentHwnd = _sbFlag == NativeMethods.SB_CTL ? Misc.GetWindowParent(_hwnd) : _hwnd;
            int wParam = 0;

            switch (amount)
            {
                case ScrollAmount.LargeDecrement :
                    wParam = NativeMethods.SB_PAGEUP;
                    break;

                case ScrollAmount.SmallDecrement :
                    wParam = NativeMethods.SB_LINEUP;
                    break;

                case ScrollAmount.LargeIncrement :
                    wParam = NativeMethods.SB_PAGEDOWN;
                    break;

                case ScrollAmount.SmallIncrement :
                    wParam = NativeMethods.SB_LINEDOWN;
                    break;
            }

            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo ();
            si.fMask = NativeMethods.SIF_ALL;
            si.cbSize = Marshal.SizeOf (si.GetType ());

            if (!Misc.GetScrollInfo(_hwnd, _sbFlag, ref si))
            {
                return;
            }

            // If the scrollbar is at the maximum position and the user passes
            // pagedown or linedown, just return
            if ((si.nPos == si.nMax) && (wParam == NativeMethods.SB_PAGEDOWN || wParam == NativeMethods.SB_LINEDOWN))
            {
                return;
            }

            // If the scrollbar is at the minimum position and the user passes
            // pageup or lineup, just return
            if ((si.nPos == si.nMin) && (wParam == NativeMethods.SB_PAGEUP || wParam == NativeMethods.SB_LINEUP))
            {
                return;
            }

            int msg = (style == NativeMethods.SBS_HORZ) ? NativeMethods.WM_HSCROLL : NativeMethods.WM_VSCROLL;

            Misc.ProxySendMessage(parentHwnd, msg, (IntPtr)wParam, (IntPtr)(parentHwnd == _hwnd ? IntPtr.Zero : _hwnd));
        }
Exemple #4
0
        // Check if a scroll bar is in a disabled state
        internal static bool IsScrollBarWithThumb (IntPtr hwnd, int sbFlag)
        {
            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo ();
            si.cbSize = Marshal.SizeOf (si.GetType ());
            si.fMask = NativeMethods.SIF_RANGE | NativeMethods.SIF_PAGE;

            if (!Misc.GetScrollInfo(hwnd, sbFlag, ref si))
            {
                return false;
            }

            // Check for the min / max value
            if (si.nMax != si.nMin && si.nPage != si.nMax - si.nMin + 1)
            {
                // The scroll bar is enabled, check if we have a thumb
                int idObject = sbFlag == NativeMethods.SB_VERT ? NativeMethods.OBJID_VSCROLL : sbFlag == NativeMethods.SB_HORZ ? NativeMethods.OBJID_HSCROLL : NativeMethods.OBJID_CLIENT;
                NativeMethods.ScrollBarInfo sbi = new NativeMethods.ScrollBarInfo ();
                sbi.cbSize = Marshal.SizeOf (sbi.GetType ());

                // check that the 2 buttons can hold in the scroll bar
                if (Misc.GetScrollBarInfo(hwnd, idObject, ref sbi))
                {
                    // When the scroll bar is for a listbox within a combo and it is hidden, then 
                    // GetScrollBarInfo returns true but the rectangle is boggus!
                    // 32 bits * 32 bits > 64 values
                    long area = (sbi.rcScrollBar.right - sbi.rcScrollBar.left) * (sbi.rcScrollBar.bottom - sbi.rcScrollBar.top);
                    if (area > 0 && area < 1000 * 1000)
                    {
                        NativeMethods.SIZE sizeArrow;

                        using (ThemePart themePart = new ThemePart(hwnd, "SCROLLBAR"))
                        {
                            sizeArrow = themePart.Size((int)ThemePart.SCROLLBARPARTS.SBP_ARROWBTN, 0);
                        }

                        bool fThumbVisible = false;
                        if (IsScrollBarVertical(hwnd, sbFlag))
                        {
                            fThumbVisible = (sbi.rcScrollBar.bottom - sbi.rcScrollBar.top >= 5 * sizeArrow.cy / 2);
                        }
                        else
                        {
                            fThumbVisible = (sbi.rcScrollBar.right - sbi.rcScrollBar.left >= 5 * sizeArrow.cx / 2);
                        }
                        return fThumbVisible;
                    }
                }
            }
            return false;
        }
Exemple #5
0
        // determine the number of lines that are visible in the Edit control.
        // if there is no scrollbar then this is the actual number of lines displayed.
        internal int LinesPerPage()
        {
            int linePerPage = 0;
            if (Misc.IsBitSet(WindowStyle, NativeMethods.WS_VSCROLL))
            {
                // we call GetScrollInfo and return the size of the "page"
                NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo();
                si.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(NativeMethods.ScrollInfo));
                si.fMask = NativeMethods.SIF_ALL;
                bool ok = Misc.GetScrollInfo(WindowHandle, NativeMethods.SB_VERT, ref si);
                linePerPage = ok ? si.nPage : 0;
                if (IsMultiline && linePerPage <= 0)
                {
                    linePerPage = 1;
                }
            }
            else
            {
                NativeMethods.Win32Rect rect = new NativeMethods.Win32Rect();

                if (Misc.GetClientRect(_hwnd, ref rect) && !rect.IsEmpty)
                {
                    NativeMethods.SIZE size;
                    string s = new string('E', 1);
                    GetTextExtentPoint32(s, out size);

                    if (size.cy != 0)
                    {
                        linePerPage = (rect.bottom - rect.top) / size.cy;
                    }
                }
            }
            return linePerPage;
        }
        // ------------------------------------------------------
        //
        // Internal Methods
        //
        // ------------------------------------------------------

        #region Internal Methods

        // Static implementation for the bounding rectangle. This is used by
        // ElementProviderFromPoint to avoid to have to create for a simple
        // boundary check
        // param "item", ID for the scrollbar bit
        // param "sbFlag", SBS_ WindowLong equivallent flag
        static internal Rect GetBoundingRectangle(IntPtr hwnd, ProxyFragment parent, WindowsScrollBar.ScrollBarItem item, int sbFlag)
        {
            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo ();
            si.cbSize = Marshal.SizeOf (si.GetType ());
            si.fMask = NativeMethods.SIF_RANGE;

            // If the scroll bar is disabled, we cannot have a thumb and large Increment/Decrement)
            bool fDisableScrollBar = !WindowsScrollBar.IsScrollBarWithThumb (hwnd, sbFlag);
            if (fDisableScrollBar && (item == WindowsScrollBar.ScrollBarItem.LargeDecrement || item == WindowsScrollBar.ScrollBarItem.Thumb || item == WindowsScrollBar.ScrollBarItem.LargeDecrement))
            {
                return Rect.Empty;
            }

            // If fails assume that the hwnd is invalid
            if (!Misc.GetScrollInfo(hwnd, sbFlag, ref si))
            {
                return Rect.Empty;
            }

            int idObject = sbFlag == NativeMethods.SB_VERT ? NativeMethods.OBJID_VSCROLL : sbFlag == NativeMethods.SB_HORZ ? NativeMethods.OBJID_HSCROLL : NativeMethods.OBJID_CLIENT;
            NativeMethods.ScrollBarInfo sbi = new NativeMethods.ScrollBarInfo ();
            sbi.cbSize = Marshal.SizeOf (sbi.GetType ());

            if (!Misc.GetScrollBarInfo(hwnd, idObject, ref sbi))
            {
                return Rect.Empty;
            }

            if (parent != null && parent._parent != null)
            {
                //
                // Builds prior to Vista 5359 failed to correctly account for RTL scrollbar layouts.
                //
                if ((Environment.OSVersion.Version.Major < 6) && (Misc.IsLayoutRTL(parent._parent._hwnd)))
                {
                    // Right to left mirroring style
                    Rect rcParent = parent._parent.BoundingRectangle;
                    int width = sbi.rcScrollBar.right - sbi.rcScrollBar.left;

                    if (sbFlag == NativeMethods.SB_VERT)
                    {
                        int offset = (int)rcParent.Right - sbi.rcScrollBar.right;
                        sbi.rcScrollBar.left = (int)rcParent.Left + offset;
                        sbi.rcScrollBar.right = sbi.rcScrollBar.left + width;
                    }
                    else
                    {
                        int offset = sbi.rcScrollBar.left - (int)rcParent.Left;
                        sbi.rcScrollBar.right = (int)rcParent.Right - offset;
                        sbi.rcScrollBar.left = sbi.rcScrollBar.right - width;
                    }
                }
            }

            // When the scroll bar is for a listbox within a combo and it is hidden, then 
            // GetScrollBarInfo returns true but the rectangle is boggus!
            // 32 bits * 32 bits > 64 values
            //
            // Note that this test must come after the rectangle has been normalized for RTL or it will fail 
            //
            long area = (sbi.rcScrollBar.right - sbi.rcScrollBar.left) * (sbi.rcScrollBar.bottom - sbi.rcScrollBar.top);
            if (area <= 0 || area > 1000 * 1000)
            {
                // Ridiculous value assume error
                return Rect.Empty;
            }

            if(WindowsScrollBar.IsScrollBarVertical(hwnd, sbFlag))
            {
                return GetVerticalScrollbarBitBoundingRectangle(hwnd, item, sbi);
            }
            else
            {
                return GetHorizontalScrollbarBitBoundingRectangle(hwnd, item, sbi);
            }
        }
        // Scroll control by a given amount
        static private bool ScrollCursor(IntPtr hwnd, ScrollAmount amount, int sbFlag, bool fForceResults)
        {
            // Check Param
            if (amount == ScrollAmount.NoAmount)
            {
                return true;
            }

            if (!Scrollable(hwnd, sbFlag))
            {
                return false;
            }

            // Get Max & min
            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo ();
            si.fMask = NativeMethods.SIF_ALL;
            si.cbSize = Marshal.SizeOf (si.GetType ());

            // if no scroll bar return false
            // on Win 6.0 success is false
            // on other system check through the scroll info is a scroll bar is there
            if ((!Misc.GetScrollInfo(hwnd, sbFlag, ref si) ||
                !((si.nMax != si.nMin && si.nPage != si.nMax - si.nMin + 1))))
            {
                return false;
            }

            // Get Action to perform
            int nAction;

            if (sbFlag == NativeMethods.SB_HORZ)
            {
                switch (amount)
                {
                    case ScrollAmount.SmallDecrement :
                        nAction = NativeMethods.SB_LINELEFT;
                        break;

                    case ScrollAmount.LargeDecrement :
                        nAction = NativeMethods.SB_PAGELEFT;
                        break;

                    case ScrollAmount.SmallIncrement :
                        nAction = NativeMethods.SB_LINERIGHT;
                        break;

                    case ScrollAmount.LargeIncrement :
                        nAction = NativeMethods.SB_PAGERIGHT;
                        break;

                    default :
                        return false;
                }
            }
            else
            {
                switch (amount)
                {
                    case ScrollAmount.SmallDecrement :
                        nAction = NativeMethods.SB_LINEUP;
                        break;

                    case ScrollAmount.LargeDecrement :
                        nAction = NativeMethods.SB_PAGEUP;
                        break;

                    case ScrollAmount.SmallIncrement :
                        nAction = NativeMethods.SB_LINEDOWN;
                        break;

                    case ScrollAmount.LargeIncrement :
                        nAction = NativeMethods.SB_PAGEDOWN;
                        break;

                    default :
                        return false;
                }
            }

            // Set position
            int wParam = NativeMethods.Util.MAKELONG (nAction, 0);
            int message = sbFlag == NativeMethods.SB_HORZ ? NativeMethods.WM_HSCROLL : NativeMethods.WM_VSCROLL;
            int result = Misc.ProxySendMessageInt(hwnd, message, (IntPtr)wParam, IntPtr.Zero);

            return result == 0 || fForceResults;
        }
Exemple #8
0
        private bool SetScrollPercent(double fScrollPos, int sbFlag, int cPelsAll, out int delta)
        {
            // in case of early exit no move
            delta = 0;

            // Check param
            if ((int)fScrollPos == (int)ScrollPattern.NoScroll)
            {
                return true;
            }

            if (fScrollPos < 0 || fScrollPos > 100)
            {
                throw new ArgumentOutOfRangeException(sbFlag == NativeMethods.SB_HORZ ? "horizontalPercent" : "verticalPercent", SR.Get(SRID.ScrollBarOutOfRange));
            }

            int scrollBar = sbFlag == NativeMethods.SB_HORZ ? NativeMethods.OBJID_HSCROLL : NativeMethods.OBJID_VSCROLL;

            NativeMethods.ScrollBarInfo scrollBarInfo = new NativeMethods.ScrollBarInfo();
            scrollBarInfo.cbSize = Marshal.SizeOf(scrollBarInfo.GetType());
            if (!Misc.GetScrollBarInfo(_hwnd, scrollBar, ref scrollBarInfo) ||
                (scrollBarInfo.scrollBarInfo & NativeMethods.STATE_SYSTEM_INVISIBLE) != 0 || 
                (scrollBarInfo.scrollBarInfo & NativeMethods.STATE_SYSTEM_UNAVAILABLE) != 0)
            {
                return false;
            }

            // Get scroll range
            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo (); // this is used all over

            si.cbSize = Marshal.SizeOf (si.GetType ());
            si.fMask = NativeMethods.SIF_ALL;

            // if no scroll bar return false
            // on Win 6.0 success is false
            // on other system check through the scroll info is a scroll bar is there
            if (!Misc.GetScrollInfo(_hwnd, sbFlag, ref si) ||
                !((si.nMax != si.nMin && si.nPage != si.nMax - si.nMin + 1)))
            {
                return false;
            }

            // calculate user-requested thumb position
            int deltaPage = (si.nPage > 0) ? si.nPage - 1 : 0;
            int future = (int) Math.Round (((si.nMax - deltaPage) - si.nMin) * fScrollPos / 100.0 + si.nMin);

            // delta between current and user-requested position in pixels
            // since the cPelsAll contains the dimension in pels for all items + the 2 pels of the border
            // the operation below does a trunc on purpose
            delta = (future - si.nPos) * (cPelsAll / (si.nMax + 1 - si.nMin));

            return true;
        }
        // Request to scroll a control horizontally or vertically by a specified amount.
        static private bool SetScrollPercent(IntPtr hwnd, double fScrollPos, int sbFlag, out bool forceResults)
        {
            forceResults = false;
            // Check param
            if ((int)fScrollPos == (int)ScrollPattern.NoScroll)
            {
                return true;
            }

            if (!Scrollable(hwnd, sbFlag))
            {
                return false;
            }

            if (fScrollPos < 0 || fScrollPos > 100)
            {
                throw new ArgumentOutOfRangeException(sbFlag == NativeMethods.SB_HORZ ? "horizontalPercent" : "verticalPercent", SR.Get(SRID.ScrollBarOutOfRange));
            }

            // Get Max & min                    
            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo ();
            si.fMask = NativeMethods.SIF_ALL;
            si.cbSize = Marshal.SizeOf(si.GetType ());

            // if no scroll bar return false
            // on Win 6.0 success is false
            // on other system check through the scroll info is a scroll bar is there
            if (!Misc.GetScrollInfo(hwnd, sbFlag, ref si) ||
                !((si.nMax != si.nMin && si.nPage != si.nMax - si.nMin + 1)))
            {
                return false;
            }

            // Set position
            int delta = (si.nPage > 0) ? si.nPage - 1 : 0;

            int newPos = (int) Math.Round (((si.nMax - delta) - si.nMin) * fScrollPos / 100.0 + si.nMin);

            // No move, exit
            if (newPos == si.nPos)
            {
                return true;
            }
            si.nPos = newPos;

            forceResults = true;

            int message = sbFlag == NativeMethods.SB_HORZ ? NativeMethods.WM_HSCROLL : NativeMethods.WM_VSCROLL;

            int wParam = NativeMethods.Util.MAKELONG(NativeMethods.SB_THUMBPOSITION, si.nPos);
            bool fRet = Misc.ProxySendMessageInt(hwnd, message, (IntPtr)wParam, IntPtr.Zero) == 0;

            if (fRet && Misc.GetScrollInfo(hwnd, sbFlag, ref si) && si.nPos != newPos)
            {
                // [....] treeview has some problems.  The first is that the SendMessage with WM_HSCROLL/WM_VSCROLL
                // with SB_THUMBPOSITION is not moving the scroll position. The second problem is that SetScrollInfo()
                // lose the theming for the scroll bars and it really does not move the scroll position.  The
                // scrollbars change but it does not scroll the treeview control.

                int prevPos = newPos;
                ScrollAmount prevAmount = si.nPos > newPos ? ScrollAmount.SmallDecrement : ScrollAmount.SmallIncrement;
                do
                {
                    ScrollAmount amount = si.nPos > newPos ? ScrollAmount.SmallDecrement : ScrollAmount.SmallIncrement;

                    // If we were moving in one direction and overshoot, break to prevent getting into infant loop.
                    // If ScrollCursor() can not set the new position, also break to prevent infant loop.
                    if (prevAmount != amount || prevPos == si.nPos)
                    {
                        break;
                    }
                    prevPos = si.nPos;
                    fRet = ScrollCursor(hwnd, amount, sbFlag, forceResults);
                } while (fRet && Misc.GetScrollInfo(hwnd, sbFlag, ref si) && si.nPos != newPos);
            }

            return fRet;
        }
        // View Size
        static private double ScrollViewSize(IntPtr hwnd, int sbFlag)
        {
            // Get scroll range and page size
            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo ();
            si.cbSize = Marshal.SizeOf (si.GetType ());
            si.fMask = NativeMethods.SIF_RANGE | NativeMethods.SIF_PAGE;

            if (!Misc.GetScrollInfo(hwnd, sbFlag, ref si) || (si.nMax == si.nMin))
            {
                return 100.0;
            }
            else
            {
                // "+1" because nPage can be 0 to nMax-nMin+1
                int nPage = si.nPage > 0 ? si.nPage : 1;

                return (100.0 * nPage) / (si.nMax + 1 - si.nMin);
            }
        }
        // ------------------------------------------------------
        //
        // Private Methods
        //
        // ------------------------------------------------------

        // Retrieve the scrollbar position in the [0..100]% range
        static private double GetScrollInfo(IntPtr hwnd, int sbFlag)
        {
            // check if there is a scrollbar 
            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo ();

            si.fMask = NativeMethods.SIF_ALL;
            si.cbSize = Marshal.SizeOf (si.GetType ());

            if (Misc.GetScrollInfo(hwnd, sbFlag, ref si))
            {
                if (si.nMax != si.nMin && si.nPage != si.nMax - si.nMin + 1)
                {
                    int delta;

                    // NOTE:
                    // Proportional scrollbars have a few key values: min, max, page, and current.
                    // 
                    // Min and max represent the endpoints of the scrollbar; page is the side of the thumb,
                    // and current is the position of the leading edge of the thumb (top for a vert scrollbar).
                    // 
                    // Because of this arrangment, current can't be any value between min and max, it's actually
                    // confied to min...(max-page)+1. That +1 is a quirk of the scrollbar's internal logic.
                    // 
                    // For example, in an edit in notepad, these might be:
                    // min = 0
                    // max = 33 (~total lines in file)
                    // current = { any value from 0 .. 12 inclusive }
                    // page = 22
                    // 
                    // Most controls just let the scrollbar do all the proportional logic: they pass the incoming
                    // values from the scroll messages (eg as a reuslt of dragging with a mouse or from UIA's SetValue)
                    // straight down to the scrollbar APIs.
                    // 
                    // RichEdit is different: it does its own extra 'validation', and limits the current value to
                    // (max-page) - without that +1.
                    //
                    // The end result of this is that it's not possible using UIA to scroll a richedit to the max value:
                    // the richedit is exposing (implicitly through the scrollbar APIs) a max value one higher than the
                    // max value that it will actually allow.

                    string classname = Misc.GetClassName(hwnd);
                    if (classname.ToLower(System.Globalization.CultureInfo.InvariantCulture).Contains("richedit"))
                    {
                        delta = (si.nPage > 0) ? si.nPage : 0;
                    }
                    else
                    {
                        delta = (si.nPage > 0) ? si.nPage - 1 : 0;
                    }

                    return 100.0 * (si.nPos - si.nMin) / ((si.nMax - delta) - si.nMin);
                }
            }
            return (double)ScrollPattern.NoScroll;
        }
        // Finds if a control can be scrolled
        static internal bool Scrollable (IntPtr hwnd, int sbFlag)
        {
            int style = Misc.GetWindowStyle(hwnd);

            if ((sbFlag == NativeMethods.SB_HORZ && !Misc.IsBitSet(style, NativeMethods.WS_HSCROLL)) ||
                (sbFlag == NativeMethods.SB_VERT && !Misc.IsBitSet(style, NativeMethods.WS_VSCROLL)))
            {
                return false;
            }

            if (!Misc.IsEnabled(hwnd))
            {
                return false;
            }

            // Check if the scroll info shows the scroll bar as enabled.
            bool scrollBarEnabled = false;
            NativeMethods.ScrollBarInfo sbi = new NativeMethods.ScrollBarInfo();
            sbi.cbSize = Marshal.SizeOf(sbi.GetType());
            int scrollBarObjectId =
                (sbFlag == NativeMethods.SB_VERT) ? NativeMethods.OBJID_VSCROLL : NativeMethods.OBJID_HSCROLL;
            if (Misc.GetScrollBarInfo(hwnd, scrollBarObjectId, ref sbi))
            {
                scrollBarEnabled =
                    !Misc.IsBitSet(sbi.scrollBarInfo, NativeMethods.STATE_SYSTEM_UNAVAILABLE);
            }

            if (!scrollBarEnabled)
            {
                return false;
            }

            // Get scroll range
            NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo ();
            si.cbSize = Marshal.SizeOf (si.GetType ());
            si.fMask = NativeMethods.SIF_ALL;

            if (!Misc.GetScrollInfo(hwnd, sbFlag, ref si))
            {
                return false;
            }

            return (si.nMax != si.nMin && si.nPage != si.nMax - si.nMin + 1);
        }
            //------------------------------------------------------
            //
            //  Patterns Implementation
            //
            //------------------------------------------------------

            #region RangeValue Pattern

            void IRangeValueProvider.SetValue(double val)
            {
                // Check if the window is disabled
                if (!SafeNativeMethods.IsWindowEnabled (_hwnd))
                {
                    throw new ElementNotEnabledException();
                }

                NativeMethods.ScrollInfo si = new NativeMethods.ScrollInfo ();
                si.fMask = NativeMethods.SIF_ALL;
                si.cbSize = Marshal.SizeOf (si.GetType ());

                if (!Misc.GetScrollInfo(_hwnd, _sbFlag, ref si))
                {
                    return;
                }

                int pos = (int)val;
                // Throw if val is greater than the maximum or less than the minimum.
                // See remarks for WindowsScrollBar.GetScrollValue(ScrollBarInfo.MaximumPosition)
                // regarding this calculation of the allowed maximum.
                if (pos > si.nMax - si.nPage + (si.nPage > 0 ? 1 : 0))
                {
                    throw new ArgumentOutOfRangeException("value", val, SR.Get(SRID.RangeValueMax));
                }
                else if (pos < si.nMin)
                {
                    throw new ArgumentOutOfRangeException("value", val, SR.Get(SRID.RangeValueMin));
                }

                // LVM_SCROLL does not work in mode Report, use SetScrollPos instead
                bool isVerticalScroll = IsScrollBarVertical(_hwnd, _sbFlag);
                if (isVerticalScroll && WindowsListView.InReportView (_hwnd))
                {
                    Misc.SetScrollPos(_hwnd, _sbFlag, pos, true);
                    return;
                }

                // get the "full size" of the list-view
                int size = WindowsListView.ApproximateViewRect (_hwnd);

                // delta between current and user-requested position in pixels
                // since the cPelsAll contains the dimension in pels for all items + the 2 pels of the border
                // the operation below does a trunc on purpose
                int dx = 0, dy = 0;
                if (!isVerticalScroll)
                {
                    int cPelsAll = NativeMethods.Util.LOWORD (size);

                    dx = (int)((pos - si.nPos) * ((double)cPelsAll / (si.nMax + 1 - si.nMin)));
                }
                else
                {
                    int cPelsAll = NativeMethods.Util.HIWORD (size);

                    dy = (int)((pos - si.nPos) * ((double)cPelsAll / (si.nMax + 1 - si.nMin)));
                }

                if (WindowsListView.Scroll (_hwnd, (IntPtr) dx, (IntPtr) dy))
                {
                    // Check the result, on occasion the result will be different to given value
                    // a-jeanp: I played a lot to figure out what it is not the case.
                    // I am giving up and issuing instead a second call with a new delta.
                    if (!Misc.GetScrollInfo(_hwnd, _sbFlag, ref si))
                    {
                        return;
                    }

                    if (si.nPos != pos)
                    {
                        if (!isVerticalScroll)
                        {
                            int cPelsAll = NativeMethods.Util.LOWORD (size);

                            dx = (pos - si.nPos) * (cPelsAll / (si.nMax + 1 - si.nMin));
                        }
                        else
                        {
                            int cPelsAll = NativeMethods.Util.HIWORD (size);

                            dy = (pos - si.nPos) * (cPelsAll / (si.nMax + 1 - si.nMin));
                        }

                        WindowsListView.Scroll (_hwnd, (IntPtr) dx, (IntPtr) dy);
                    }
                }
            }