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); } }
// 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; }
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; }
// ------------------------------------------------------ // // 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); } }
// ------------------------------------------------------ // // 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)); }
// 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; }
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); } } }