/// <include file='doc\ScrollBar.uex' path='docs/doc[@for="ScrollBar.UpdateScrollInfo"]/*' /> /// <devdoc> /// Internal helper method /// </devdoc> /// <internalonly/> protected void UpdateScrollInfo() { if (IsHandleCreated && Enabled) { NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); si.cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO)); si.fMask = NativeMethods.SIF_ALL; si.nMin = minimum; si.nMax = maximum; si.nPage = LargeChange; if (RightToLeft == RightToLeft.Yes) { // Reflect the scrollbar position horizontally on an Rtl system si.nPos = ReflectPosition(value); } else { si.nPos = value; } si.nTrackPos = 0; UnsafeNativeMethods.SetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_CTL, si, true); } }
private void richTextBoxOutput_VScroll(object sender, EventArgs e) { var info = new NativeMethods.SCROLLINFO(); info.cbSize = Convert.ToUInt32(Marshal.SizeOf(info)); info.fMask = NativeMethods.ScrollBarFlags.SIF_ALL; NativeMethods.GetScrollInfo(richTextBoxOutput.Handle, NativeMethods.ScrollBarConsts.SB_VERT, ref info); autoscroll = (info.nTrackPos + richTextBoxOutput.Font.Height > info.nMax - richTextBoxOutput.ClientSize.Height); }
public static bool ReachedBottom(RichTextBox rtb) { var scrollInfo = new NativeMethods.SCROLLINFO(); scrollInfo.cbSize = Marshal.SizeOf(scrollInfo); //SIF_RANGE = 0x1, SIF_TRACKPOS = 0x10, SIF_PAGE= 0x2 scrollInfo.fMask = 0x10 | 0x1 | 0x2; NativeMethods.GetScrollInfo(rtb.Handle, 1, ref scrollInfo); //nBar = 1 -> VScrollbar return(scrollInfo.max <= scrollInfo.nTrackPos + scrollInfo.nPage); }
/// <include file='doc\ScrollProperties.uex' path='docs/doc[@for="ScrollProperties.UpdateScrollInfo"]/*' /> /// <devdoc> /// Internal helper method /// </devdoc> /// <internalonly/> internal void UpdateScrollInfo() { if (parent.IsHandleCreated && visible) { NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); si.cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO)); si.fMask = NativeMethods.SIF_ALL; si.nMin = minimum; si.nMax = maximum; si.nPage = (parent.AutoScroll) ? PageSize : LargeChange; si.nPos = value; si.nTrackPos = 0; UnsafeNativeMethods.SetScrollInfo(new HandleRef(parent, parent.Handle), Orientation, si, true); } }
/// <summary> /// Append and scroll Textbox if needed. /// </summary> /// <param name="str">String to append.</param> private void Print(string str) { richTextBoxOutput.InvokeIfRequired(() => { richTextBoxOutput.AppendText(str); if (autoscroll) { var info = new NativeMethods.SCROLLINFO(); info.cbSize = Convert.ToUInt32(Marshal.SizeOf(info)); info.fMask = NativeMethods.ScrollBarFlags.SIF_RANGE; NativeMethods.GetScrollInfo(richTextBoxOutput.Handle, NativeMethods.ScrollBarConsts.SB_VERT, ref info); NativeMethods.PostMessage(richTextBoxOutput.Handle, NativeMethods.Win32Msgs.WM_VSCROLL, NativeMethods.MakeWParam((uint)NativeMethods.ScrollBarCmds.SB_THUMBPOSITION, (uint)info.nMax), IntPtr.Zero); } }); }
internal void SetVirtualSizeNoInvalidate(Size value) { virtualSize = value; SetPositionNoInvalidate(position); // Make sure it's within range NativeMethods.SCROLLINFO info = new NativeMethods.SCROLLINFO(); info.fMask = NativeMethods.SIF_RANGE | NativeMethods.SIF_PAGE; info.nMin = 0; info.nMax = Math.Max(Height, virtualSize.Height) - 1; info.nPage = Height; UnsafeNativeMethods.SetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_VERT, info, true); info.fMask = NativeMethods.SIF_RANGE | NativeMethods.SIF_PAGE; info.nMin = 0; info.nMax = Math.Max(Width, virtualSize.Width) - 1; info.nPage = Width; UnsafeNativeMethods.SetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_HORZ, info, true); }
private bool IsScrollEnabled(IntPtr window, int scrollBar) { var scrollInfo = new NativeMethods.SCROLLINFO { fMask = NativeMethods.SIF_ALL }; // Lie and say yes if we cannot get the scroll info. if (!NativeMethods.GetScrollInfo(window, scrollBar, scrollInfo)) { return(true); } // Test whether the scroll bars are enabled. If the window // has SIF_DISABLENOSCROLL set, the scrollbar will be // disabled if there is nothing to scroll. In that case, we // try the parent. This prevents strange behavior with // nested scrollable controls. return((scrollInfo.nMax - scrollInfo.nMin) + 1 > scrollInfo.nPage); }
/// <include file='doc\ScrollBar.uex' path='docs/doc[@for="ScrollBar.WmReflectScroll"]/*' /> /// <devdoc> /// </devdoc> /// <internalonly/> private void WmReflectScroll(ref Message m) { ScrollEventType id = (ScrollEventType)NativeMethods.Util.LOWORD(m.WParam); // For Rtl systems we need to swap increment and decrement // if (RightToLeft == RightToLeft.Yes) { switch (id) { case ScrollEventType.First: id = ScrollEventType.Last; break; case ScrollEventType.Last: id = ScrollEventType.First; break; case ScrollEventType.SmallDecrement: id = ScrollEventType.SmallIncrement; break; case ScrollEventType.SmallIncrement: id = ScrollEventType.SmallDecrement; break; case ScrollEventType.LargeDecrement: id = ScrollEventType.LargeIncrement; break; case ScrollEventType.LargeIncrement: id = ScrollEventType.LargeDecrement; break; } } int newValue = value; // The ScrollEventArgs constants are defined in terms of the windows // messages.. this eliminates confusion between the VSCROLL and // HSCROLL constants, which are identical. // switch (id) { case ScrollEventType.First: newValue = minimum; break; case ScrollEventType.Last: newValue = maximum - LargeChange + 1; // si.nMax - si.nPage + 1; break; case ScrollEventType.SmallDecrement: newValue = Math.Max(value - SmallChange, minimum); break; case ScrollEventType.SmallIncrement: newValue = Math.Min(value + SmallChange, maximum - LargeChange + 1); // max - lChange + 1); break; case ScrollEventType.LargeDecrement: newValue = Math.Max(value - LargeChange, minimum); break; case ScrollEventType.LargeIncrement: newValue = Math.Min(value + LargeChange, maximum - LargeChange + 1); // si.nPos + si.nPage,si.nMax - si.nPage + 1); break; case ScrollEventType.ThumbPosition: case ScrollEventType.ThumbTrack: NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); si.fMask = NativeMethods.SIF_TRACKPOS; SafeNativeMethods.GetScrollInfo(new HandleRef(this, Handle), NativeMethods.SB_CTL, si); if (RightToLeft == RightToLeft.Yes) { newValue = ReflectPosition(si.nTrackPos); } else { newValue = si.nTrackPos; } break; } ScrollEventArgs se = new ScrollEventArgs(id, newValue); OnScroll(se); Value = se.NewValue; }
private int AdjustScroll(Message m, int pos, int maxPos, bool horizontal) { switch (NativeMethods.Util.LOWORD(m.WParam)) { case NativeMethods.SB_THUMBPOSITION: case NativeMethods.SB_THUMBTRACK: NativeMethods.SCROLLINFO si = new NativeMethods.SCROLLINFO(); si.cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO)); si.fMask = NativeMethods.SIF_TRACKPOS; int direction = horizontal ? NativeMethods.SB_HORZ : NativeMethods.SB_VERT; if (SafeNativeMethods.GetScrollInfo(new HandleRef(this, m.HWnd), direction, si)) { pos = si.nTrackPos; } else { pos = NativeMethods.Util.HIWORD(m.WParam); } break; case NativeMethods.SB_LINEUP: if (pos > SCROLL_LINE) { pos -= SCROLL_LINE; } else { pos = 0; } break; case NativeMethods.SB_LINEDOWN: if (pos < maxPos - SCROLL_LINE) { pos += SCROLL_LINE; } else { pos = maxPos; } break; case NativeMethods.SB_PAGEUP: if (pos > SCROLL_PAGE) { pos -= SCROLL_PAGE; } else { pos = 0; } break; case NativeMethods.SB_PAGEDOWN: if (pos < maxPos - SCROLL_PAGE) { pos += SCROLL_PAGE; } else { pos = maxPos; } break; } return(pos); }
private void _WndProc(ref Message m) { int lParam; switch (m.Msg) { case NativeMethods.WM_ERASEBKGND: // Avoid drawing in area already used by items. We will fully erase the item area // as part of the draw item message. var visibleItemHeight = 0; if (myTree != null) { visibleItemHeight = (myTree.VisibleItemCount - TopIndex) * myItemHeight; } // Avoid using ClientRectangle here, since this is a cached WinForms property // which may not be up to date in some cases, such as during a control resize. NativeMethods.RECT clientRect; NativeMethods.GetClientRect(Handle, out clientRect); var remainingHeight = clientRect.height - visibleItemHeight; if (remainingHeight > 0) { using (var g = Graphics.FromHdc(m.WParam)) { Brush backBrush = null; EnsureBrush(ref backBrush, BackColor); try { g.FillRectangle(backBrush, 0, visibleItemHeight, clientRect.width, remainingHeight); } finally { CleanBrush(ref backBrush, BackColor); } } } // Nonzero return value indicates we've done the erasing. m.Result = (IntPtr)1; break; case NativeMethods.WM_NCCALCSIZE: base.WndProc(ref m); if ((int)m.WParam != 0 && HeaderVisible) { Marshal.WriteInt32( m.LParam, NativeMethods.NCCALCSIZE_PARAMS.rgrc0TopOffset, HeaderHeight + Marshal.ReadInt32(m.LParam, NativeMethods.NCCALCSIZE_PARAMS.rgrc0TopOffset)); myBorderOffset = -1; } break; case NativeMethods.WM_LBUTTONUP: { var restoreSelChange = GetStateFlag(VTCStateFlags.SelChangeFromMouse); if (restoreSelChange) { SetStateFlag(VTCStateFlags.SelChangeFromMouse, true); } base.WndProc(ref m); if (restoreSelChange) { SetStateFlag(VTCStateFlags.SelChangeFromMouse, false); } } break; case NativeMethods.WM_KILLFOCUS: case NativeMethods.WM_SETFOCUS: // If we're setting focus to a fake focus control, then don't // redraw now. In particular, doing this with the header control // will mess up the splitter lines. if (!GetStateFlag(VTCStateFlags.RedrawOff) && myUpdateCount == 0 && (m.WParam == IntPtr.Zero || !(IsDrawWithFocusWindow(m.WParam) || IsInPlaceEditWindow(m.WParam)))) { RedrawVisibleSelectedItems(); } // notify accessibility clients of focus change to caret item if (m.Msg == NativeMethods.WM_SETFOCUS && CurrentIndex != VirtualTreeConstant.NullIndex) { if (VirtualTreeAccEvents.ShouldNotify(VirtualTreeAccEvents.eventObjectFocus, this)) { VirtualTreeAccEvents.Notify( VirtualTreeAccEvents.eventObjectFocus, CurrentIndex, CurrentColumn, this); } } // We don't call the base on a WM_SETFOCUS, since DefWndProc will send a focus WinEvents, // which we don't want. We still need to raise events, however. if (m.Msg == NativeMethods.WM_SETFOCUS) { OnGotFocus(EventArgs.Empty); } else { OnLostFocus(EventArgs.Empty); } break; case NativeMethods.WM_REFLECT + NativeMethods.WM_CHARTOITEM: m.Result = (IntPtr)(-1); break; case NativeMethods.WM_REFLECT + NativeMethods.WM_DRAWITEM: WmReflectDrawItem(ref m); break; case NativeMethods.WM_LBUTTONDBLCLK: SetStyle(ControlStyles.UserMouse, true); SetStateFlag(VTCStateFlags.CallDefWndProc, true); base.WndProc(ref m); if (GetStateFlag(VTCStateFlags.CallDefWndProc)) { DefWndProc(ref m); } SetStyle(ControlStyles.UserMouse, false); break; case NativeMethods.WM_LBUTTONDOWN: { // Handling the mouse down is a very delicate operation that // involves letting us, and winforms all having a shot // at the operation. WinForms does some useful things and sets // internal state indicating the the mouse is down (and gets // the capture, etc), but we don't want them to call DefWndProc // because that will automatically do things like change selection // when we click on an expansion button, or drag select in multiselect // mode when we're trying to do a drag drop. The UserMouse=true style // used here stops the control from calling DefWndProc so we can decide // ourselves if it needs to be called or not to do standard selection // processing for the control. Note that base.WndProc call here sets // the WinForms state and calls our OnMouseDown, where most of the // processing is done. var startIndex = CurrentIndex; var skipStandardProcessing = true; var localCallDefWndProc = true; SetStyle(ControlStyles.UserMouse, true); SetStateFlag(VTCStateFlags.CallDefWndProc, true); SetStateFlag(VTCStateFlags.SelChangeFromMouse, true); SetStateFlag(VTCStateFlags.StandardLButtonDownProcessing, false); // WinForms discards the modifer key information in the original message. Capture that here for use in OnMouseDown. if (0 != ((int)m.WParam & NativeMethods.MK_CONTROL)) { SetStateFlag(VTCStateFlags.MouseButtonDownCtrl, true); } if (0 != ((int)m.WParam & NativeMethods.MK_SHIFT)) { SetStateFlag(VTCStateFlags.MouseButtonDownShift, true); } try { base.WndProc(ref m); localCallDefWndProc = GetStateFlag(VTCStateFlags.CallDefWndProc); if (localCallDefWndProc) { if (myMouseDownHitInfo.HitTarget != VirtualTreeHitTargets.NoWhere) { skipStandardProcessing = false; if (RequireColumnSwitchForSelection(ref myMouseDownHitInfo)) { var columnChangeOnly = startIndex == myMouseDownHitInfo.Row; SetSelectionColumn(myMouseDownHitInfo.DisplayColumn, false); // don't fire events here, we'll fire when we set selection. if (columnChangeOnly) { startIndex = -1; } } } } else if (GetStateFlag(VTCStateFlags.StandardLButtonDownProcessing)) { // The selection pieces have already been taken care of, to the drag-drop and // label edit bits. skipStandardProcessing = false; startIndex = -1; } SetStyle(ControlStyles.UserMouse, false); var checkForDrag = !skipStandardProcessing && IsDragSource; var multiSelect = GetStyleFlag(VTCStyleFlags.MultiSelect); var fContinue = true; if (checkForDrag && multiSelect) { // MultiSelect drag needs to be initiated before the DoSelectionChangeFromMouse // call, not after. In the single select case, the default processing // will select an item. But in the multiselect case, it will deselect // the item if it is already selected, so we need to check for // starting the drag before doing the default processing. if (IsSelected(myMouseDownHitInfo.Row)) { checkForDrag = false; var p = new Point((int)m.LParam); if (CheckForDragBegin( p.X, p.Y, myMouseDownHitInfo.Row, myMouseDownHitInfo.DisplayColumn, ref localCallDefWndProc)) { // Code repeated below var dragData = PopulateDragData( myMouseDownHitInfo.Row, myMouseDownHitInfo.DisplayColumn, DragReason.DragDrop); if (!dragData.IsEmpty) { if (startIndex != -1 && startIndex != myMouseDownHitInfo.Row) { DoSelectionChanged(); } DoDragDrop(dragData.Data, dragData.AllowedEffects); fContinue = false; } } } else { checkForDrag = localCallDefWndProc; // Nothing is going to change } } if (fContinue && localCallDefWndProc) { DoSelectionChangeFromMouse( ref myMouseDownHitInfo, 0 != ((int)m.WParam & NativeMethods.MK_SHIFT), 0 != ((int)m.WParam & NativeMethods.MK_CONTROL), MouseButtons.Left); if (startIndex == -1) { // startIndex == -1 indicates we already set column selection above, and the caret index isn't changing. // need to fire events here, since to DoSelectionChangeFromMouse, it will look like nothing changed. DoSelectionChanged(); } } if (checkForDrag && ((myMouseDownHitInfo.HitTarget & (VirtualTreeHitTargets.OnItem | VirtualTreeHitTargets.OnItemRight | VirtualTreeHitTargets.OnItemLeft)) != 0) && (!multiSelect || IsSelected(myMouseDownHitInfo.Row))) { var p = new Point((int)m.LParam); var dummyPending = false; if (CheckForDragBegin( p.X, p.Y, myMouseDownHitInfo.Row, myMouseDownHitInfo.DisplayColumn, ref dummyPending)) { // Code copied from above var dragData = PopulateDragData( myMouseDownHitInfo.Row, myMouseDownHitInfo.DisplayColumn, DragReason.DragDrop); if (!dragData.IsEmpty) { if (startIndex != -1 && startIndex != myMouseDownHitInfo.Row) { DoSelectionChanged(); } DoDragDrop(dragData.Data, dragData.AllowedEffects); fContinue = false; } } } if (!skipStandardProcessing && fContinue && (0 != (myMouseDownHitInfo.HitTarget & (VirtualTreeHitTargets.OnItemLabel | VirtualTreeHitTargets.OnItemRight | VirtualTreeHitTargets.OnItemLeft)))) { StartEditTimer(myMouseDownHitInfo.Row, m.Msg); } } finally { SetStateFlag(VTCStateFlags.MouseButtonDownCtrl, false); SetStateFlag(VTCStateFlags.MouseButtonDownShift, false); SetStateFlag(VTCStateFlags.SelChangeFromMouse, false); } break; } case NativeMethods.WM_RBUTTONDOWN: // Focus doesn't happen automatically on a right // mouse click. Set this up front before other // mouse events start firing. if (!Focused) { Focus(); } // WinForms discards the modifer key information in the original message. Capture that here for use in OnMouseDown. if (0 != ((int)m.WParam & NativeMethods.MK_CONTROL)) { SetStateFlag(VTCStateFlags.MouseButtonDownCtrl, true); } if (0 != ((int)m.WParam & NativeMethods.MK_SHIFT)) { SetStateFlag(VTCStateFlags.MouseButtonDownShift, true); } try { base.WndProc(ref m); } finally { SetStateFlag(VTCStateFlags.MouseButtonDownCtrl, false); SetStateFlag(VTCStateFlags.MouseButtonDownShift, false); } break; case NativeMethods.WM_MOUSEMOVE: if (myTooltip != null || StandardCheckBoxes) { // update tooltips and checkbox hot-track state. UpdateMouseTargets(); if (myTooltip != null) { myTooltip.Relay(m.WParam, m.LParam); } } base.WndProc(ref m); break; case NativeMethods.WM_NCMOUSEMOVE: if (myTooltip != null || StandardCheckBoxes) { // update tooltips and checkbox hot-track state. UpdateMouseTargets(); if (myTooltip != null) { myTooltip.Relay(m.WParam, m.LParam); } } base.WndProc(ref m); break; case NativeMethods.WM_MOUSELEAVE: if (myRawMouseOverIndex != -1) { // make sure to invalidate checkbox hot-track region if necessary when the mouse leaves the control. UpdateMouseTargets(); } base.WndProc(ref m); break; case NativeMethods.WM_NOTIFY: // NMHDR is layed out hwndFrom/idFrom/code if (myTooltip != null && myTooltip.IsHandleCreated && Marshal.ReadIntPtr(m.LParam) == myTooltip.Handle) { var fContinue = false; #if DEBUG // - to not have to compile structs used only in Asserts Debug.Assert(Marshal.OffsetOf(typeof(NativeMethods.NMHDR), "code").ToInt32() == 8); Debug.Assert(Marshal.OffsetOf(typeof(NativeMethods.TOOLTIPTEXT), "lpszText").ToInt32() == 12); #endif var code = Marshal.ReadIntPtr(m.LParam, 8).ToInt32(); switch (code) { case NativeMethods.TTN_NEEDTEXTA: case NativeMethods.TTN_NEEDTEXTW: if (myMouseOverIndex == VirtualTreeConstant.NullIndex) { if (!UpdateMouseTargets()) { fContinue = true; break; } } // This gets the active selection column, // need to support all columns. Marshal.WriteIntPtr( m.LParam, 12, myTooltip.GetTextPtr(myTree, myMouseOverIndex, myMouseOverColumn, myTipType)); break; case NativeMethods.TTN_SHOW: if (myMouseOverIndex != VirtualTreeConstant.NullIndex) { SetStateFlag(VTCStateFlags.ShowingTooltip, true); myTooltip.PositionTipWindow(this); m.Result = (IntPtr)1; } break; case NativeMethods.TTN_POP: SetStateFlag(VTCStateFlags.ShowingTooltip, false); fContinue = true; break; default: fContinue = true; break; } if (!fContinue) { break; } } base.WndProc(ref m); break; case NativeMethods.WM_WINDOWPOSCHANGING: { var flags = (NativeMethods.SetWindowPosFlags)Marshal.ReadInt32(m.LParam, NativeMethods.WINDOWPOS.flagsOffset); if (((NativeMethods.SetWindowPosFlags.SWP_NOSIZE | NativeMethods.SetWindowPosFlags.SWP_NOMOVE) != (flags & (NativeMethods.SetWindowPosFlags.SWP_NOSIZE | NativeMethods.SetWindowPosFlags.SWP_NOMOVE))) || (0 != (flags & NativeMethods.SetWindowPosFlags.SWP_FRAMECHANGED))) { // The exclusions are a strange case, but Windows was not sending a // WM_WINDOWPOSCHANGED if SetWindowPos was called with NOMOVE | NOSIZE, // so this was not getting turned back on and DrawItem was blocked until // the next size SetStateFlag(VTCStateFlags.WindowPositionChanging, true); } base.WndProc(ref m); break; } case NativeMethods.WM_WINDOWPOSCHANGED: { var oldWidth = ClientSize.Width; base.WndProc(ref m); SetStateFlag(VTCStateFlags.WindowPositionChanging, false); var flags = (NativeMethods.SetWindowPosFlags)Marshal.ReadInt32(m.LParam, NativeMethods.WINDOWPOS.flagsOffset); var sizeChanged = 0 == (flags & NativeMethods.SetWindowPosFlags.SWP_NOSIZE) && (oldWidth != ClientSize.Width && Width > 0); if (HeaderVisible && myHeaderContainer != null) { var visibleChanged = ((flags & (NativeMethods.SetWindowPosFlags.SWP_HIDEWINDOW | NativeMethods.SetWindowPosFlags.SWP_SHOWWINDOW)) != 0); var frameChanged = 0 != (flags & NativeMethods.SetWindowPosFlags.SWP_FRAMECHANGED); var positionChanged = 0 == (flags & NativeMethods.SetWindowPosFlags.SWP_NOMOVE); var zorderChanged = 0 == (flags & NativeMethods.SetWindowPosFlags.SWP_NOZORDER); if (visibleChanged) { myHeaderContainer.Visible = ((flags & NativeMethods.SetWindowPosFlags.SWP_SHOWWINDOW) != 0); } if (sizeChanged || frameChanged) { myHeaderContainer.UpdateHeaderControlPosition(true); } if (zorderChanged) { myHeaderContainer.UpdateHeaderControlZOrder(); } if (positionChanged || frameChanged || sizeChanged) { RepositionHeaderContainer(); } } if (sizeChanged) { if (myMctree != null && myUpdateCount == 0) { var columns = (myColumnPermutation != null) ? myColumnPermutation.VisibleColumnCount : myMctree.ColumnCount; if (columns > 1) { // Following are conditions where we need to refresh completely on a resize: // 1. Variable column bounds. These change when resize occurs. // 2. Left scroll bar. This may adjust column widths // 3. No vertical gridlines. This causes us to use ellipsis string trimming, which may require repaint if text was truncated. if ((myHeaderBounds.HasVariableBounds || LeftScrollBar || !HasVerticalGridLines) && 0 == (flags & NativeMethods.SetWindowPosFlags.SWP_NOREDRAW)) { Refresh(); } } } } break; } case NativeMethods.WM_SIZE: { lParam = (int)m.LParam; var curSize = new Size(NativeMethods.UnsignedLOWORD(lParam), NativeMethods.UnsignedHIWORD(lParam)); var cacheLastSize = myLastSize; var checkHScrollHack = !GetStateFlag(VTCStateFlags.InFirstWmSize) && (curSize.Width != cacheLastSize.Width); var iPrevPos = 0; var scrollRegionClean = false; if (checkHScrollHack) { checkHScrollHack = HasHorizontalScrollBar; if (checkHScrollHack) { iPrevPos = myXPos; // There are several options here to avoid the braindead (and heavily flashing) // approach of fully invalidating the window when this happens. var hRgn = NativeMethods.CreateRectRgn(0, 0, 0, 0); if (hRgn != IntPtr.Zero) { var regType = NativeMethods.GetUpdateRgn(m.HWnd, hRgn, false); switch (regType) { case NativeMethods.RegionType.Complex: case NativeMethods.RegionType.Simple: { var testRect = new NativeMethods.RECT(0, 0, cacheLastSize.Width, cacheLastSize.Height); if (NativeMethods.RectInRegion(hRgn, ref testRect)) { scrollRegionClean = false; } break; } } NativeMethods.DeleteObject(hRgn); } } } //Debug.WriteLine("WM_SIZE Before base WndProc " + myXPos.ToString() + " " + HasHorizontalScrollBar.ToString() + cacheLastSize.Width.ToString() + "/" + cacheLastSize.Height.ToString()); SetStateFlag(VTCStateFlags.InWmSize, true); var inFirstSize = !GetStateFlag(VTCStateFlags.InFirstWmSize); if (inFirstSize) { Redraw = false; SetStateFlag(VTCStateFlags.InFirstWmSize, true); } base.WndProc(ref m); if (inFirstSize) { SetStateFlag(VTCStateFlags.InFirstWmSize, false); Redraw = true; } if (GetStateFlag(VTCStateFlags.InWmSize)) { // WM_SIZE can recurse as the scrollbars come and go, use // the last pass, not the first, to record the new size, but // always use the first pass to do the scrolling. myLastSize = curSize; SetStateFlag(VTCStateFlags.InWmSize, false); } if (checkHScrollHack) { if (iPrevPos != myXPos) { if (scrollRegionClean) { var ssi = new SmoothScroll.Info(m.HWnd, myLastSize.Width - cacheLastSize.Width, 0); ssi.scrollWindowFlags = NativeMethods.ScrollWindowFlags.Invalidate | NativeMethods.ScrollWindowFlags.Erase; ssi.smoothScrollFlags = SmoothScroll.Flags.Immediate; ssi.srcRect.Width = cacheLastSize.Width; ssi.srcRect.Height = cacheLastSize.Height; Debug.WriteLine("Before Scrolling Window"); SmoothScroll.ScrollWindow(this, ref ssi); Debug.WriteLine("After Scrolling Window"); } else { NativeMethods.InvalidateRect(m.HWnd, IntPtr.Zero, true); } } } if (inFirstSize) { //Debug.WriteLine("WM_SIZE After base WndProc " + myXPos.ToString() + " " + HasHorizontalScrollBar.ToString() + myLastSize.Width.ToString() + "/" + myLastSize.Height.ToString()); SizeWnd(myLastSize.Width, myLastSize.Height); } break; } case NativeMethods.WM_HSCROLL: DismissLabelEdit(false, true); base.WndProc(ref m); if (myHeaderContainer != null) { if (ItemCount == 0) { // DefWndProc does nothing if there are no items in the tree, so we // need to do it ourselves to get the scrollbar to move. var si = new NativeMethods.SCROLLINFO(0); si.fMask = NativeMethods.ScrollInfoFlags.All; NativeMethods.GetScrollInfo(m.HWnd, NativeMethods.ScrollBarType.Horizontal, ref si); var newPos = si.nPos; switch ((NativeMethods.ScrollAction)NativeMethods.UnsignedLOWORD((int)m.WParam)) { case NativeMethods.ScrollAction.LineLeft: newPos -= HORIZONTAL_SCROLL_AMOUNT; break; case NativeMethods.ScrollAction.LineRight: newPos += HORIZONTAL_SCROLL_AMOUNT; break; case NativeMethods.ScrollAction.PageLeft: newPos -= si.nPage; break; case NativeMethods.ScrollAction.PageRight: newPos += si.nPage; break; case NativeMethods.ScrollAction.ThumbPosition: case NativeMethods.ScrollAction.ThumbTrack: newPos = si.nTrackPos; break; } if (newPos != si.nPos) { newPos = Math.Min(Math.Max(0, newPos), si.nMax - si.nPage + 1); if (newPos != si.nPos) { NativeMethods.SetScrollPos(m.HWnd, NativeMethods.ScrollBarType.Horizontal, newPos, true); } } } myHeaderContainer.UpdateHeaderControlPosition(false); } // wParam = (int)m.WParam; // HorzScroll((NativeMethods.ScrollAction)NativeMethods.LOWORD(wParam), NativeMethods.HIWORD(wParam)); break; case NativeMethods.WM_VSCROLL: { var fEndScroll = (NativeMethods.ScrollAction)NativeMethods.UnsignedLOWORD((int)m.WParam) == NativeMethods.ScrollAction.EndScroll; if (!fEndScroll && !GetStateFlag(VTCStateFlags.InVerticalScroll)) { DismissLabelEdit(false, true); SetStateFlag(VTCStateFlags.InVerticalScroll, true); myTopStartScroll = TopIndex; } base.WndProc(ref m); if (fEndScroll) { SetStateFlag(VTCStateFlags.InVerticalScroll, false); //BeginInvoke(new CallVoid(VScrollCompleted)); VScrollCompleted(); } //VertScroll((NativeMethods.ScrollAction)NativeMethods.LOWORD(wParam), NativeMethods.HIWORD(wParam)); break; } case NativeMethods.WM_MOUSEWHEEL: DismissLabelEdit(false, true); base.WndProc(ref m); if (myHeaderContainer != null && HasHorizontalScrollBar && !HasVerticalScrollBar) { // if there's no vertical scroll bar, mousehweel scrolls horizontally so we need to // update the header. myHeaderContainer.UpdateHeaderControlPosition(false); } break; // case NativeMethods.WM_LBUTTONUP: // // Get the mouse location // // // int x = (int)(short)m.LParam; // int y = (int)m.LParam >> 16; // Point pt = new Point(x,y); // pt = PointToScreen(pt); // bool captured = Capture; // if (captured && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) // { // if (selectedItems != null) // { // selectedItems.Dirty(); // } // // if (!doubleClickFired && !ValidationCancelled) // { // OnClick(EventArgs.Empty); // } // else // { // doubleClickFired = false; // // WM_COMMAND is only fired if the user double clicks an item, // // so we can't use that as a double-click substitute // if (!ValidationCancelled) // { // OnDoubleClick(EventArgs.Empty); // } // } // } // base.WndProc(ref m); // doubleClickFired = false; // break; // // case NativeMethods.WM_LBUTTONDBLCLK: // //the Listbox gets WM_LBUTTONDOWN - WM_LBUTTONUP -WM_LBUTTONDBLCLK - WM_LBUTTONUP... // //sequence for doubleclick... // //the first WM_LBUTTONUP, resets the flag for Doubleclick // //So its necessary for us to set it again... // doubleClickFired = true; // base.WndProc(ref m); // break; // // case NativeMethods.WM_WINDOWPOSCHANGED: // base.WndProc(ref m); // if (integralHeight && fontIsChanged) // { // Height = Math.Max(Height,ItemHeight); // fontIsChanged = false; // } // break; case NativeMethods.WM_CONTEXTMENU: ContextMenuEventArgs e = null; if (m.LParam.ToInt32() != -1) { e = new ContextMenuEventArgs(NativeMethods.SignedLOWORD(m.LParam), NativeMethods.SignedHIWORD(m.LParam)); } else { // context menu was invoked through the keyboard. determine location based on // current selection within the tree. var index = CurrentIndex; Point p; if (index != -1) { var r = GetItemRectangle(CurrentIndex, CurrentColumn, true, true, null); p = PointToScreen(new Point(r.Right, r.Bottom)); } else { p = PointToScreen(Point.Empty); } e = new ContextMenuEventArgs(p.X, p.Y); } OnContextMenuInvoked(e); break; case NativeMethods.WM_GETOBJECT: OnWmGetObject(ref m); break; case NativeMethods.WM_SYSCOLORCHANGE: case NativeMethods.WM_THEMECHANGED: // theme changed, invalidate the indent bitmaps, which may contain themed button glyphs. // this will get recreated the next time it's needed for painting IndentBitmap = null; // Also, let the header control change its theme if (myHeaderContainer != null && myHeaderContainer.IsHandleCreated) { NativeMethods.SendMessage(myHeaderContainer.HeaderControl.Handle, m.Msg, m.WParam, m.LParam); } break; // UNDONE : following are provided temporarily for compatibility with existing Burton clients. // should be removed once those clients switch to the corresponding public APIs. case NativeMethods.LB_SETSEL: var select = m.WParam == IntPtr.Zero ? false : true; if (m.LParam == (IntPtr)(-1)) { if (select) { SelectAll(); } else { ClearSelection(); } } else { SelectRange((int)m.LParam, (int)m.LParam, select); } m.Result = (IntPtr)1; break; case NativeMethods.LB_SETANCHORINDEX: AnchorIndex = (int)m.WParam; m.Result = (IntPtr)1; break; case NativeMethods.LB_GETANCHORINDEX: m.Result = (IntPtr)AnchorIndex; break; default: base.WndProc(ref m); break; } }
/// <summary> /// Updates the vertical scrollbar. /// </summary> protected virtual void UpdateVerticalScrollbar() { NativeMethods.SCROLLINFO scrollInfo; int scrollHeight; int pageHeight; scrollHeight = this.ScrollSize.Height - 1; pageHeight = this.PageSize.Height; if (scrollHeight < 1) { scrollHeight = 0; pageHeight = 1; } scrollInfo = new NativeMethods.SCROLLINFO(); scrollInfo.fMask = NativeMethods.SIF.SIF_PAGE | NativeMethods.SIF.SIF_RANGE; if (AlwaysShowVScroll) { scrollInfo.fMask |= NativeMethods.SIF.SIF_DISABLENOSCROLL; } scrollInfo.nMin = 0; scrollInfo.nMax = scrollHeight; scrollInfo.nPage = pageHeight; this.SetScrollInfo(ScrollOrientation.VerticalScroll, scrollInfo, true); }
/// <summary> /// Set the given scrollbar's tracking position to the specified value /// </summary> /// <param name="scrollbar">The scrollbar.</param> /// <param name="value">The value.</param> protected virtual void ScrollTo(ScrollOrientation scrollbar, int value) { NativeMethods.SCROLLINFO oldInfo; oldInfo = this.GetScrollInfo(scrollbar); if (value > ((oldInfo.nMax - oldInfo.nMin) + 1) - oldInfo.nPage) { value = ((oldInfo.nMax - oldInfo.nMin) + 1) - oldInfo.nPage; } if (value < oldInfo.nMin) { value = oldInfo.nMin; } if (oldInfo.nPos != value) { NativeMethods.SCROLLINFO scrollInfo; scrollInfo = new NativeMethods.SCROLLINFO(); scrollInfo.fMask = NativeMethods.SIF.SIF_POS; scrollInfo.nPos = value; this.SetScrollInfo(scrollbar, scrollInfo, true); this.OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, oldInfo.nPos, value, scrollbar)); } }
private bool IsScrollEnabled(IntPtr window, int scrollBar) { var scrollInfo = new NativeMethods.SCROLLINFO { fMask = NativeMethods.SIF_ALL }; // Lie and say yes if we cannot get the scroll info. if (!NativeMethods.GetScrollInfo(window, scrollBar, scrollInfo)) return true; // Test whether the scroll bars are enabled. If the window // has SIF_DISABLENOSCROLL set, the scrollbar will be // disabled if there is nothing to scroll. In that case, we // try the parent. This prevents strange behavior with // nested scrollable controls. return (scrollInfo.nMax - scrollInfo.nMin) + 1 > scrollInfo.nPage; }
private Size GetBumpScrollSize(Point clientPoint) { var bumpScrollSize = Size.Empty; var clientSize = ClientSize; var bumpScrollRegionSize = myItemHeight / 2; // bump scroll if mouse is within half an item of the edge of the control // check for vertical scrolling if (clientPoint.Y <= bumpScrollRegionSize) { if (myYPos > 0) { bumpScrollSize.Height = -1; } } else if (clientPoint.Y <= clientSize.Height && clientPoint.Y >= (clientSize.Height - bumpScrollRegionSize)) { if (HasVerticalScrollBar) { var scrollInfo = new NativeMethods.SCROLLINFO(); scrollInfo.fMask = NativeMethods.ScrollInfoFlags.Range | NativeMethods.ScrollInfoFlags.Position; NativeMethods.GetScrollInfo(Handle, NativeMethods.ScrollBarType.Vertical, ref scrollInfo); if (scrollInfo.nPos < scrollInfo.nMax) { bumpScrollSize.Height = 1; } } } // check for horizontal scrolling if (clientPoint.X <= bumpScrollRegionSize) { if (myXPos > 0) { bumpScrollSize.Width = -1; } } else if (clientPoint.X <= clientSize.Width && clientPoint.X >= (clientSize.Width - bumpScrollRegionSize)) { if (HasHorizontalScrollBar) { var scrollInfo = new NativeMethods.SCROLLINFO(); scrollInfo.fMask = NativeMethods.ScrollInfoFlags.Range | NativeMethods.ScrollInfoFlags.Position; NativeMethods.GetScrollInfo(Handle, NativeMethods.ScrollBarType.Horizontal, ref scrollInfo); if (scrollInfo.nPos < scrollInfo.nMax) { bumpScrollSize.Width = 1; } } } return bumpScrollSize; }
private int ScrollThumbPosition(int fnBar) { var si = new NativeMethods.SCROLLINFO { fMask = NativeMethods.SIF_TRACKPOS }; NativeMethods.GetScrollInfo(new HandleRef(this, Handle), fnBar, si); return si.nTrackPos; }
public void UpdateScrollInfo() { if (!_parentControl.IsHandleCreated || !Visible) return; var si = new NativeMethods.SCROLLINFO { cbSize = Marshal.SizeOf(typeof(NativeMethods.SCROLLINFO)), fMask = NativeMethods.SIF_ALL, nMin = 0, nMax = Maximum, nPage = LargeChange, nPos = Value, nTrackPos = 0 }; NativeMethods.SetScrollInfo(new HandleRef(_parentControl, _parentControl.Handle), _orientation, si, true); }
/// <summary> /// Updates the horizontal scrollbar. /// </summary> protected virtual void UpdateHorizontalScrollbar() { NativeMethods.SCROLLINFO scrollInfo; int scrollWidth; int pageWidth; scrollWidth = this.ScrollSize.Width - 1; pageWidth = this.PageSize.Width; if (scrollWidth < 1) { scrollWidth = 0; pageWidth = 1; } scrollInfo = new NativeMethods.SCROLLINFO(); scrollInfo.fMask = NativeMethods.SIF.SIF_PAGE | NativeMethods.SIF.SIF_RANGE; if (this.AlwaysShowHScroll || !this.Enabled) { scrollInfo.fMask |= NativeMethods.SIF.SIF_DISABLENOSCROLL; } scrollInfo.nMin = 0; scrollInfo.nMax = scrollWidth; scrollInfo.nPage = pageWidth; this.SetScrollInfo(ScrollOrientation.HorizontalScroll, scrollInfo, true); }
/// <summary> /// Gets scrollbar properties /// </summary> /// <param name="scrollbar">The bar.</param> /// <returns></returns> private NativeMethods.SCROLLINFO GetScrollInfo(ScrollOrientation scrollbar) { NativeMethods.SCROLLINFO info; info = new NativeMethods.SCROLLINFO(); info.fMask = NativeMethods.SIF.SIF_ALL; NativeMethods.GetScrollInfo(this.Handle, (int)scrollbar, info); return info; }
public static extern bool GetScrollInfo(HandleRef hWnd, int fnBar, [In, Out] NativeMethods.SCROLLINFO si);