private void ScrollItems(int itemCount, int topShownIndex, bool scrollDown) { int dx = (scrollDown ? 1 : -1) * itemCount * myItemHeight; SmoothScroll.Info ssi = new SmoothScroll.Info(Handle, 0, (scrollDown ? 1 : -1) * itemCount * myItemHeight); ssi.clipRect = ssi.srcRect = new Rectangle(new Point(0, (topShownIndex + 1) * myItemHeight), ClientSize); ssi.scrollWindowFlags = NativeMethods.ScrollWindowFlags.Erase | NativeMethods.ScrollWindowFlags.Invalidate; SmoothScroll.ScrollWindow(ref ssi); UpdateToolTip(); }
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; } }