1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal.PropertyEditing
8 using System.Windows.Controls;
9 using System.Windows.Media;
12 // PropertyPanel is a simplified version of a horizontal StackPanel that we use for PropertyContainer
13 // visuals in lieu of Grid, which was too heavy-weight and bogging down perf. It exposes a property,
14 // LastChildWidth, that specifies the forced width of the last child in the panel. All other
15 // children are stacked on the left and eat up the remainder of the space left on the panel.
17 // The panel also deals with drawing compartments for itself and the last child and it deals with
18 // visually nesting sub-properties based on their depth (Level).
20 internal class PropertyPanel : Panel
25 public static readonly DependencyProperty OutlineBrushProperty =
26 DependencyProperty.Register("OutlineBrush",
28 typeof(PropertyPanel),
29 new FrameworkPropertyMetadata((Brush)null,
30 FrameworkPropertyMetadataOptions.AffectsRender |
31 FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
33 public static readonly DependencyProperty SelectionBrushProperty =
34 DependencyProperty.Register("SelectionBrush",
36 typeof(PropertyPanel),
37 new FrameworkPropertyMetadata((Brush)null,
38 FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
40 public static readonly DependencyProperty OutlineThicknessProperty =
41 DependencyProperty.Register("OutlineThickness",
43 typeof(PropertyPanel),
44 new FrameworkPropertyMetadata((double)1,
45 FrameworkPropertyMetadataOptions.AffectsRender));
47 public static readonly DependencyProperty IgnoreFirstChildBackgroundProperty =
48 DependencyProperty.Register("IgnoreFirstChildBackground",
50 typeof(PropertyPanel),
51 new FrameworkPropertyMetadata(
53 FrameworkPropertyMetadataOptions.AffectsRender));
55 public static DependencyProperty LastChildWidthProperty = DependencyProperty.Register(
58 typeof(PropertyPanel),
59 new FrameworkPropertyMetadata(
61 FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender));
63 public static DependencyProperty LevelProperty = DependencyProperty.Register(
66 typeof(PropertyPanel),
67 new FrameworkPropertyMetadata(
69 FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
71 public static DependencyProperty LevelIndentProperty = DependencyProperty.Register(
74 typeof(PropertyPanel),
75 new FrameworkPropertyMetadata(
77 FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
80 // Gets or sets the pixel width of the last child added into this panel.
82 public double LastChildWidth
84 get { return (double)this.GetValue(LastChildWidthProperty); }
85 set { this.SetValue(LastChildWidthProperty, value); }
93 // Gets or sets the indentation level for the first child in this panel. Levels are
94 // measured in ints, with 0 = no indentation, 1 = 1st sub-property, ...
95 // The actual amount of space taken up by each level is controled by LevelIndent property
99 get { return (int)this.GetValue(LevelProperty); }
100 set { this.SetValue(LevelProperty, value); }
108 // Gets or sets the pixel width that the first child is indented for each level that
111 public double LevelIndent
113 get { return (double)this.GetValue(LevelIndentProperty); }
114 set { this.SetValue(LevelIndentProperty, value); }
122 // Gets or sets the line brush to use for the panel compartments
124 public Brush OutlineBrush
126 get { return (Brush)GetValue(OutlineBrushProperty); }
127 set { SetValue(OutlineBrushProperty, value); }
135 // Gets or sets the brush to be used as the background for everything but the last
136 // element in the panel
138 public Brush SelectionBrush
140 get { return (Brush)GetValue(SelectionBrushProperty); }
141 set { SetValue(SelectionBrushProperty, value); }
146 // OutlineThickness DP
149 // Gets or sets the line thickness for the panel compartments (not as Thickness, but
150 // instead as a simple double)
152 public double OutlineThickness
154 get { return (double)GetValue(OutlineThicknessProperty); }
155 set { SetValue(OutlineThicknessProperty, value); }
160 // IgnoreFirstChildBackground DP
163 // Gets or sets a flag indicating whether the SelectionBrush background should
164 // or should not be applied to the first child of the panel
166 public bool IgnoreFirstChildBackground
168 get { return (bool)GetValue(IgnoreFirstChildBackgroundProperty); }
169 set { SetValue(IgnoreFirstChildBackgroundProperty, value); }
174 // Stacks the children to the left, leaving LastChildWidth amount of space for the last child
175 protected override Size MeasureOverride(Size availableSize)
178 double lastChildWidth = Math.Max(0, this.LastChildWidth);
179 double indent = this.LevelIndent * this.Level;
180 double availableWidth = Math.Max(0, availableSize.Width - lastChildWidth - indent);
181 int childrenCount = InternalChildren.Count;
182 int lastIndex = childrenCount - 1;
183 Size actualSize = new Size();
185 for (int i = 0; i < childrenCount; i++)
187 UIElement child = InternalChildren[i];
191 InternalChildren[i].Measure(new Size(lastChildWidth, availableSize.Height));
195 InternalChildren[i].Measure(new Size(availableWidth, availableSize.Height));
198 availableWidth -= child.DesiredSize.Width;
199 //Compute the actual size for the propertypanel
200 actualSize.Height = Math.Max(actualSize.Height, child.DesiredSize.Height);
201 actualSize.Width += child.DesiredSize.Width;
207 // Stacks the children to the left, leaving LastChildWidth amount of space for the last child
208 protected override Size ArrangeOverride(Size finalSize)
211 double lastChildWidth = Math.Max(0, this.LastChildWidth);
212 double indent = this.LevelIndent * this.Level;
213 double availableWidth = Math.Max(0, finalSize.Width - lastChildWidth - indent);
214 double left = indent;
215 int childrenCount = InternalChildren.Count;
216 int lastIndex = childrenCount - 1;
218 for (int i = 0; i < childrenCount; i++)
221 UIElement child = InternalChildren[i];
222 double desiredWidth = child.DesiredSize.Width;
225 child.Arrange(new Rect(Math.Max(0, finalSize.Width - lastChildWidth), 0, lastChildWidth, finalSize.Height));
229 child.Arrange(new Rect(left, 0, Math.Min(desiredWidth, availableWidth), finalSize.Height));
232 left += desiredWidth;
233 availableWidth -= desiredWidth;
234 availableWidth = Math.Max(0, availableWidth);
240 // Custom renders compartments and dividers
241 protected override void OnRender(DrawingContext dc)
244 Size renderSize = this.RenderSize;
245 Brush outlineBrush = this.OutlineBrush;
246 double outlineThickness = this.OutlineThickness;
247 double halfThickness = outlineThickness / 2.0;
248 double dividerRight = Math.Max(0, this.LastChildWidth);
249 double dividerLeft = renderSize.Width - dividerRight - outlineThickness;
251 Brush selectionBrush = this.SelectionBrush;
253 if (selectionBrush != null)
255 bool ignoreFirstChildBackground = this.IgnoreFirstChildBackground;
256 double firstChildWidth = 0;
258 if (ignoreFirstChildBackground && this.Children.Count > 0)
260 firstChildWidth = this.Children[0].RenderSize.Width;
263 dc.DrawRectangle(selectionBrush, null, new Rect(
266 Math.Max(dividerLeft - firstChildWidth, 0),
272 // Use Guidelines to avoid anti-aliasing (fuzzy border lines)
273 dc.PushGuidelineSet(new GuidelineSet(
274 // X coordinates for guidelines (vertical lines)
275 new double[] { 0, dividerLeft, dividerLeft + outlineThickness, renderSize.Width - outlineThickness, renderSize.Width },
276 // Y coordinates for guidelines (horizontal lines)
277 new double[] { 0, renderSize.Height - outlineThickness, renderSize.Height }));
279 Pen outlinePen = new Pen(outlineBrush, outlineThickness);
284 new Point(0, renderSize.Height - halfThickness),
285 new Point(renderSize.Width, renderSize.Height - halfThickness));
290 new Point(0, 0 - halfThickness),
291 new Point(renderSize.Width, 0 - halfThickness));
296 new Point(renderSize.Width - halfThickness, 0),
297 new Point(renderSize.Width - halfThickness, renderSize.Height));
302 new Point(dividerLeft + halfThickness, 0),
303 new Point(dividerLeft + halfThickness, renderSize.Height));