internal void CheckScrollbar() { if (IsVerticallyFrozen) return; if (_displayModeChangeInProgress) return; if (IsHandleCreated == false) return; Rectangle clientRect = ClientRectangle; if (clientRect.Height == 0) return; Win32Calls.SCROLLINFO si = new Win32Calls.SCROLLINFO(); si.cbSize = Marshal.SizeOf(si); // Determine if the number of properties to display can fit in the grid. int lineCount = GetVisibleLineCount(); if (lineCount * _basicPropertyHeight > clientRect.Height) { // If it can't, the scrollbar must be visible and its parameters checked if (!IsScrollbarVisible()) { Win32Calls.ShowScrollBar(Handle, Win32Calls.SB_VERT, true); // Redraw only the value column Invalidate(new Rectangle(LeftColumnWidth + LabelColumnWidth, 0, clientRect.Width - (LeftColumnWidth + LabelColumnWidth), clientRect.Height), false); Invalidate(false); // Invalidate the inplace control if (_currentInPlaceControl != null) _currentInPlaceControl.Refresh(); } // Set the scrollbar parameters si.nMin = 0; si.nMax = lineCount - 1; si.nPage = clientRect.Height / _basicPropertyHeight; si.fMask = Win32Calls.SIF_PAGE | Win32Calls.SIF_RANGE; Win32Calls.SetScrollInfo(Handle, Win32Calls.SB_VERT, ref si, true); // Enable both ends of the scrollbar Win32Calls.EnableScrollBar(Handle, Win32Calls.SB_VERT, Win32Calls.ESB_ENABLE_BOTH); // Disable the scrollbar arrow if it is at a limit Win32Calls.GetScrollInfo(Handle, Win32Calls.SB_VERT, ref si); if (Win32Calls.GetScrollPos(Handle, Win32Calls.SB_VERT) == (si.nMax - (si.nPage - 1))) Win32Calls.EnableScrollBar(Handle, Win32Calls.SB_VERT, Win32Calls.ESB_DISABLE_DOWN); else if (Win32Calls.GetScrollPos(Handle, Win32Calls.SB_VERT) == 0) Win32Calls.EnableScrollBar(Handle, Win32Calls.SB_VERT, Win32Calls.ESB_DISABLE_UP); // Control the position of the scroll PropertyVisibleDeepEnumerator enumerator = _activePropertyCollection.GetVisibleDeepEnumerator(); enumerator.MoveFirst(); int pos = 0; while (enumerator != _firstDisplayedPropertyEnumerator) { pos += enumerator.Property.HeightMultiplier; enumerator.MoveNext(); } if (_firstDisplayedPropertyEnumerator != _firstDisplayedPropertyEnumerator.RightBound) pos += _firstDisplayedLine - 1; if (pos != Win32Calls.GetScrollPos(Handle, Win32Calls.SB_VERT)) Win32Calls.SetScrollPos(Handle, Win32Calls.SB_VERT, pos, true); } else { // If the properties can all fit in the grid, the scrollbar must be hidden if (IsScrollbarVisible()) { Win32Calls.ShowScrollBar(Handle, Win32Calls.SB_VERT, false); Win32Calls.SetScrollPos(Handle, Win32Calls.SB_VERT, 0, true); // Set the scrollbar parameters to 0 so that we can test these values to // 0 elsewhere in the code (meaning no scroll is possible) si.nMin = 0; si.nMax = 0; si.nPage = 0; si.fMask = Win32Calls.SIF_PAGE | Win32Calls.SIF_RANGE; Win32Calls.SetScrollInfo(Handle, Win32Calls.SB_VERT, ref si, true); // Redraw only the value column Invalidate(new Rectangle(LeftColumnWidth + LabelColumnWidth, 0, clientRect.Width - (LeftColumnWidth + LabelColumnWidth), clientRect.Height), false); Invalidate(false); // Invalidate the inplace control if (_currentInPlaceControl != null) _currentInPlaceControl.Refresh(); } } // Resize current inplace control Property property = SelectedPropertyEnumerator.Property; if ((property != null) && property.Enabled) { Rectangle valueRect = GetItemRect(SelectedPropertyEnumerator); valueRect = property.GetValueRect(valueRect); PropertyFeel feel = property.Feel; if ((feel != null) && (feel != PropertyFeel.Empty)) { if ((valueRect.Bottom <= clientRect.Top) || (valueRect.Top >= clientRect.Bottom)) HideInPlaceControl(); else { if (_currentInPlaceControl == null) { // When we scroll and the selected property comes back into view, we need to restore the inplace control ShowInPlaceControl(SelectedPropertyEnumerator); } else { feel.MoveControl(valueRect, SelectedPropertyEnumerator); _currentInPlaceControl.Refresh(); } } } } }
protected override void WndProc(ref Message m) { if (m.Msg == Win32Calls.WM_MOUSEACTIVATE) { if (Focused == false) { Rectangle itemRect; Point pt = PointToClient(MousePosition); if (ClientRectangle.Contains(pt)) { PropertyEnumerator propEnum = PropertyItemFromPoint(pt, out itemRect); if (SelectedPropertyEnumerator != propEnum) { _dontShowInPlaceCtrlInGotFocus = true; // When we are in the dropdown part of a UITypeEditor, we want to forward the click // to the control (internalgrid or another inplace control). If Focus() is not called, // it behaves like the MSPG if (Form.ActiveForm is PropInPlaceUITypeEditor.DropDownForm) Focus(); } } } } else if (m.Msg == Win32Calls.WM_LBUTTONDOWN) { // If we don't do all this stuff here, a shift + double-click is not correctly detected // (this is used when double-clicking a property label to cycle values) _doubleClickDone = false; long now = DateTime.Now.Ticks; int x = Win32Calls.LoWord((int)m.LParam); int y = Win32Calls.HiWord((int)m.LParam); if ((_lastTimeClicked != 0) && ((int)((now - _lastTimeClicked) / 0x2710) < SystemInformation.DoubleClickTime)) { if ((Math.Abs(x - _lastPosClicked.X) < SystemInformation.DoubleClickSize.Width) && (Math.Abs(y - _lastPosClicked.Y) < SystemInformation.DoubleClickSize.Height)) _doubleClickDone = true; _lastTimeClicked = 0; _lastPosClicked = Point.Empty; } if (_doubleClickDone == false) { _lastTimeClicked = now; _lastPosClicked = new Point(x, y); } } else if (m.Msg == Win32Calls.WM_LBUTTONDBLCLK) { _lastTimeClicked = 0; } else if (m.Msg == Win32Calls.WM_NCCALCSIZE) { // if (m.WParam != IntPtr.Zero) { Win32Calls.NCCALCSIZE_PARAMS csp; csp = (Win32Calls.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(Win32Calls.NCCALCSIZE_PARAMS)); Rectangle rect = new Rectangle(csp.rgrc0.Left, csp.rgrc0.Top, csp.rgrc0.Right - csp.rgrc0.Left, csp.rgrc0.Bottom - csp.rgrc0.Top); DrawManager.NcCalcSize(ref rect); csp.rgrc0.Left = rect.X; csp.rgrc0.Right = rect.X + rect.Width; csp.rgrc0.Top = rect.Y; csp.rgrc0.Bottom = rect.Y + rect.Height; Marshal.StructureToPtr(csp, m.LParam, false); } } else if (m.Msg == Win32Calls.WM_NOTIFY) { if (m.LParam != IntPtr.Zero) { Win32Calls.NMHDR nmhdr = (Win32Calls.NMHDR)m.GetLParam(typeof(Win32Calls.NMHDR)); if (nmhdr.code == Win32Calls.TTN_SHOW) { if ((ToolTip != null) && (nmhdr.hwndFrom == ToolTip.Handle)) { ToolTip.Location = _toolTipLocation; m.Result = (IntPtr)1; return; } } } } else if (m.Msg == Win32Calls.WM_NCPAINT) { IntPtr hDC = Win32Calls.GetWindowDC(m.HWnd); Graphics graphics = Graphics.FromHdc(hDC); Win32Calls.RECT rc = new Win32Calls.RECT(); Win32Calls.GetWindowRect(m.HWnd, ref rc); Rectangle rect = new Rectangle(0, 0, rc.Right - rc.Left, rc.Bottom - rc.Top); DrawManager.DrawGridNonClientArea(graphics, rect); Win32Calls.ReleaseDC(m.HWnd, hDC); } else if (m.Msg == Win32Calls.WM_VSCROLL) { int nSBCode = Win32Calls.LoWord((int)m.WParam); if (nSBCode == Win32Calls.SB_ENDSCROLL) { _scrollingInProgress = false; if ((_parentCtrl.NavigationKeyMode == PropertyGrid.NavigationKeyModes.TabKey) && ((_parentCtrl.TabKeyNavigationMode & PropertyGrid.TabKeyNavigationModes.TabKeyWithAutoFocus) != 0)) { ShowInPlaceControl(SelectedPropertyEnumerator); } return; } _scrollingInProgress = true; Rectangle clientRect = ClientRectangle; int itemsPerPage = clientRect.Height / _basicPropertyHeight; // Get some info about the old state of the scroll Win32Calls.SCROLLINFO si = new Win32Calls.SCROLLINFO(); si.cbSize = Marshal.SizeOf(si); si.fMask = Win32Calls.SIF_PAGE | Win32Calls.SIF_RANGE; Win32Calls.GetScrollInfo(Handle, Win32Calls.SB_VERT, ref si); switch (nSBCode) { case Win32Calls.SB_LINEDOWN: { if ((si.nPage > 0) && (Win32Calls.GetScrollPos(Handle, Win32Calls.SB_VERT) < si.nMax - (si.nPage - 1))) { if (_firstDisplayedLine == FirstDisplayedProperty.Property.HeightMultiplier) { _firstDisplayedPropertyEnumerator.MoveNext(); _firstDisplayedLine = 1; } else _firstDisplayedLine++; Win32Calls.SetScrollPos(Handle, Win32Calls.SB_VERT, Win32Calls.GetScrollPos(Handle, Win32Calls.SB_VERT) + 1, true); Invalidate(); } break; } case Win32Calls.SB_LINEUP: { if (Win32Calls.GetScrollPos(Handle, Win32Calls.SB_VERT) > 0) { if (_firstDisplayedLine > 1) _firstDisplayedLine--; else { _firstDisplayedPropertyEnumerator.MovePrev(); _firstDisplayedLine = FirstDisplayedProperty.Property.HeightMultiplier; } Win32Calls.SetScrollPos(Handle, Win32Calls.SB_VERT, Win32Calls.GetScrollPos(Handle, Win32Calls.SB_VERT) - 1, true); Invalidate(); } break; } case Win32Calls.SB_PAGEDOWN: { int counter = 0; PropertyVisibleDeepEnumerator enumerator = FirstDisplayedProperty.GetVisibleDeepEnumerator(); while (enumerator != enumerator.RightBound) { Rectangle itemRect = GetAbsoluteItemRect(enumerator); if (itemRect.Bottom > clientRect.Bottom) { // We have found the last displayed property pointed by iter PropertyVisibleDeepEnumerator currentLastDisplayedPropertyEnum = enumerator.GetVisibleDeepEnumerator(); int lastItemDisplayLine = (clientRect.Bottom - itemRect.Top) / _basicPropertyHeight + 1; // We loop to make it scroll up without creating blank space at the bottom do { // Scroll one line at the top if (_firstDisplayedLine == FirstDisplayedProperty.Property.HeightMultiplier) { _firstDisplayedPropertyEnumerator.MoveNext(); _firstDisplayedLine = 1; } else _firstDisplayedLine++; // Determine the new bottom item if (lastItemDisplayLine == currentLastDisplayedPropertyEnum.Property.HeightMultiplier) { currentLastDisplayedPropertyEnum.MoveNext(); lastItemDisplayLine = 1; } else lastItemDisplayLine++; counter++; } while ((counter < itemsPerPage - 1) && (currentLastDisplayedPropertyEnum != currentLastDisplayedPropertyEnum.RightBound)); break; } enumerator.MoveNext(); } Win32Calls.SetScrollPos(Handle, Win32Calls.SB_VERT, Win32Calls.GetScrollPos(Handle, Win32Calls.SB_VERT) + counter, true); Invalidate(); break; } case Win32Calls.SB_PAGEUP: { int newPos = Win32Calls.GetScrollPos(Handle, Win32Calls.SB_VERT) - itemsPerPage + 1; // Provide a limit for the maximum position if (newPos < 0) newPos = 0; // Scroll the first displayed element for (int i = 0; i < Win32Calls.GetScrollPos(Handle, Win32Calls.SB_VERT) - newPos; i++) { if (_firstDisplayedLine > 1) _firstDisplayedLine--; else { _firstDisplayedPropertyEnumerator.MovePrev(); _firstDisplayedLine = FirstDisplayedProperty.Property.HeightMultiplier; } } Win32Calls.SetScrollPos(Handle, Win32Calls.SB_VERT, newPos, true); Invalidate(); break; } case Win32Calls.SB_THUMBTRACK: case Win32Calls.SB_THUMBPOSITION: { if (FirstDisplayedProperty != RightBound.GetVisibleDeepEnumerator()) { int nPos = Win32Calls.HiWord((int)m.WParam); int currentPos = Win32Calls.GetScrollPos(Handle, Win32Calls.SB_VERT); // Scroll the first displayed element int i = 0; for (; i < Math.Abs((nPos - currentPos)); i++) { if (nPos > currentPos) { if (FirstDisplayedProperty.Property.HeightMultiplier == _firstDisplayedLine) { _firstDisplayedPropertyEnumerator.MoveNext(); _firstDisplayedLine = 1; } else _firstDisplayedLine++; if (_firstDisplayedPropertyEnumerator == RightBound.GetVisibleDeepEnumerator()) break; } else { if (_firstDisplayedLine > 1) _firstDisplayedLine--; else { _firstDisplayedPropertyEnumerator.MovePrev(); _firstDisplayedLine = FirstDisplayedProperty.Property.HeightMultiplier; } } } if (nPos < currentPos) Win32Calls.SetScrollPos(Handle, Win32Calls.SB_VERT, currentPos - i, true); else Win32Calls.SetScrollPos(Handle, Win32Calls.SB_VERT, currentPos + i, true); Invalidate(); } break; } } if (Focused == false) Focus(); CheckScrollbar(); } base.WndProc(ref m); }
protected override void OnKeyDown(KeyEventArgs e) { // If only Control is clicked we simulate a mouse move so that the cursor could be updated on // a hyperlink property. if (e.KeyCode == Keys.ControlKey) { if (_currentInPlaceControl != null) { Win32Calls.RECT rect = new Win32Calls.RECT(); Win32Calls.GetWindowRect(_currentInPlaceControl.Handle, ref rect); Rectangle bounds = Rectangle.FromLTRB(rect.Left, rect.Top, rect.Right, rect.Bottom); if (bounds.Contains(MousePosition)) { base.OnKeyDown(e); return; } } Rectangle itemRect; Point pt = PointToClient(MousePosition); PropertyEnumerator propEnum = PropertyItemFromPoint(pt, out itemRect); if (propEnum.Property != null) propEnum.Property.OverrideCursor(pt, itemRect, propEnum); base.OnKeyDown(e); return; } Property selectedProperty = _selectedPropertyEnum.Property; if (selectedProperty != null) { // Hide the tooltip if it is visible // if (ToolTip != null) // ToolTip.SetText("", null); if (_currentInPlaceControl != null) { IInPlaceControl c = (_currentInPlaceControl as IInPlaceControl); if ((c != null) && c.OnParentKeyDown(e)) return; } switch (e.KeyCode) { case Keys.F4: { if ((_currentInPlaceControl != null) && (e.Modifiers == 0)) { IInPlaceControl c = (_currentInPlaceControl as IInPlaceControl); if (c != null) { c.OnF4(); } } break; } case Keys.Space: case Keys.Return: { if ((SelectedPropertyEnumerator.Property.CanBeDisabledManually) && (e.KeyCode == Keys.Space)) { if (SelectedPropertyEnumerator.HasParent == false) { EnableProperty(SelectedPropertyEnumerator, !SelectedPropertyEnumerator.Property.Enabled); #if _DOTNET2 e.SuppressKeyPress = true; #else HandleRef handle = new HandleRef(this, this.Handle); Win32Calls.RemovePendingMessages(handle, 0x102, 0x102); Win32Calls.RemovePendingMessages(handle, 0x106, 0x106); Win32Calls.RemovePendingMessages(handle, 0x286, 0x286); e.Handled = true; #endif } else { // The user can press on the checkbox only if its parent category is enabled if (SelectedPropertyEnumerator.Parent.Property.Enabled) { EnableProperty(SelectedPropertyEnumerator, !SelectedPropertyEnumerator.Property.Enabled); #if _DOTNET2 e.SuppressKeyPress = true; #else HandleRef handle = new HandleRef(this, this.Handle); Win32Calls.RemovePendingMessages(handle, 0x102, 0x102); Win32Calls.RemovePendingMessages(handle, 0x106, 0x106); Win32Calls.RemovePendingMessages(handle, 0x286, 0x286); e.Handled = true; #endif } } } // TODO : the following code should not be in this class. else if (SelectedPropertyEnumerator.Property is PropertyHyperLink) { if (SelectedPropertyEnumerator.Property.Enabled) { PropertyHyperLinkClickedEventArgs ev = new PropertyHyperLinkClickedEventArgs(SelectedPropertyEnumerator, SelectedPropertyEnumerator.Property.HyperLink); _parentCtrl.OnHyperLinkPropertyClicked(ev); } } else if (HasOneVisibleChild(SelectedPropertyEnumerator)) { Property property = SelectedPropertyEnumerator.Property; ExpandProperty(SelectedPropertyEnumerator, !property.Expanded); } break; } case Keys.Right: // Opens the item if it is a closed category case Keys.Add: { if ((e.KeyCode == Keys.Right) && (ModifierKeys == Keys.Control)) { SetLabelColumnWidthRatio((double)(LabelColumnWidth + 3) / (ClientRectangle.Width - LeftColumnWidth)); break; } if (SelectedPropertyEnumerator.Children.Count > 0) { Property property = SelectedPropertyEnumerator.Property; if (!property.Expanded && HasOneVisibleChild(SelectedPropertyEnumerator)) { ExpandProperty(SelectedPropertyEnumerator, true); break; } } if (e.KeyCode == Keys.Add) break; goto case Keys.Down; } case Keys.Down: // Moves the selected item down { if (e.Modifiers == Keys.Alt) { if (_currentInPlaceControl != null) { IInPlaceControl c = (_currentInPlaceControl as IInPlaceControl); if ((c != null) && !SelectedPropertyEnumerator.Property.Value.IsReadOnly(SelectedPropertyEnumerator)) { c.OnF4(); break; } } } PropertyVisibleDeepEnumerator nextEnum = new PropertyVisibleDeepEnumerator(_selectedPropertyEnum.Node); nextEnum.MoveNext(); Property nextProperty = nextEnum.Property; if (nextProperty != null) { Rectangle clientRect = ClientRectangle; Rectangle itemRect = GetAbsoluteItemRect(nextEnum); if (itemRect.Top < clientRect.Top) // Test if the item is above the top of the grid { while (true) { Win32Calls.SendMessage(Handle, Win32Calls.WM_VSCROLL, Win32Calls.MakeLong(Win32Calls.SB_LINEUP, 0), 0); itemRect = GetAbsoluteItemRect(nextEnum); if (itemRect.Top == clientRect.Top) break; } Win32Calls.SendMessage(Handle, Win32Calls.WM_VSCROLL, Win32Calls.MakeLong(Win32Calls.SB_ENDSCROLL, 0), 0); } else if (itemRect.Bottom > clientRect.Bottom) // Test if the item is below the bottom of the grid { // We scroll in a loop because the item may have a row height greater than the base row height // if the current selected item is the last one displayed // (TODO: we could be more performant by avoiding scrolls and directly calculating which property // needs to be the first displayed) _parentCtrl.BeginUpdate(); // To avoid the remancence of the inplace control do { Win32Calls.SendMessage(Handle, Win32Calls.WM_VSCROLL, Win32Calls.MakeLong(Win32Calls.SB_LINEDOWN, 0), 0); itemRect = GetAbsoluteItemRect(nextEnum); } while ((itemRect.Bottom > clientRect.Bottom) && (itemRect.Top > clientRect.Top)); Win32Calls.SendMessage(Handle, Win32Calls.WM_VSCROLL, Win32Calls.MakeLong(Win32Calls.SB_ENDSCROLL, 0), 0); _parentCtrl.EndUpdate(); } SelectProperty(nextEnum); } break; } case Keys.Left: // Closes the item if it is an expanded category case Keys.Subtract: { if ((e.KeyCode == Keys.Left) && (ModifierKeys == Keys.Control)) { SetLabelColumnWidthRatio((double)(LabelColumnWidth - 3) / (ClientRectangle.Width - LeftColumnWidth)); break; } if (SelectedPropertyEnumerator.Children.Count > 0) { Property property = SelectedPropertyEnumerator.Property; if (property.Expanded && HasOneVisibleChild(SelectedPropertyEnumerator)) { ExpandProperty(SelectedPropertyEnumerator, false); break; } } if (e.KeyCode == Keys.Subtract) break; goto case Keys.Up; } case Keys.Up: // Moves the selected item up { PropertyVisibleDeepEnumerator prevEnum = new PropertyVisibleDeepEnumerator(_selectedPropertyEnum.Node); prevEnum.MovePrev(); Property prevProperty = prevEnum.Property; if (prevProperty != null) { Rectangle clientRect = ClientRectangle; Rectangle itemRect = GetAbsoluteItemRect(prevEnum); if (itemRect.Top < clientRect.Top) { _firstDisplayedLine = 1; FirstDisplayedProperty = prevEnum; } else if (itemRect.Bottom > clientRect.Bottom) { while(true) { Win32Calls.SendMessage(Handle, Win32Calls.WM_VSCROLL, Win32Calls.MakeLong(Win32Calls.SB_LINEDOWN, 0), 0); itemRect = GetAbsoluteItemRect(prevEnum); if (itemRect.Bottom < clientRect.Bottom) break; } Win32Calls.SendMessage(Handle, Win32Calls.WM_VSCROLL, Win32Calls.MakeLong(Win32Calls.SB_ENDSCROLL, 0), 0); } SelectProperty(prevEnum); } break; } case Keys.Next: { Rectangle clientRect = ClientRectangle; int itemsPerPage = clientRect.Height / _basicPropertyHeight - 1; Rectangle itemRect = GetAbsoluteItemRect(SelectedPropertyEnumerator); // Find the item one page down int count = 0; PropertyEnumerator propEnum = SelectedPropertyEnumerator.GetVisibleDeepEnumerator(); int lineCount = (propEnum == FirstDisplayedProperty ? _firstDisplayedLine : 1); while ((count < itemsPerPage) && (propEnum != propEnum.RightBound)) { if (lineCount == propEnum.Property.HeightMultiplier) { propEnum.MoveNext(); lineCount = 1; } else lineCount++; count++; } // Selected item is above the grid ? if (itemRect.Bottom < clientRect.Top) { if (propEnum == propEnum.RightBound) { propEnum.MovePrev(); _firstDisplayedLine = propEnum.Property.HeightMultiplier; } else _firstDisplayedLine = lineCount; SelectProperty(propEnum); EnsureVisible(propEnum); } // Selected item is in or below the grid ? else { if (propEnum == propEnum.RightBound) // Not a complete page down because at the far bottom ? { propEnum.MovePrev(); lineCount = propEnum.Property.HeightMultiplier; PropertyVisibleDeepEnumerator propertyToSelectEnum = propEnum.GetVisibleDeepEnumerator(); count = itemsPerPage; while ((propEnum != _activePropertyCollection.GetVisibleDeepEnumerator().MoveFirst()) && (count > 0)) { if (lineCount > 1) lineCount--; else { propEnum.MovePrev(); lineCount = propEnum.Property.HeightMultiplier; } count--; } _firstDisplayedLine = lineCount; FirstDisplayedProperty = propEnum as PropertyVisibleDeepEnumerator; SelectProperty(propertyToSelectEnum); } else { // The grid height is to small to properly page down ? if (itemsPerPage < propEnum.Property.HeightMultiplier) { // Advance one item if (propEnum != _activePropertyCollection.GetVisibleDeepEnumerator().MoveLast()) { if (propEnum == SelectedPropertyEnumerator) propEnum.MoveNext(); _firstDisplayedLine = 1; FirstDisplayedProperty = propEnum as PropertyVisibleDeepEnumerator; SelectProperty(propEnum); } } else { FirstDisplayedProperty = SelectedPropertyEnumerator; SelectProperty(propEnum); } } } Invalidate(); break; } case Keys.Prior: { Rectangle clientRect = ClientRectangle; int itemsPerPage = clientRect.Height / _basicPropertyHeight - 1; // Find the item one page up int count = 0; PropertyVisibleDeepEnumerator enumerator = SelectedPropertyEnumerator.GetVisibleDeepEnumerator(); int lineCount = enumerator.Property.HeightMultiplier; while ((count < itemsPerPage) && (enumerator != _activePropertyCollection.GetVisibleDeepEnumerator().MoveFirst())) { if (lineCount == 1) { enumerator.MovePrev(); lineCount = enumerator.Property.HeightMultiplier; } else lineCount--; count++; } if ((count == 0) && (enumerator != _activePropertyCollection.GetVisibleDeepEnumerator().MoveFirst())) { enumerator.MovePrev(); count = 1; } Rectangle itemRect = GetAbsoluteItemRect(enumerator); // Is the new selected item in the grid ? if ((itemRect.Top >= clientRect.Top) && (itemRect.Bottom < clientRect.Bottom)) SelectProperty(enumerator); // Is the new selected item below the grid ? else if (itemRect.Bottom > clientRect.Bottom) { PropertyEnumerator currentEnum = enumerator.GetVisibleDeepEnumerator(); lineCount = 1; while((count > 0) && (currentEnum != _activePropertyCollection.GetVisibleDeepEnumerator().MoveFirst())) { if (lineCount == 1) { currentEnum.MovePrev(); lineCount = currentEnum.Property.HeightMultiplier; } else lineCount--; count--; } FirstDisplayedProperty = currentEnum as PropertyVisibleDeepEnumerator; SelectProperty(enumerator); } // Is the new selected item above the grid ? else if (itemRect.Top < clientRect.Top) { _firstDisplayedLine = 1; FirstDisplayedProperty = enumerator; SelectProperty(FirstDisplayedProperty); } Refresh(); break; } case Keys.Home: { Win32Calls.SetScrollPos(Handle, Win32Calls.SB_VERT, 0, true); _firstDisplayedPropertyEnumerator = _activePropertyCollection.GetVisibleDeepEnumerator(); _firstDisplayedPropertyEnumerator.MoveFirst(); _firstDisplayedLine = 1; SelectProperty(FirstDisplayedProperty); break; } case Keys.End: { Rectangle clientRect = ClientRectangle; _firstDisplayedPropertyEnumerator = _activePropertyCollection.GetVisibleDeepEnumerator(); _firstDisplayedPropertyEnumerator.MoveLast(); _firstDisplayedLine = 1; PropertyVisibleDeepEnumerator currentEnum = _firstDisplayedPropertyEnumerator.GetVisibleDeepEnumerator(); Rectangle itemRect = GetItemRect(currentEnum); while ((_firstDisplayedPropertyEnumerator != _activePropertyCollection.GetVisibleDeepEnumerator().MoveFirst()) && (clientRect.Bottom - itemRect.Bottom > _basicPropertyHeight)) { if (_firstDisplayedLine > 1) _firstDisplayedLine--; else { _firstDisplayedPropertyEnumerator.MovePrev(); _firstDisplayedLine = _firstDisplayedPropertyEnumerator.Property.HeightMultiplier; } itemRect = GetItemRect(currentEnum); } Win32Calls.SCROLLINFO si = new Win32Calls.SCROLLINFO(); si.cbSize = Marshal.SizeOf(si); si.fMask = Win32Calls.SIF_PAGE | Win32Calls.SIF_RANGE; Win32Calls.GetScrollInfo(Handle, Win32Calls.SB_VERT, ref si); Win32Calls.SetScrollPos(Handle, Win32Calls.SB_VERT, si.nMax - (si.nPage - 1), true); PropertyVisibleDeepEnumerator lastEnum = _activePropertyCollection.GetVisibleDeepEnumerator(); lastEnum.MoveLast(); SelectProperty(lastEnum); break; } case Keys.A: { if (e.Control) { if (_multiSelectMode == PropertyGrid.MultiSelectModes.SameLevel) { // Find the parent of the concerned level PropertyEnumerator ancestorEnum; if (SelectedPropertyEnumerator.Children.Count == 0) ancestorEnum = SelectedPropertyEnumerator.Parent; else ancestorEnum = SelectedPropertyEnumerator; // And browse all properties at this level and under PropertyEnumerator propEnum = ancestorEnum.Children.GetVisibleDeepEnumerator(); propEnum.RestrictedToThisLevelAndUnder = true; while(propEnum != ancestorEnum.Children.RightBound) { if (_parentCtrl.OnValidateMultiSelection(SelectedPropertyEnumerator, propEnum as PropertyVisibleDeepEnumerator)) { propEnum.Property.SelectedInternal = true; _multiSelectedItems.Add(propEnum); } propEnum.MoveNext(); } Invalidate(); } else if (_multiSelectMode == PropertyGrid.MultiSelectModes.Global) { PropertyEnumerator propEnum = _activePropertyCollection.GetVisibleDeepEnumerator(); propEnum.MoveFirst(); while (propEnum != propEnum.RightBound) { if ((propEnum != SelectedPropertyEnumerator) && (propEnum.Children.Count == 0)) { if (_parentCtrl.OnValidateMultiSelection(SelectedPropertyEnumerator, propEnum as PropertyVisibleDeepEnumerator)) { propEnum.Property.SelectedInternal = true; _multiSelectedItems.Add(propEnum); } } propEnum.MoveNext(); } Invalidate(); } } break; } } } base.OnKeyDown(e); }