* TextBoxBase.cs: Use the new SuspendRecalc/ResumeRecalc methods
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / PropertyGridView.cs
blob05394680c809e07b9895af9ed0c60e8f0035496b
1 #define DOUBLEBUFFER
3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
24 // Authors:
25 // Jonathan Chambers (jonathan.chambers@ansys.com)
29 // NOT COMPLETE
31 using System;
32 using System.Collections;
33 using System.ComponentModel.Design;
34 using System.Drawing;
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 double splitter_percent = .5;
45 private const int V_INDENT = 16;
46 private int row_height;
47 private int font_height_padding = 3;
48 private const int RESIZE_WIDTH = 3;
49 private const int BUTTON_WIDTH = 25;
50 private PropertyGridTextBox grid_textbox;
51 internal PropertyGrid property_grid;
52 private bool resizing_grid;
53 private int open_grid_item_count = -1;
54 private int skipped_grid_items;
55 private PropertyGridDropDown dropdown_form;
56 private Form dialog_form;
57 private ImplicitVScrollBar vbar;
58 private StringFormat string_format;
59 private Font bold_font;
60 #if !DOUBLEBUFFER
61 private int cached_splitter_location;
62 #endif
63 #endregion
65 #region Contructors
66 public PropertyGridView (PropertyGrid propertyGrid) {
67 property_grid = propertyGrid;
69 property_grid.SelectedGridItemChanged += new SelectedGridItemChangedEventHandler (SelectedGridItemChanged);
70 property_grid.PropertyValueChanged += new PropertyValueChangedEventHandler (PropertyValueChanged);
71 property_grid.SelectedObjectsChanged += new EventHandler (SelectedObjectsChanged);
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 skipped_grid_items = 0;
92 row_height = Font.Height + font_height_padding;
94 grid_textbox.Visible = false;
95 grid_textbox.Font = this.Font;
96 grid_textbox.BackColor = this.BackColor;
97 // Not working at all, used to??
98 grid_textbox.Validating += new CancelEventHandler(TextBoxValidating);
99 grid_textbox.ToggleValue+=new EventHandler(grid_textbox_ToggleValue);
100 this.Controls.Add(grid_textbox);
102 vbar = new ImplicitVScrollBar();
103 vbar.Visible = false;
104 vbar.ValueChanged+=new EventHandler(HandleValueChanged);
105 vbar.Dock = DockStyle.Right;
106 this.Controls.AddImplicit(vbar);
108 resizing_grid = false;
110 bold_font = new Font(this.Font, FontStyle.Bold);
112 ForeColorChanged+=new EventHandler(RedrawEvent);
113 BackColorChanged+=new System.EventHandler(RedrawEvent);
114 FontChanged+=new EventHandler(RedrawEvent);
116 SetStyle(ControlStyles.Selectable, true);
117 #if DOUBLEBUFFER
118 SetStyle(ControlStyles.DoubleBuffer, true);
119 #endif
120 SetStyle(ControlStyles.UserPaint, true);
121 SetStyle(ControlStyles.AllPaintingInWmPaint, true);
122 #if DOUBLEBUFFER
123 SetStyle(ControlStyles.ResizeRedraw, true);
124 #endif
127 #endregion
129 #region Protected Instance Methods
131 protected override void OnFontChanged(EventArgs e) {
132 base.OnFontChanged (e);
134 bold_font = new Font(this.Font, FontStyle.Bold);
135 row_height = Font.Height + font_height_padding;
138 void InvalidateGridItemLabel (GridItem item)
140 Invalidate (new Rectangle (0, item.Top, SplitterLocation, row_height));
143 #if !DOUBLEBUFFER
144 void InvalidateGridItem (GridItem item)
146 Invalidate (new Rectangle (0, item.Top, Width, row_height));
148 #endif
150 void CalcHeightOfGridItems (GridItemCollection col, ref int amount)
152 foreach (GridItem i in col) {
153 amount += row_height;
154 if (i.Expanded)
155 CalcHeightOfGridItems (i.GridItems, ref amount);
159 public void RedrawBelowItemOnExpansion (GridItem item)
161 grid_textbox_Hide ();
162 #if DOUBLEBUFFER
163 Invalidate(new Rectangle (0, item.Top, Width, Height - item.Top));
164 #else
165 // ugh, we need to recurse down into all our
166 // group items to figure out the space to
167 // scroll..
168 int amount = 0;
170 CalcHeightOfGridItems (item.GridItems, ref amount);
172 if (item.Expanded) {
173 XplatUI.ScrollWindow (Handle,
174 new Rectangle (0, item.Top + row_height,
175 Width, Height - item.Top - row_height),
176 0, amount, false);
178 else {
179 XplatUI.ScrollWindow (Handle,
180 new Rectangle (0, item.Top + row_height,
181 Width, Height - item.Top - row_height),
182 0, -amount, false);
184 InvalidateGridItem (item);
185 Update();
186 #endif
187 grid_textbox_Show (property_grid.SelectedGridItem);
190 protected override void OnDoubleClick(EventArgs e) {
191 if (property_grid.SelectedGridItem.Expandable) {
192 property_grid.SelectedGridItem.Expanded = !property_grid.SelectedGridItem.Expanded;
194 else {
195 GridItem item = property_grid.SelectedGridItem;
196 if (item.GridItemType == GridItemType.Property
197 && !item.PropertyDescriptor.IsReadOnly) {
198 ToggleValue();
199 Invalidate();
204 protected override void OnPaint(PaintEventArgs e) {
205 // Decide if we need a scrollbar
206 open_grid_item_count = 0;
208 // Background
209 e.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), ClientRectangle);
211 int yLoc = -vbar.Value*row_height;
213 if (property_grid.root_grid_item != null)
214 DrawGridItems(property_grid.root_grid_item.GridItems, e, 1, ref yLoc);
216 UpdateScrollBar();
219 protected override void OnMouseWheel(MouseEventArgs e) {
220 if (vbar == null || !vbar.Visible) {
221 return;
224 if (e.Delta < 0) {
225 vbar.Value = Math.Min(vbar.Value + SystemInformation.MouseWheelScrollLines, vbar.Maximum);
226 } else {
227 vbar.Value = Math.Max(0, vbar.Value - SystemInformation.MouseWheelScrollLines);
229 base.OnMouseWheel (e);
233 protected override void OnMouseMove (MouseEventArgs e) {
234 if (property_grid.root_grid_item == null)
235 return;
237 if (resizing_grid) {
238 int loc = Math.Max(e.X,2*V_INDENT);
239 SplitterPercent = 1.0*loc/Width;
241 if (e.X > SplitterLocation - RESIZE_WIDTH && e.X < SplitterLocation + RESIZE_WIDTH)
242 this.Cursor = Cursors.SizeWE;
243 else
244 this.Cursor = Cursors.Default;
245 base.OnMouseMove (e);
248 protected override void OnMouseDown (MouseEventArgs e) {
249 if (property_grid.root_grid_item == null)
250 return;
252 if (e.X > SplitterLocation - RESIZE_WIDTH && e.X < SplitterLocation + RESIZE_WIDTH) {
253 resizing_grid = true;
255 else {
256 int offset = -vbar.Value*row_height;
257 GridItem foundItem = GetSelectedGridItem(property_grid.root_grid_item.GridItems, e.Y, ref offset);
259 if (foundItem != null) {
260 if (foundItem.Expandable) {
261 if (e.X >=3 && e.X <= 11 && (e.Y % row_height >= row_height/2-2 && e.Y % row_height <= row_height/2+4)) {
262 foundItem.Expanded = !foundItem.Expanded;
265 this.property_grid.SelectedGridItem = foundItem;
268 base.OnMouseDown (e);
272 protected override void OnMouseUp (MouseEventArgs e) {
273 resizing_grid = false;
274 base.OnMouseUp (e);
277 protected override void OnResize(EventArgs e) {
278 SuspendLayout ();
279 grid_textbox.Hide ();
280 ResumeLayout (false);
282 #if !DOUBLEBUFFER
283 // we need to explicitly handle this redraw
284 // before getting to the scrollwindow, or we
285 // end up having to redraw the entire item
286 // value area because the exposed areas are
287 // unioned.
288 Update ();
289 #endif
291 base.OnResize (e);
292 #if !DOUBLEBUFFER
293 if (cached_splitter_location != SplitterLocation) {
294 int x = cached_splitter_location > SplitterLocation ? SplitterLocation : cached_splitter_location;
295 XplatUI.ScrollWindow (Handle,
296 new Rectangle (x, 0, Width - x - (vbar.Visible ? vbar.Width : 0), Height),
297 SplitterLocation - cached_splitter_location, 0, false);
298 Update();
300 cached_splitter_location = SplitterLocation;
301 #endif
302 SuspendLayout ();
303 grid_textbox_Show (property_grid.SelectedGridItem);
304 ResumeLayout (false);
307 private void UnfocusSelection ()
309 Select (this);
312 protected override bool ProcessDialogKey (Keys keyData) {
313 GridItem selectedItem = property_grid.SelectedGridItem;
314 if (selectedItem != null
315 && grid_textbox.Visible
316 /* if the textbox has focus? */) {
317 switch (keyData) {
318 case Keys.Enter:
319 SetPropertyValue(selectedItem.PropertyDescriptor.Converter.ConvertFromString(grid_textbox.Text));
320 UnfocusSelection ();
321 return true;
322 case Keys.Escape:
323 grid_textbox.Text = selectedItem.PropertyDescriptor.Converter.ConvertToString(selectedItem.Value);
324 UnfocusSelection ();
325 return true;
326 default:
327 return false;
330 return base.ProcessDialogKey (keyData);
334 protected override bool IsInputKey (Keys keyData) {
335 switch (keyData) {
336 case Keys.Left:
337 case Keys.Right:
338 case Keys.Enter:
339 case Keys.Escape:
340 case Keys.Up:
341 case Keys.Down:
342 case Keys.PageDown:
343 case Keys.PageUp:
344 case Keys.Home:
345 case Keys.End:
346 return true;
347 default:
348 return false;
352 private GridEntry MoveUpFromItem (GridEntry item, int up_count)
354 GridItemCollection items;
355 int index;
357 /* move back up the visible rows (and up the hierarchy as necessary) until
358 up_count == 0, or we reach the top of the display */
359 while (up_count > 0) {
360 items = item.Parent != null ? item.Parent.GridItems : property_grid.root_grid_item.GridItems;
361 index = items.IndexOf (item);
363 if (index == 0) {
364 if (item.Parent.GridItemType == GridItemType.Root) // we're at the top row
365 return item;
366 item = (GridEntry)item.Parent;
367 up_count --;
369 else {
370 GridEntry prev_item = (GridEntry)items[index-1];
371 if (prev_item.Expandable && prev_item.Expanded) {
372 item = (GridEntry)prev_item.GridItems[prev_item.GridItems.Count - 1];
374 else {
375 item = prev_item;
377 up_count --;
380 return item;
383 private GridEntry MoveDownFromItem (GridEntry item, int down_count)
385 while (down_count > 0) {
386 /* if we're a parent node and we're expanded, move to our first child */
387 if (item.Expandable && item.Expanded) {
388 item = (GridEntry)item.GridItems[0];
389 down_count--;
391 else {
392 GridItem searchItem = item;
393 GridItemCollection searchItems = searchItem.Parent.GridItems;
394 int searchIndex = searchItems.IndexOf (searchItem);
396 while (searchIndex == searchItems.Count - 1) {
397 searchItem = searchItem.Parent;
399 if (searchItem == null || searchItem.Parent == null)
400 break;
402 searchItems = searchItem.Parent.GridItems;
403 searchIndex = searchItems.IndexOf (searchItem);
406 if (searchIndex == searchItems.Count - 1) {
407 /* if we got all the way back to the root with no nodes after
408 us, the original item was the last one */
409 return item;
411 else {
412 item = (GridEntry)searchItems[searchIndex+1];
413 down_count--;
418 return item;
421 protected override void OnKeyDown(KeyEventArgs e) {
422 GridEntry selectedItem = (GridEntry)property_grid.SelectedGridItem;
424 if (selectedItem == null) {
425 /* XXX not sure what MS does, but at least we shouldn't crash */
426 base.OnKeyDown (e);
427 return;
430 switch (e.KeyData & Keys.KeyCode) {
431 case Keys.Left:
432 if (e.Control) {
433 if (SplitterLocation > 2 * V_INDENT)
434 SplitterPercent -= 0.01;
436 e.Handled = true;
437 break;
439 else {
440 /* if the node is expandable and is expanded, collapse it.
441 otherwise, act just like the user pressed up */
442 if (selectedItem.Expandable && selectedItem.Expanded) {
443 selectedItem.Expanded = false;
444 e.Handled = true;
445 break;
447 else
448 goto case Keys.Up;
450 case Keys.Right:
451 if (e.Control) {
452 if (SplitterLocation < Width)
453 SplitterPercent += 0.01;
455 e.Handled = true;
456 break;
458 else {
459 /* if the node is expandable and not expanded, expand it.
460 otherwise, act just like the user pressed down */
461 if (selectedItem.Expandable && !selectedItem.Expanded) {
462 selectedItem.Expanded = true;
463 e.Handled = true;
464 break;
466 else
467 goto case Keys.Down;
469 case Keys.Enter:
470 /* toggle the expanded state of the selected item */
471 if (selectedItem.Expandable) {
472 selectedItem.Expanded = !selectedItem.Expanded;
474 e.Handled = true;
475 break;
476 case Keys.Up:
477 property_grid.SelectedGridItem = MoveUpFromItem (selectedItem, 1);
478 e.Handled = true;
479 break;
480 case Keys.Down:
481 property_grid.SelectedGridItem = MoveDownFromItem (selectedItem, 1);
482 e.Handled = true;
483 break;
484 case Keys.PageUp:
485 property_grid.SelectedGridItem = MoveUpFromItem (selectedItem, vbar.LargeChange);
486 e.Handled = true;
487 break;
488 case Keys.PageDown:
489 property_grid.SelectedGridItem = MoveDownFromItem (selectedItem, vbar.LargeChange);
490 e.Handled = true;
491 break;
492 case Keys.End:
493 /* find the last, most deeply nested visible item */
494 GridEntry item = (GridEntry)property_grid.root_grid_item.GridItems[property_grid.root_grid_item.GridItems.Count - 1];
495 while (item.Expandable && item.Expanded)
496 item = (GridEntry)item.GridItems[item.GridItems.Count - 1];
497 property_grid.SelectedGridItem = item;
498 e.Handled = true;
499 break;
500 case Keys.Home:
501 property_grid.SelectedGridItem = property_grid.root_grid_item.GridItems[0];
502 e.Handled = true;
503 break;
506 base.OnKeyDown (e);
509 #endregion
511 #region Private Helper Methods
513 private int SplitterLocation{
514 get {
515 return (int)(splitter_percent*Width);
519 private double SplitterPercent{
520 set {
521 int old_splitter_location = SplitterLocation;
523 splitter_percent = Math.Max(Math.Min(value, .9),.1);
525 if (old_splitter_location != SplitterLocation) {
526 grid_textbox_Hide ();
527 int x = old_splitter_location > SplitterLocation ? SplitterLocation : old_splitter_location;
528 #if DOUBLEBUFFER
529 Invalidate(new Rectangle (x, 0, Width - x - (vbar.Visible ? vbar.Width : 0), Height));
530 #else
531 XplatUI.ScrollWindow (Handle,
532 new Rectangle (x, 0, Width - x - (vbar.Visible ? vbar.Width : 0), Height),
533 SplitterLocation - old_splitter_location, 0, false);
534 Update ();
535 #endif
536 grid_textbox_Show (property_grid.SelectedGridItem);
539 #if !DOUBLEBUFFER
540 cached_splitter_location = SplitterLocation;
541 #endif
543 get {
544 return splitter_percent;
548 private GridItem GetSelectedGridItem (GridItemCollection grid_items, int y, ref int current) {
549 foreach (GridItem child_grid_item in grid_items) {
550 if (y > current && y < current + row_height) {
551 return child_grid_item;
553 current += row_height;
554 if (child_grid_item.Expanded) {
555 GridItem foundItem = GetSelectedGridItem(child_grid_item.GridItems, y, ref current);
556 if (foundItem != null)
557 return foundItem;
560 return null;
563 private void UpdateScrollBar() {
564 int visible_rows = this.ClientRectangle.Height/row_height;
565 if (open_grid_item_count > visible_rows) {
566 vbar.Visible = true;
567 vbar.SmallChange = 1;
568 vbar.Minimum = 0;
569 vbar.Maximum = open_grid_item_count-1;
570 vbar.LargeChange = visible_rows;
572 else {
573 vbar.Visible = false;
578 #region Drawing Code
580 private void DrawGridItems(GridItemCollection grid_items, PaintEventArgs pevent, int depth, ref int yLoc) {
581 foreach (GridItem grid_item in grid_items) {
582 DrawGridItem (grid_item, pevent, depth, ref yLoc);
583 if (grid_item.Expanded)
584 DrawGridItems(grid_item.GridItems, pevent, (grid_item.GridItemType == GridItemType.Category) ? depth : depth+1, ref yLoc);
588 private void DrawGridItemLabel(GridItem grid_item, PaintEventArgs pevent, int depth, Rectangle rect) {
589 Font font = this.Font;
590 Brush brush;
592 if (grid_item.GridItemType == GridItemType.Category) {
593 font = bold_font;
594 brush = SystemBrushes.ControlDark;
596 pevent.Graphics.DrawString (grid_item.Label, font, brush, rect.X + 1, rect.Y + 2);
597 if (grid_item == property_grid.SelectedGridItem) {
598 SizeF size = pevent.Graphics.MeasureString (grid_item.Label, font);
599 ControlPaint.DrawFocusRectangle (pevent.Graphics, new Rectangle(rect.X + 1, rect.Y+2, (int)size.Width, (int)size.Height));
602 else {
603 if (grid_item == property_grid.SelectedGridItem) {
604 Rectangle highlight = rect;
605 if (depth > 1) {
606 highlight.X -= V_INDENT;
607 highlight.Width += V_INDENT;
609 pevent.Graphics.FillRectangle (SystemBrushes.Highlight, highlight);
610 // Label
611 brush = SystemBrushes.HighlightText;
613 else {
614 brush = SystemBrushes.WindowText;
615 if (grid_item.PropertyDescriptor.IsReadOnly
616 && !grid_item.Expandable)
617 brush = SystemBrushes.InactiveCaption;
621 pevent.Graphics.DrawString (grid_item.Label, font, brush,
622 new Rectangle (rect.X + 1, rect.Y + 2, rect.Width - 2, rect.Height - 2),
623 string_format);
626 private void DrawGridItemValue(GridItem grid_item, PaintEventArgs pevent, int depth, Rectangle rect) {
627 // Value
628 if (grid_item.PropertyDescriptor != null) {
630 bool paintsValue = false;
631 UITypeEditor editor = (UITypeEditor)grid_item.PropertyDescriptor.GetEditor(typeof(UITypeEditor));
632 if (editor != null) {
633 paintsValue = editor.GetPaintValueSupported();
636 int xLoc = SplitterLocation+1;
637 if (paintsValue) {
638 pevent.Graphics.DrawRectangle(Pens.Black, SplitterLocation+2,rect.Y+2, 20, row_height-4);
639 try {
640 editor.PaintValue(grid_item.Value, pevent.Graphics, new Rectangle(SplitterLocation+3,rect.Y+3, 19, row_height-5));
642 catch (Exception) {
644 xLoc += 27;
647 try {
648 if (grid_item.PropertyDescriptor.Converter != null) {
649 Font font = this.Font;
650 Brush brush;
652 string value = grid_item.PropertyDescriptor.Converter.ConvertToString(grid_item.Value);
653 if (((GridEntry)grid_item).CanResetValue ())
654 font = bold_font;
656 brush = SystemBrushes.WindowText;
657 if (grid_item.PropertyDescriptor.IsReadOnly
658 && !grid_item.Expandable)
659 brush = SystemBrushes.InactiveCaption;
661 pevent.Graphics.DrawString(value, font,
662 brush,
663 new RectangleF(xLoc, rect.Y+2,
664 ClientRectangle.Width-(xLoc), row_height),string_format);
666 else {
667 Console.WriteLine("No converter for type {0}",grid_item.PropertyDescriptor.PropertyType);
671 catch (Exception) {
676 private void DrawGridItem (GridItem grid_item, PaintEventArgs pevent, int depth, ref int yLoc) {
677 if (yLoc > -row_height && yLoc < ClientRectangle.Height) {
679 // Left column
680 pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (property_grid.LineColor),
681 0, yLoc, V_INDENT, row_height);
683 if (grid_item.Expandable) {
684 grid_item.PlusMinusBounds = DrawPlusMinus(pevent.Graphics, 3, yLoc+row_height/2-3, grid_item.Expanded, grid_item.GridItemType == GridItemType.Category);
687 if (grid_item.GridItemType == GridItemType.Category) {
688 pevent.Graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (property_grid.LineColor), depth*V_INDENT,yLoc,ClientRectangle.Width-(depth*V_INDENT), row_height);
691 DrawGridItemLabel(grid_item, pevent,
692 depth,
693 new Rectangle(depth * V_INDENT, yLoc, SplitterLocation - depth * V_INDENT, row_height));
694 DrawGridItemValue(grid_item, pevent,
695 depth,
696 new Rectangle(SplitterLocation + 2 , yLoc, ClientRectangle.Width - SplitterLocation - 2 - (vbar.Visible ? vbar.Width : 0), row_height));
698 if (grid_item.GridItemType != GridItemType.Category) {
699 Pen pen = ThemeEngine.Current.ResPool.GetPen(property_grid.LineColor);
700 // vertical divider line
701 pevent.Graphics.DrawLine(pen, SplitterLocation, yLoc, SplitterLocation, yLoc + row_height);
703 // draw the horizontal line
704 pevent.Graphics.DrawLine(pen, 0, yLoc + row_height, ClientRectangle.Width, yLoc + row_height);
707 grid_item.Top = yLoc;
708 yLoc += row_height;
709 open_grid_item_count++;
712 private Rectangle DrawPlusMinus (Graphics g, int x, int y, bool expanded, bool category) {
713 Rectangle bounds = new Rectangle(x, y, 8, 8);
714 if (!category) g.FillRectangle (Brushes.White, bounds);
715 Pen pen = ThemeEngine.Current.ResPool.GetPen (property_grid.ViewForeColor);
716 g.DrawRectangle (pen, bounds);
717 g.DrawLine (pen, x+2, y+4, x + 6, y+4);
718 if (!expanded)
719 g.DrawLine (pen, x+4, y+2, x+4, y+6);
721 return bounds;
724 #endregion
726 #region Event Handling
727 private void RedrawEvent (object sender, System.EventArgs e) {
728 Refresh();
731 private void TextBoxValidating (object sender, CancelEventArgs e) {
732 if (this.property_grid.SelectedGridItem != null) {
733 PropertyDescriptor desc = property_grid.SelectedGridItem.PropertyDescriptor;
734 if (desc != null) {
735 try {
736 if (desc.Converter != null) {
737 SetPropertyValue(desc.Converter.ConvertFromString(grid_textbox.Text));
739 else {
740 Console.WriteLine("No converter for type {0}",desc.PropertyType);
743 catch (Exception) {
744 Console.WriteLine("Error converting string");
750 #endregion
752 private void ToggleValue() {
753 if (property_grid.SelectedGridItem.GridItemType == GridItemType.Property) {
754 if (property_grid.SelectedGridItem.PropertyDescriptor != null) {
755 if (property_grid.SelectedGridItem.PropertyDescriptor.PropertyType == typeof(bool))
756 SetPropertyValue(!(bool)property_grid.SelectedGridItem.Value);
757 else if (property_grid.SelectedGridItem.PropertyDescriptor.Converter.GetStandardValuesSupported()){
758 System.ComponentModel.TypeConverter.StandardValuesCollection coll =
759 (System.ComponentModel.TypeConverter.StandardValuesCollection)property_grid.SelectedGridItem.PropertyDescriptor.Converter.GetStandardValues();
760 for (int i = 0; i < coll.Count; i++) {
761 if (property_grid.SelectedGridItem.Value.Equals(coll[i])){
762 if (i < coll.Count-1)
763 SetPropertyValue(coll[i+1]);
764 else
765 SetPropertyValue(coll[0]);
766 break;
775 private void listBox_MouseUp(object sender, MouseEventArgs e) {
776 AcceptListBoxSelection (sender);
779 private void listBox_KeyDown(object sender, KeyEventArgs e)
781 switch (e.KeyData & Keys.KeyCode) {
782 case Keys.Enter:
783 AcceptListBoxSelection (sender);
784 return;
785 case Keys.Escape:
786 CloseDropDown ();
787 return;
791 void AcceptListBoxSelection (object sender) {
792 if (this.property_grid.SelectedGridItem != null) {
793 PropertyDescriptor desc = property_grid.SelectedGridItem.PropertyDescriptor;
794 if (desc != null) {
795 SetPropertyValue(((ListBox)sender).SelectedItem);
798 CloseDropDown ();
801 private void SetPropertyValue(object newVal) {
802 if (property_grid.SelectedGridItem == null)
803 return;
805 PropertyDescriptor desc = property_grid.SelectedGridItem.PropertyDescriptor;
806 if (desc == null)
807 return;
809 for (int i = 0; i < ((GridEntry)property_grid.SelectedGridItem).SelectedObjects.Length; i ++) {
810 object target = property_grid.GetTarget (property_grid.SelectedGridItem, i);
811 desc.SetValue(target, newVal);
815 private void DropDownButtonClicked (object sender, EventArgs e) {
816 UITypeEditor editor = property_grid.SelectedGridItem.PropertyDescriptor.GetEditor (typeof (UITypeEditor)) as UITypeEditor;
817 if (editor == null) {
818 if (dropdown_form.Visible) {
819 CloseDropDown ();
821 else {
822 TypeConverter converter = property_grid.SelectedGridItem.PropertyDescriptor.Converter;
823 ICollection std_values;
825 if (!converter.GetStandardValuesSupported ())
826 return;
828 std_values = converter.GetStandardValues();
829 if (std_values == null)
830 return;
832 ListBox listBox = new ListBox();
833 listBox.BorderStyle = BorderStyle.FixedSingle;
834 int selected_index = 0;
835 int i = 0;
836 object selected_value = property_grid.SelectedGridItem.Value;
837 foreach (object obj in std_values) {
838 listBox.Items.Add(obj);
839 if (selected_value != null && selected_value.Equals(obj))
840 selected_index = i;
841 i++;
843 listBox.Height = row_height * Math.Min (listBox.Items.Count, 15);
844 listBox.SelectedIndex = selected_index;
845 listBox.KeyDown += new KeyEventHandler(listBox_KeyDown);
846 listBox.MouseUp+=new MouseEventHandler(listBox_MouseUp);
848 DropDownControl (listBox);
850 } else { // use editor
851 SetPropertyValueFromUITypeEditor (editor);
855 void SetPropertyValueFromUITypeEditor (UITypeEditor editor)
857 ServiceContainer service_container = new ServiceContainer ();
858 service_container.AddService (typeof (IWindowsFormsEditorService), this);
859 object initial_value = property_grid.SelectedGridItem.Value;
860 object value = editor.EditValue (
861 (ITypeDescriptorContext)property_grid.SelectedGridItem,
862 service_container,
863 initial_value);
864 if (!Object.Equals (value, initial_value)) {
865 SetPropertyValue (value);
869 private void DialogButtonClicked(object sender, EventArgs e) {
870 UITypeEditor editor = property_grid.SelectedGridItem.PropertyDescriptor.GetEditor (typeof (UITypeEditor)) as UITypeEditor;
871 if (editor != null)
872 SetPropertyValueFromUITypeEditor (editor);
875 private void HandleValueChanged(object sender, EventArgs e) {
876 if (vbar.Value <= 0) {
877 vbar.Value = 0;
879 if (vbar.Value > vbar.Maximum-ClientRectangle.Height/row_height) {
880 vbar.Value = vbar.Maximum-ClientRectangle.Height/row_height+1;
883 int scroll_amount = (skipped_grid_items-vbar.Value)*row_height;
885 if (scroll_amount == 0)
886 return;
888 grid_textbox_Hide ();
890 skipped_grid_items = vbar.Value;
891 XplatUI.ScrollWindow(Handle, 0, scroll_amount, false);
892 #if DOUBLEBUFFER
893 Invalidate ();
894 #endif
895 Update ();
897 if (property_grid.SelectedGridItem != null)
898 grid_textbox_Show (property_grid.SelectedGridItem);
901 private void grid_textbox_ToggleValue(object sender, EventArgs e) {
902 if (!property_grid.SelectedGridItem.PropertyDescriptor.IsReadOnly) {
903 ToggleValue();
904 Invalidate();
908 internal void grid_textbox_Show (GridItem forItem)
910 if (forItem == null || forItem.PropertyDescriptor == null)
911 return;
913 SuspendLayout ();
915 if (((GridEntry)forItem).CanResetValue ())
916 grid_textbox.Font = bold_font;
917 else
918 grid_textbox.Font = this.Font;
920 grid_textbox.ReadOnly = false;
921 grid_textbox.DropDownButtonVisible = false;
922 grid_textbox.DialogButtonVisible = false;
924 bool paintsValue = false;
925 UITypeEditor editor = (UITypeEditor)forItem.PropertyDescriptor.GetEditor(typeof(UITypeEditor));
926 if (editor != null) {
927 paintsValue = editor.GetPaintValueSupported();
930 if (editor != null) {
931 UITypeEditorEditStyle style = editor.GetEditStyle();
933 switch (style) {
934 case UITypeEditorEditStyle.DropDown:
935 grid_textbox.DropDownButtonVisible = true;
936 break;
937 case UITypeEditorEditStyle.Modal:
938 grid_textbox.DialogButtonVisible = true;
939 break;
942 else {
943 try {
944 if (forItem.PropertyDescriptor.Converter != null) {
945 if (forItem.PropertyDescriptor.Converter.GetStandardValuesSupported()) {
947 grid_textbox.DropDownButtonVisible = true;
948 grid_textbox.ReadOnly = true;
951 else {
952 Console.WriteLine("Converter not available for type {0}",forItem.PropertyDescriptor.PropertyType);
956 catch (Exception) {
960 grid_textbox.ReadOnly = grid_textbox.ReadOnly || forItem.PropertyDescriptor.IsReadOnly;
962 int xloc = SplitterLocation + 1 + (paintsValue ? 27 : 0);
963 grid_textbox.SetBounds (xloc,
964 forItem.Top + 2,
965 ClientRectangle.Width - xloc - (vbar.Visible ? vbar.Width : 0),
966 row_height - 2);
967 grid_textbox.Visible = true;
969 ResumeLayout (false);
972 private void grid_textbox_Hide ()
974 SuspendLayout ();
975 grid_textbox.Bounds = Rectangle.Empty;
976 grid_textbox.Visible = false;
977 ResumeLayout (false);
980 void EnsureItemIsVisible (GridItem item)
982 if (item.Top < 0) {
983 // the new item is above the viewable area
984 vbar.Value += item.Top / row_height;
986 else if (item.Top + row_height > Height) {
987 // the new item is below the viewable area
988 vbar.Value += ((item.Top + row_height) - Height) / row_height + 1;
990 else {
991 // do nothing
996 private void SelectedObjectsChanged (object sender, EventArgs args) {
997 grid_textbox_Hide ();
1000 private void SelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e) {
1001 if (e.OldSelection != null)
1002 InvalidateGridItemLabel (e.OldSelection);
1003 InvalidateGridItemLabel (e.NewSelection);
1005 EnsureItemIsVisible (e.NewSelection);
1007 if (e.NewSelection.GridItemType == GridItemType.Property) {
1008 if (e.NewSelection.PropertyDescriptor != null) {
1010 if (e.NewSelection.Value == null)
1011 grid_textbox.Text = "";
1012 else
1013 grid_textbox.Text = e.NewSelection.PropertyDescriptor.Converter.ConvertToString(e.NewSelection.Value);
1014 if (((GridEntry)e.NewSelection).CanResetValue ())
1015 grid_textbox.Font = bold_font;
1016 else
1017 grid_textbox.Font = this.Font;
1019 grid_textbox_Show (e.NewSelection);
1022 else {
1023 grid_textbox_Hide ();
1027 private void PropertyValueChanged(object s, PropertyValueChangedEventArgs e) {
1028 if (e.ChangedItem.PropertyDescriptor != null) {
1029 grid_textbox.Text = e.ChangedItem.PropertyDescriptor.Converter.ConvertToString(e.ChangedItem.Value);
1030 if (((GridEntry)e.ChangedItem).CanResetValue())
1031 grid_textbox.Font = bold_font;
1032 else
1033 grid_textbox.Font = this.Font;
1037 private void DropDownControl(Control control, bool block) {
1038 Object queue_id;
1040 dropdown_form.Size = control.Size;
1041 control.Dock = DockStyle.Fill;
1042 dropdown_form.Controls.Clear();
1043 dropdown_form.Controls.Add(control);
1044 dropdown_form.Location = PointToScreen(new Point(SplitterLocation,grid_textbox.Location.Y+row_height));
1045 dropdown_form.Width = ClientRectangle.Width - SplitterLocation - (vbar.Visible ? vbar.Width : 0);
1047 FindForm ().AddOwnedForm (dropdown_form);
1049 dropdown_form.Show();
1051 if (block) {
1052 System.Windows.Forms.MSG msg = new MSG();
1053 queue_id = XplatUI.StartLoop(Thread.CurrentThread);
1054 while (dropdown_form.Visible && XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
1056 if ((msg.message == Msg.WM_NCLBUTTONDOWN ||
1057 msg.message == Msg.WM_NCMBUTTONDOWN ||
1058 msg.message == Msg.WM_NCRBUTTONDOWN ||
1059 msg.message == Msg.WM_LBUTTONDOWN ||
1060 msg.message == Msg.WM_MBUTTONDOWN ||
1061 msg.message == Msg.WM_RBUTTONDOWN)
1062 && !HwndInControl (dropdown_form, msg.hwnd)) {
1063 CloseDropDown ();
1064 break;
1066 XplatUI.TranslateMessage(ref msg);
1067 XplatUI.DispatchMessage(ref msg);
1072 private bool HwndInControl (Control c, IntPtr hwnd)
1074 if (hwnd == c.window.Handle)
1075 return true;
1076 foreach (Control cc in c.Controls.GetAllControls ())
1077 if (HwndInControl (cc, hwnd))
1078 return true;
1079 return false;
1082 #endregion
1084 #region IWindowsFormsEditorService Members
1086 public void CloseDropDown() {
1087 Control c = dropdown_form.Controls[0];
1088 c.Capture = false;
1089 dropdown_form.Hide();
1092 public void DropDownControl(Control control) {
1093 DropDownControl (control, true);
1096 public System.Windows.Forms.DialogResult ShowDialog(Form dialog) {
1097 return dialog.ShowDialog(this);
1100 #endregion
1103 class ComboListBox
1105 internal class PropertyGridDropDown : Form {
1106 protected override CreateParams CreateParams {
1107 get {
1108 CreateParams cp = base.CreateParams;
1109 cp.Style = unchecked ((int)(WindowStyles.WS_POPUP | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN));
1110 cp.ExStyle |= (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);
1111 return cp;