2 // Permission is hereby granted, free of charge, to any person obtaining
3 // a copy of this software and associated documentation files (the
4 // "Software"), to deal in the Software without restriction, including
5 // without limitation the rights to use, copy, modify, merge, publish,
6 // distribute, sublicense, and/or sell copies of the Software, and to
7 // permit persons to whom the Software is furnished to do so, subject to
8 // the following conditions:
10 // The above copyright notice and this permission notice shall be
11 // included in all copies or substantial portions of the Software.
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
17 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
19 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 // Copyright (c) 2005-2008 Novell, Inc. (http://www.novell.com)
24 // Jonathan Chambers (jonathan.chambers@ansys.com)
25 // Ivan N. Zlatev (contact@i-nz.net)
32 using System.Collections;
33 using System.ComponentModel.Design;
35 using System.Drawing.Design;
36 using System.ComponentModel;
37 using System.Threading;
38 using System.Windows.Forms.Design;
40 namespace System.Windows.Forms.PropertyGridInternal {
41 internal class PropertyGridView : ScrollableControl, IWindowsFormsEditorService {
43 #region Private Members
44 private const char PASSWORD_PAINT_CHAR = '\u25cf'; // the dot char
45 private const char PASSWORD_TEXT_CHAR = '*';
46 private const int V_INDENT = 16;
47 private const int ENTRY_SPACING = 2;
48 private const int RESIZE_WIDTH = 3;
49 private const int BUTTON_WIDTH = 25;
50 private const int VALUE_PAINT_WIDTH = 19;
51 private const int VALUE_PAINT_INDENT = 27;
52 private double splitter_percent = .5;
53 private int row_height;
54 private int font_height_padding = 3;
55 private PropertyGridTextBox grid_textbox;
56 private PropertyGrid property_grid;
57 private bool resizing_grid;
58 private PropertyGridDropDown dropdown_form;
59 private Form dialog_form;
60 private ImplicitVScrollBar vbar;
61 private StringFormat string_format;
62 private Font bold_font;
63 private Brush inactive_text_brush;
64 private ListBox dropdown_list;
65 private Point last_click;
66 private Padding dropdown_form_padding;
70 public PropertyGridView (PropertyGrid propertyGrid) {
71 property_grid = propertyGrid;
73 string_format = new StringFormat ();
74 string_format.FormatFlags = StringFormatFlags.NoWrap;
75 string_format.Trimming = StringTrimming.None;
77 grid_textbox = new PropertyGridTextBox ();
78 grid_textbox.DropDownButtonClicked +=new EventHandler (DropDownButtonClicked);
79 grid_textbox.DialogButtonClicked +=new EventHandler (DialogButtonClicked);
81 dropdown_form = new PropertyGridDropDown ();
82 dropdown_form.FormBorderStyle = FormBorderStyle.None;
83 dropdown_form.StartPosition = FormStartPosition.Manual;
84 dropdown_form.ShowInTaskbar = false;
86 dialog_form = new Form ();
87 dialog_form.StartPosition = FormStartPosition.Manual;
88 dialog_form.FormBorderStyle = FormBorderStyle.None;
89 dialog_form.ShowInTaskbar = false;
91 dropdown_form_padding = new Padding (0, 0, 2, 2);
93 row_height = Font.Height + font_height_padding;
95 grid_textbox.Visible = false;
96 grid_textbox.Font = this.Font;
97 grid_textbox.BackColor = SystemColors.Window;
98 grid_textbox.Validate += new CancelEventHandler (grid_textbox_Validate);
99 grid_textbox.ToggleValue+=new EventHandler (grid_textbox_ToggleValue);
100 grid_textbox.KeyDown+=new KeyEventHandler (grid_textbox_KeyDown);
101 this.Controls.Add (grid_textbox);
103 vbar = new ImplicitVScrollBar ();
104 vbar.Visible = false;
106 vbar.ValueChanged+=new EventHandler (VScrollBar_HandleValueChanged);
107 vbar.Dock = DockStyle.Right;
108 this.Controls.AddImplicit (vbar);
110 resizing_grid = false;
112 bold_font = new Font (this.Font, FontStyle.Bold);
113 inactive_text_brush = new SolidBrush (ThemeEngine.Current.ColorGrayText);
115 ForeColorChanged+=new EventHandler (RedrawEvent);
116 BackColorChanged+=new System.EventHandler (RedrawEvent);
117 FontChanged+=new EventHandler (RedrawEvent);
119 SetStyle (ControlStyles.Selectable, true);
120 SetStyle (ControlStyles.DoubleBuffer, true);
121 SetStyle (ControlStyles.UserPaint, true);
122 SetStyle (ControlStyles.AllPaintingInWmPaint, true);
123 SetStyle (ControlStyles.ResizeRedraw, true);
128 private GridEntry RootGridItem {
129 get { return (GridEntry)property_grid.RootGridItem; }
132 private GridEntry SelectedGridItem {
133 get { return (GridEntry)property_grid.SelectedGridItem; }
134 set { property_grid.SelectedGridItem = value; }
137 #region Protected Instance Methods
139 protected override void OnFontChanged (EventArgs e) {
140 base.OnFontChanged (e);
142 bold_font = new Font (this.Font, FontStyle.Bold);
143 row_height = Font.Height + font_height_padding;
146 private void InvalidateItemLabel (GridEntry item)
148 Invalidate (new Rectangle (0, ((GridEntry)item).Top, SplitterLocation, row_height));
151 private void InvalidateItem (GridEntry item)
156 Rectangle rect = new Rectangle (0, item.Top, Width, row_height);
160 rect = new Rectangle (0, item.Top + row_height, Width,
161 Height - (item.Top + row_height));
166 // [+] expanding is handled in OnMouseDown, so in order to prevent
167 // duplicate expanding ignore it here.
169 protected override void OnDoubleClick (EventArgs e)
171 if (this.SelectedGridItem != null && this.SelectedGridItem.Expandable &&
172 !this.SelectedGridItem.PlusMinusBounds.Contains (last_click))
173 this.SelectedGridItem.Expanded = !this.SelectedGridItem.Expanded;
175 ToggleValue (this.SelectedGridItem);
178 protected override void OnPaint (PaintEventArgs e)
181 e.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), ClientRectangle);
183 int yLoc = -vbar.Value*row_height;
184 if (this.RootGridItem != null)
185 DrawGridItems (this.RootGridItem.GridItems, e, 1, ref yLoc);
190 protected override void OnMouseWheel (MouseEventArgs e)
192 if (vbar == null || !vbar.Visible)
195 vbar.Value = Math.Min (vbar.Maximum - GetVisibleRowsCount () + 1, vbar.Value + SystemInformation.MouseWheelScrollLines);
197 vbar.Value = Math.Max (0, vbar.Value - SystemInformation.MouseWheelScrollLines);
198 base.OnMouseWheel (e);
202 protected override void OnMouseMove (MouseEventArgs e) {
203 if (this.RootGridItem == null)
207 int loc = Math.Max (e.X,2*V_INDENT);
208 SplitterPercent = 1.0*loc/Width;
210 if (e.X > SplitterLocation - RESIZE_WIDTH && e.X < SplitterLocation + RESIZE_WIDTH)
211 this.Cursor = Cursors.SizeWE;
213 this.Cursor = Cursors.Default;
214 base.OnMouseMove (e);
217 protected override void OnMouseDown (MouseEventArgs e)
219 base.OnMouseDown (e);
220 last_click = e.Location;
221 if (this.RootGridItem == null)
224 if (e.X > SplitterLocation - RESIZE_WIDTH && e.X < SplitterLocation + RESIZE_WIDTH) {
225 resizing_grid = true;
228 int offset = -vbar.Value*row_height;
229 GridItem foundItem = GetSelectedGridItem (this.RootGridItem.GridItems, e.Y, ref offset);
231 if (foundItem != null) {
232 if (foundItem.Expandable && ((GridEntry)foundItem).PlusMinusBounds.Contains (e.X, e.Y))
233 foundItem.Expanded = !foundItem.Expanded;
235 this.SelectedGridItem = (GridEntry)foundItem;
236 if (!GridLabelHitTest (e.X)) {
237 // send mouse down so we get the carret under cursor
238 grid_textbox.SendMouseDown (PointToScreen (e.Location));
244 protected override void OnMouseUp (MouseEventArgs e) {
245 resizing_grid = false;
249 protected override void OnResize (EventArgs e) {
251 if (this.SelectedGridItem != null) // initialized already
255 private void UnfocusSelection ()
260 private void FocusSelection ()
262 Select (grid_textbox);
265 protected override bool ProcessDialogKey (Keys keyData) {
266 GridEntry selectedItem = this.SelectedGridItem;
267 if (selectedItem != null
268 && grid_textbox.Visible) {
271 if (TrySetEntry (selectedItem, grid_textbox.Text))
275 if (selectedItem.IsEditable)
276 UpdateItem (selectedItem); // reset value
286 return base.ProcessDialogKey (keyData);
289 private bool TrySetEntry (GridEntry entry, object value)
291 if (entry == null || grid_textbox.Text.Equals (entry.ValueText))
294 if (entry.IsEditable || !entry.IsEditable && (entry.HasCustomEditor || entry.AcceptedValues != null) ||
295 !entry.IsMerged || entry.HasMergedValue ||
296 (!entry.HasMergedValue && grid_textbox.Text != String.Empty)) {
298 bool changed = entry.SetValue (value, out error);
299 if (!changed && error != null) {
300 if (property_grid.ShowError (error, MessageBoxButtons.OKCancel) == DialogResult.Cancel) {
301 UpdateItem (entry); // restore value, repaint, etc
307 UpdateItem (entry); // restore value, repaint, etc
311 protected override bool IsInputKey (Keys keyData) {
329 private GridEntry MoveUpFromItem (GridEntry item, int up_count)
331 GridItemCollection items;
334 /* move back up the visible rows (and up the hierarchy as necessary) until
335 up_count == 0, or we reach the top of the display */
336 while (up_count > 0) {
337 items = item.Parent != null ? item.Parent.GridItems : this.RootGridItem.GridItems;
338 index = items.IndexOf (item);
341 if (item.Parent.GridItemType == GridItemType.Root) // we're at the top row
343 item = (GridEntry)item.Parent;
347 GridEntry prev_item = (GridEntry)items[index-1];
348 if (prev_item.Expandable && prev_item.Expanded) {
349 item = (GridEntry)prev_item.GridItems[prev_item.GridItems.Count - 1];
360 private GridEntry MoveDownFromItem (GridEntry item, int down_count)
362 while (down_count > 0) {
363 /* if we're a parent node and we're expanded, move to our first child */
364 if (item.Expandable && item.Expanded) {
365 item = (GridEntry)item.GridItems[0];
369 GridItem searchItem = item;
370 GridItemCollection searchItems = searchItem.Parent.GridItems;
371 int searchIndex = searchItems.IndexOf (searchItem);
373 while (searchIndex == searchItems.Count - 1) {
374 searchItem = searchItem.Parent;
376 if (searchItem == null || searchItem.Parent == null)
379 searchItems = searchItem.Parent.GridItems;
380 searchIndex = searchItems.IndexOf (searchItem);
383 if (searchIndex == searchItems.Count - 1) {
384 /* if we got all the way back to the root with no nodes after
385 us, the original item was the last one */
389 item = (GridEntry)searchItems[searchIndex+1];
398 protected override void OnKeyDown (KeyEventArgs e)
400 GridEntry selectedItem = this.SelectedGridItem;
402 if (selectedItem == null) {
403 /* XXX not sure what MS does, but at least we shouldn't crash */
408 switch (e.KeyData & Keys.KeyCode) {
411 if (SplitterLocation > 2 * V_INDENT)
412 SplitterPercent -= 0.01;
418 /* if the node is expandable and is expanded, collapse it.
419 otherwise, act just like the user pressed up */
420 if (selectedItem.Expandable && selectedItem.Expanded) {
421 selectedItem.Expanded = false;
430 if (SplitterLocation < Width)
431 SplitterPercent += 0.01;
437 /* if the node is expandable and not expanded, expand it.
438 otherwise, act just like the user pressed down */
439 if (selectedItem.Expandable && !selectedItem.Expanded) {
440 selectedItem.Expanded = true;
448 /* toggle the expanded state of the selected item */
449 if (selectedItem.Expandable) {
450 selectedItem.Expanded = !selectedItem.Expanded;
455 this.SelectedGridItem = MoveUpFromItem (selectedItem, 1);
459 this.SelectedGridItem = MoveDownFromItem (selectedItem, 1);
463 this.SelectedGridItem = MoveUpFromItem (selectedItem, vbar.LargeChange);
467 this.SelectedGridItem = MoveDownFromItem (selectedItem, vbar.LargeChange);
471 /* find the last, most deeply nested visible item */
472 GridEntry item = (GridEntry)this.RootGridItem.GridItems[this.RootGridItem.GridItems.Count - 1];
473 while (item.Expandable && item.Expanded)
474 item = (GridEntry)item.GridItems[item.GridItems.Count - 1];
475 this.SelectedGridItem = item;
479 this.SelectedGridItem = (GridEntry)this.RootGridItem.GridItems[0];
489 #region Private Helper Methods
491 private int SplitterLocation {
493 return (int)(splitter_percent*Width);
497 private double SplitterPercent {
499 int old_splitter_location = SplitterLocation;
501 splitter_percent = Math.Max (Math.Min (value, .9),.1);
503 if (old_splitter_location != SplitterLocation) {
504 int x = old_splitter_location > SplitterLocation ? SplitterLocation : old_splitter_location;
505 Invalidate (new Rectangle (x, 0, Width - x - (vbar.Visible ? vbar.Width : 0), Height));
506 UpdateItem (this.SelectedGridItem);
510 return splitter_percent;
514 private bool GridLabelHitTest (int x)
516 if (0 <= x && x <= splitter_percent * this.Width)
521 private GridItem GetSelectedGridItem (GridItemCollection grid_items, int y, ref int current) {
522 foreach (GridItem child_grid_item in grid_items) {
523 if (y > current && y < current + row_height) {
524 return child_grid_item;
526 current += row_height;
527 if (child_grid_item.Expanded) {
528 GridItem foundItem = GetSelectedGridItem (child_grid_item.GridItems, y, ref current);
529 if (foundItem != null)
536 private int GetVisibleItemsCount (GridEntry entry)
542 foreach (GridEntry e in entry.GridItems) {
544 if (e.Expandable && e.Expanded)
545 count += GetVisibleItemsCount (e);
550 private int GetVisibleRowsCount ()
552 return this.Height / row_height;
555 private void UpdateScrollBar ()
557 if (this.RootGridItem == null)
560 int visibleRows = GetVisibleRowsCount ();
561 int openedItems = GetVisibleItemsCount (this.RootGridItem);
562 if (openedItems > visibleRows) {
564 vbar.SmallChange = 1;
565 vbar.LargeChange = visibleRows;
566 vbar.Maximum = Math.Max (0, openedItems - 1);
569 vbar.Visible = false;
571 UpdateGridTextBoxBounds (this.SelectedGridItem);
574 // private bool GetScrollBarVisible ()
576 // if (this.RootGridItem == null)
579 // int visibleRows = GetVisibleRowsCount ();
580 // int openedItems = GetVisibleItemsCount (this.RootGridItem);
581 // if (openedItems > visibleRows)
587 private void DrawGridItems (GridItemCollection grid_items, PaintEventArgs pevent, int depth, ref int yLoc) {
588 foreach (GridItem grid_item in grid_items) {
589 DrawGridItem ((GridEntry)grid_item, pevent, depth, ref yLoc);
590 if (grid_item.Expanded)
591 DrawGridItems (grid_item.GridItems, pevent, (grid_item.GridItemType == GridItemType.Category) ? depth : depth+1, ref yLoc);
595 private void DrawGridItemLabel (GridEntry grid_item, PaintEventArgs pevent, int depth, Rectangle rect) {
596 Font font = this.Font;
599 if (grid_item.GridItemType == GridItemType.Category) {
601 brush = SystemBrushes.ControlText;
603 pevent.Graphics.DrawString (grid_item.Label, font, brush, rect.X + 1, rect.Y + ENTRY_SPACING);
604 if (grid_item == this.SelectedGridItem) {
605 SizeF size = pevent.Graphics.MeasureString (grid_item.Label, font);
606 ControlPaint.DrawFocusRectangle (pevent.Graphics, new Rectangle (rect.X + 1, rect.Y+ENTRY_SPACING, (int)size.Width, (int)size.Height));
610 if (grid_item == this.SelectedGridItem) {
611 Rectangle highlight = rect;
613 highlight.X -= V_INDENT;
614 highlight.Width += V_INDENT;
616 pevent.Graphics.FillRectangle (SystemBrushes.Highlight, highlight);
618 brush = SystemBrushes.HighlightText;
621 brush = grid_item.IsReadOnly ? inactive_text_brush : SystemBrushes.ControlText;
624 pevent.Graphics.DrawString (grid_item.Label, font, brush,
625 new Rectangle (rect.X + 1, rect.Y + ENTRY_SPACING, rect.Width - ENTRY_SPACING, rect.Height - ENTRY_SPACING),
629 private void DrawGridItemValue (GridEntry grid_item, PaintEventArgs pevent, int depth, Rectangle rect)
631 if (grid_item.PropertyDescriptor == null)
634 int xLoc = SplitterLocation+ENTRY_SPACING;
635 if (grid_item.PaintValueSupported) {
636 pevent.Graphics.DrawRectangle (Pens.Black, SplitterLocation + ENTRY_SPACING,
637 rect.Y + 2, VALUE_PAINT_WIDTH + 1, row_height - ENTRY_SPACING*2);
638 grid_item.PaintValue (pevent.Graphics,
639 new Rectangle (SplitterLocation + ENTRY_SPACING + 1,
640 rect.Y + ENTRY_SPACING + 1,
641 VALUE_PAINT_WIDTH, row_height - (ENTRY_SPACING*2 +1)));
642 xLoc += VALUE_PAINT_INDENT;
645 Font font = this.Font;
646 if (grid_item.IsResetable || !grid_item.HasDefaultValue)
648 Brush brush = grid_item.IsReadOnly ? inactive_text_brush : SystemBrushes.ControlText;
649 string valueText = String.Empty;
650 if (!grid_item.IsMerged || grid_item.IsMerged && grid_item.HasMergedValue) {
651 if (grid_item.IsPassword)
652 valueText = new String (PASSWORD_PAINT_CHAR, grid_item.ValueText.Length);
654 valueText = grid_item.ValueText;
656 pevent.Graphics.DrawString (valueText, font,
658 new RectangleF (xLoc + ENTRY_SPACING, rect.Y + ENTRY_SPACING,
659 ClientRectangle.Width-(xLoc), row_height - ENTRY_SPACING*2),
663 private void DrawGridItem (GridEntry grid_item, PaintEventArgs pevent, int depth, ref int yLoc) {
664 if (yLoc > -row_height && yLoc < ClientRectangle.Height) {
666 pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (property_grid.LineColor),
667 0, yLoc, V_INDENT, row_height);
669 if (grid_item.GridItemType == GridItemType.Category) {
670 pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (property_grid.CategoryForeColor), depth*V_INDENT,yLoc,ClientRectangle.Width-(depth*V_INDENT), row_height);
673 DrawGridItemLabel (grid_item, pevent,
675 new Rectangle (depth * V_INDENT, yLoc, SplitterLocation - depth * V_INDENT, row_height));
676 DrawGridItemValue (grid_item, pevent,
678 new Rectangle (SplitterLocation + ENTRY_SPACING , yLoc,
679 ClientRectangle.Width - SplitterLocation - ENTRY_SPACING - (vbar.Visible ? vbar.Width : 0),
682 if (grid_item.GridItemType != GridItemType.Category) {
683 Pen pen = ThemeEngine.Current.ResPool.GetPen (property_grid.LineColor);
684 // vertical divider line
685 pevent.Graphics.DrawLine (pen, SplitterLocation, yLoc, SplitterLocation, yLoc + row_height);
687 // draw the horizontal line
688 pevent.Graphics.DrawLine (pen, 0, yLoc + row_height, ClientRectangle.Width, yLoc + row_height);
691 if (grid_item.Expandable) {
692 int y = yLoc + row_height / 2 - ENTRY_SPACING + 1;
693 grid_item.PlusMinusBounds = DrawPlusMinus (pevent.Graphics, (depth - 1) * V_INDENT + ENTRY_SPACING + 1,
694 y, grid_item.Expanded, grid_item.GridItemType == GridItemType.Category);
698 grid_item.Top = yLoc;
702 private Rectangle DrawPlusMinus (Graphics g, int x, int y, bool expanded, bool category) {
703 Rectangle bounds = new Rectangle (x, y, 8, 8);
704 if (!category) g.FillRectangle (Brushes.White, bounds);
705 Pen pen = ThemeEngine.Current.ResPool.GetPen (property_grid.ViewForeColor);
706 g.DrawRectangle (pen, bounds);
707 g.DrawLine (pen, x+2, y+4, x + 6, y+4);
709 g.DrawLine (pen, x+4, y+2, x+4, y+6);
716 #region Event Handling
717 private void RedrawEvent (object sender, System.EventArgs e)
724 private void listBox_MouseUp (object sender, MouseEventArgs e) {
725 AcceptListBoxSelection (sender);
728 private void listBox_KeyDown (object sender, KeyEventArgs e)
730 switch (e.KeyData & Keys.KeyCode) {
732 AcceptListBoxSelection (sender);
740 void AcceptListBoxSelection (object sender)
742 GridEntry entry = this.SelectedGridItem as GridEntry;
744 grid_textbox.Text = (string) ((ListBox) sender).SelectedItem;
746 if (TrySetEntry (entry, grid_textbox.Text))
751 private void DropDownButtonClicked (object sender, EventArgs e)
756 private void DropDownEdit ()
758 GridEntry entry = this.SelectedGridItem as GridEntry;
762 if (entry.HasCustomEditor) {
763 entry.EditValue ((IWindowsFormsEditorService) this);
765 if (dropdown_form.Visible) {
769 ICollection std_values = entry.AcceptedValues;
770 if (std_values != null) {
771 if (dropdown_list == null) {
772 dropdown_list = new ListBox ();
773 dropdown_list.KeyDown += new KeyEventHandler (listBox_KeyDown);
774 dropdown_list.MouseUp += new MouseEventHandler (listBox_MouseUp);
776 dropdown_list.Items.Clear ();
777 dropdown_list.BorderStyle = BorderStyle.FixedSingle;
778 int selected_index = 0;
780 string valueText = entry.ValueText;
781 foreach (object obj in std_values) {
782 dropdown_list.Items.Add (obj);
783 if (valueText != null && valueText.Equals (obj))
787 dropdown_list.Height = row_height * Math.Min (dropdown_list.Items.Count, 15);
788 dropdown_list.Width = ClientRectangle.Width - SplitterLocation - (vbar.Visible ? vbar.Width : 0);
789 if (std_values.Count > 0)
790 dropdown_list.SelectedIndex = selected_index;
791 DropDownControl (dropdown_list);
797 private void DialogButtonClicked (object sender, EventArgs e)
799 GridEntry entry = this.SelectedGridItem as GridEntry;
800 if (entry != null && entry.HasCustomEditor)
801 entry.EditValue ((IWindowsFormsEditorService) this);
804 private void VScrollBar_HandleValueChanged (object sender, EventArgs e)
809 private void grid_textbox_ToggleValue (object sender, EventArgs args)
811 ToggleValue (this.SelectedGridItem);
814 private void grid_textbox_KeyDown (object sender, KeyEventArgs e)
816 switch (e.KeyData & Keys.KeyCode) {
826 private void grid_textbox_Validate (object sender, CancelEventArgs args)
828 if (!TrySetEntry (this.SelectedGridItem, grid_textbox.Text))
832 private void ToggleValue (GridEntry entry)
834 if (entry != null && !entry.IsReadOnly && entry.GridItemType == GridItemType.Property)
835 entry.ToggleValue ();
838 internal void UpdateItem (GridEntry entry)
840 if (entry == null || entry.GridItemType == GridItemType.Category ||
841 entry.GridItemType == GridItemType.Root) {
842 grid_textbox.Visible = false;
843 InvalidateItem (entry);
847 if (this.SelectedGridItem == entry) {
849 grid_textbox.Visible = false;
850 if (entry.IsResetable || !entry.HasDefaultValue)
851 grid_textbox.Font = bold_font;
853 grid_textbox.Font = this.Font;
855 if (entry.IsReadOnly) {
856 grid_textbox.DropDownButtonVisible = false;
857 grid_textbox.DialogButtonVisible = false;
858 grid_textbox.ReadOnly = true;
859 grid_textbox.ForeColor = SystemColors.GrayText;
861 grid_textbox.DropDownButtonVisible = entry.AcceptedValues != null ||
862 entry.EditorStyle == UITypeEditorEditStyle.DropDown;
863 grid_textbox.DialogButtonVisible = entry.EditorStyle == UITypeEditorEditStyle.Modal;
864 grid_textbox.ForeColor = SystemColors.ControlText;
865 grid_textbox.ReadOnly = !entry.IsEditable;
867 UpdateGridTextBoxBounds (entry);
868 grid_textbox.PasswordChar = entry.IsPassword ? PASSWORD_TEXT_CHAR : '\0';
869 grid_textbox.Text = entry.IsMerged && !entry.HasMergedValue ? String.Empty : entry.ValueText;
870 grid_textbox.Visible = true;
871 InvalidateItem (entry);
872 ResumeLayout (false);
874 grid_textbox.Visible = false;
878 private void UpdateGridTextBoxBounds (GridEntry entry)
880 if (entry == null || this.RootGridItem == null)
883 int y = -vbar.Value*row_height;
884 CalculateItemY (entry, this.RootGridItem.GridItems, ref y);
885 int x = SplitterLocation + ENTRY_SPACING + (entry.PaintValueSupported ? VALUE_PAINT_INDENT : 0);
886 grid_textbox.SetBounds (x + ENTRY_SPACING, y + ENTRY_SPACING,
887 ClientRectangle.Width - ENTRY_SPACING - x - (vbar.Visible ? vbar.Width : 0),
888 row_height - ENTRY_SPACING);
891 // Calculates the sum of the heights of all items before the one
893 private bool CalculateItemY (GridEntry entry, GridItemCollection items, ref int y)
895 foreach (GridItem item in items) {
899 if (item.Expandable && item.Expanded)
900 if (CalculateItemY (entry, item.GridItems, ref y))
906 private void ScrollToItem (GridEntry item)
908 if (item == null || this.RootGridItem == null)
911 int itemY = -vbar.Value*row_height;
912 int value = vbar.Value;;
913 CalculateItemY (item, this.RootGridItem.GridItems, ref itemY);
914 if (itemY < 0) // the new item is above the viewable area
915 value += itemY / row_height;
916 else if (itemY + row_height > Height) // the new item is below the viewable area
917 value += ((itemY + row_height) - Height) / row_height + 1;
918 if (value >= vbar.Minimum && value <= vbar.Maximum)
922 internal void SelectItem (GridEntry oldItem, GridEntry newItem)
925 InvalidateItemLabel (oldItem);
926 if (newItem != null) {
927 UpdateItem (newItem);
928 ScrollToItem (newItem);
930 grid_textbox.Visible = false;
931 vbar.Visible = false;
935 internal void UpdateView ()
940 UpdateItem (this.SelectedGridItem);
943 internal void ExpandItem (GridEntry item)
945 UpdateItem (this.SelectedGridItem);
946 Invalidate (new Rectangle (0, item.Top, Width, Height - item.Top));
949 internal void CollapseItem (GridEntry item)
951 UpdateItem (this.SelectedGridItem);
952 Invalidate (new Rectangle (0, item.Top, Width, Height - item.Top));
955 private void ShowDropDownControl (Control control, bool resizeable)
957 dropdown_form.Size = control.Size;
958 control.Dock = DockStyle.Fill;
961 dropdown_form.Padding = dropdown_form_padding;
962 dropdown_form.Width += dropdown_form_padding.Right;
963 dropdown_form.Height += dropdown_form_padding.Bottom;
964 dropdown_form.FormBorderStyle = FormBorderStyle.Sizable;
965 dropdown_form.SizeGripStyle = SizeGripStyle.Show;
967 dropdown_form.FormBorderStyle = FormBorderStyle.None;
968 dropdown_form.SizeGripStyle = SizeGripStyle.Hide;
969 dropdown_form.Padding = Padding.Empty;
972 dropdown_form.Controls.Add (control);
973 dropdown_form.Width = Math.Max (ClientRectangle.Width - SplitterLocation - (vbar.Visible ? vbar.Width : 0),
975 dropdown_form.Location = PointToScreen (new Point (grid_textbox.Right - dropdown_form.Width, grid_textbox.Location.Y + row_height));
976 RepositionInScreenWorkingArea (dropdown_form);
977 Point location = dropdown_form.Location;
979 Form owner = FindForm ();
980 owner.AddOwnedForm (dropdown_form);
981 dropdown_form.Show ();
982 if (dropdown_form.Location != location)
983 dropdown_form.Location = location;
985 System.Windows.Forms.MSG msg = new MSG ();
986 object queue_id = XplatUI.StartLoop (Thread.CurrentThread);
988 while (dropdown_form.Visible && XplatUI.GetMessage (queue_id, ref msg, IntPtr.Zero, 0, 0)) {
989 switch (msg.message) {
990 case Msg.WM_NCLBUTTONDOWN:
991 case Msg.WM_NCMBUTTONDOWN:
992 case Msg.WM_NCRBUTTONDOWN:
993 case Msg.WM_LBUTTONDOWN:
994 case Msg.WM_MBUTTONDOWN:
995 case Msg.WM_RBUTTONDOWN:
996 if (!HwndInControl (dropdown_form, msg.hwnd))
999 case Msg.WM_ACTIVATE:
1000 case Msg.WM_NCPAINT:
1001 if (owner.window.Handle == msg.hwnd)
1005 XplatUI.TranslateMessage (ref msg);
1006 XplatUI.DispatchMessage (ref msg);
1008 XplatUI.EndLoop (Thread.CurrentThread);
1011 private void RepositionInScreenWorkingArea (Form form)
1013 Rectangle workingArea = Screen.FromControl (form).WorkingArea;
1014 if (!workingArea.Contains (form.Bounds)) {
1016 x = form.Location.X;
1017 y = form.Location.Y;
1019 if (form.Location.X < workingArea.X)
1022 if (form.Location.Y + form.Size.Height > workingArea.Height) {
1023 Point aboveTextBox = PointToScreen (new Point (grid_textbox.Right - form.Width, grid_textbox.Location.Y));
1024 y = aboveTextBox.Y - form.Size.Height;
1027 form.Location = new Point (x, y);
1031 private bool HwndInControl (Control c, IntPtr hwnd)
1033 if (hwnd == c.window.Handle)
1035 foreach (Control cc in c.Controls.GetAllControls ()) {
1036 if (HwndInControl (cc, hwnd))
1043 #region IWindowsFormsEditorService Members
1045 public void CloseDropDown ()
1047 dropdown_form.Hide ();
1048 dropdown_form.Controls.Clear ();
1051 public void DropDownControl (Control control)
1053 bool resizeable = this.SelectedGridItem != null ? SelectedGridItem.EditorResizeable : false;
1054 ShowDropDownControl (control, resizeable);
1057 public System.Windows.Forms.DialogResult ShowDialog (Form dialog) {
1058 return dialog.ShowDialog (this);
1063 internal class PropertyGridDropDown : Form
1065 protected override CreateParams CreateParams {
1067 CreateParams cp = base.CreateParams;
1068 cp.Style = unchecked ((int)(WindowStyles.WS_POPUP | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN));
1069 cp.ExStyle |= (int)(WindowExStyles.WS_EX_TOPMOST);