|
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Presentation.View
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Globalization;
using System.Runtime;
using System.Diagnostics.CodeAnalysis;
//This class is responsible for providing functionality to display additional information in context of
//the designer view in a popup-like manner. It is basically the canvas control, which is placed on top of
//the other visual elements. It provides functionality to add and remove extension windows, as well as manipulating
//their position and size
sealed class ExtensionSurface : Panel
{
public static readonly DependencyProperty DesignerProperty = DependencyProperty.Register(
"Designer",
typeof(DesignerView),
typeof(ExtensionSurface),
new PropertyMetadata(OnDesignerChanged));
public static readonly DependencyProperty AutoExpandCanvasProperty = DependencyProperty.Register(
"AutoExpandCanvas",
typeof(bool),
typeof(ExtensionSurface),
new UIPropertyMetadata(false));
public static readonly DependencyProperty PlacementTargetProperty = DependencyProperty.RegisterAttached(
"PlacementTarget",
typeof(FrameworkElement),
typeof(ExtensionSurface),
new UIPropertyMetadata(null, OnPlacementTargetChanged));
public static readonly DependencyProperty AlignmentProperty = DependencyProperty.RegisterAttached(
"Alignment",
typeof(PositionAlignment),
typeof(ExtensionSurface),
new UIPropertyMetadata(PositionAlignment.LeftTop));
public static readonly DependencyProperty ModeProperty = DependencyProperty.RegisterAttached(
"Mode",
typeof(PlacementMode),
typeof(ExtensionSurface),
new UIPropertyMetadata(PlacementMode.Absolute, OnPlacementModeChanged));
public static readonly DependencyProperty PositionProperty = DependencyProperty.RegisterAttached(
"Position",
typeof(Point),
typeof(ExtensionSurface),
new UIPropertyMetadata(new Point()));
Func<double, double, double, bool> IsGreater;
KeyValuePair<FrameworkElement, Point> selectedChild;
Size rearangeStartSize = new Size();
Rect actualPanelRect = new Rect(0, 0, 0, 0);
Point canvasOffset = new Point();
int currentZIndex = 1000;
public ExtensionSurface()
{
//add global handled for ExtensionWindow's CloseEvent
this.AddHandler(ExtensionWindow.CloseEvent, new RoutedEventHandler(OnExtensionWindowClosed));
this.ClipToBounds = true;
this.IsGreater = (v1, v2, v3) => (v1 + v2 > v3);
}
[Fx.Tag.KnownXamlExternal]
public DesignerView Designer
{
get { return (DesignerView)GetValue(DesignerProperty); }
set { SetValue(DesignerProperty, value); }
}
public bool AutoExpandCanvas
{
get { return (bool)GetValue(AutoExpandCanvasProperty); }
set { SetValue(AutoExpandCanvasProperty, value); }
}
static void OnPlacementModeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
ExtensionWindow window = sender as ExtensionWindow;
if (null != window && null != window.Surface && window.Visibility == Visibility.Visible)
{
window.Surface.PlaceWindow(window);
}
}
static void OnPlacementTargetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
}
//hook for designer mouse events - they are required to handle positioning and resizing
static void OnDesignerChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
ExtensionSurface ctrl = (ExtensionSurface)sender;
DesignerView designer;
if (null != args.OldValue)
{
designer = (DesignerView)args.OldValue;
}
if (null != args.NewValue)
{
designer = (DesignerView)args.NewValue;
}
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
ExtensionWindow window = visualRemoved as ExtensionWindow;
if (null != window)
{
window.VisibilityChanged -= OnWindowVisibilityChanged;
// window.SizeChanged -= OnWindowSizeChanged;
this.rearangeStartSize.Width = 0;
this.rearangeStartSize.Height = 0;
}
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
window = visualAdded as ExtensionWindow;
if (null != window)
{
window.VisibilityChanged += OnWindowVisibilityChanged;
// window.SizeChanged += OnWindowSizeChanged;
if (!window.IsLoaded)
{
window.Loaded += OnChildWindowLoaded;
}
}
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
foreach (FrameworkElement child in this.Children)
{
ExtensionWindow window = child as ExtensionWindow;
if (null != window)
{
if (PlacementMode.Relative == GetMode(window) && null != GetPlacementTarget(window))
{
this.PlaceWindow(window);
continue;
}
if (!this.AutoExpandCanvas)
{
this.EnsureWindowIsVisible(window);
}
}
}
}
void OnChildWindowLoaded(object sender, EventArgs e)
{
ExtensionWindow window = (ExtensionWindow)sender;
this.OnWindowVisibilityChanged(window, null);
window.Loaded -= OnChildWindowLoaded;
}
//void OnWindowSizeChanged(object sender, SizeChangedEventArgs e)
//{
// ExtensionWindow window = (ExtensionWindow)sender;
// // EnsureWindowIsVisible(window);
//}
void OnWindowVisibilityChanged(object sender, RoutedEventArgs args)
{
ExtensionWindow window = (ExtensionWindow)sender;
if (window.IsVisible)
{
Func<double, bool> IsInvalid = x => (double.IsInfinity(x) || double.IsNaN(x) || double.Epsilon > x);
if (IsInvalid(window.ActualWidth) || IsInvalid(window.ActualWidth) || IsInvalid(window.DesiredSize.Width) || IsInvalid(window.DesiredSize.Height))
{
window.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
}
PlaceWindow(window);
}
}
void PlaceWindow(ExtensionWindow window)
{
if (null != window)
{
FrameworkElement target = ExtensionSurface.GetPlacementTarget(window);
PositionAlignment alignment = ExtensionSurface.GetAlignment(window);
PlacementMode mode = ExtensionSurface.GetMode(window);
Point position = ExtensionSurface.GetPosition(window);
Point calculatedPosition = new Point();
FrameworkElement commonRoot = null;
MatrixTransform transform = null;
switch (mode)
{
case PlacementMode.Relative:
if (null != target)
{
commonRoot = target.FindCommonVisualAncestor(this) as FrameworkElement;
if (null == commonRoot)
{
return;
}
transform = (MatrixTransform)target.TransformToAncestor(commonRoot);
}
else
{
if (!DesignerProperties.GetIsInDesignMode(this))
{
Fx.Assert(string.Format(CultureInfo.InvariantCulture, "PlacementTarget must be set in RelativeMode on ExtensionSurface '{0}'", this.Name));
}
}
break;
case PlacementMode.Absolute:
calculatedPosition = position;
break;
default:
Fx.Assert(string.Format(CultureInfo.CurrentCulture, "ExtensionWindowPlacement.Mode {0} specified in ExtensionWindow '{1}' is not supported for ExtensionSurface", mode, window.Name));
return;
}
if (PlacementMode.Relative == mode)
{
if (null != target)
{
double x;
double y;
switch (alignment)
{
case PositionAlignment.LeftTop:
calculatedPosition = transform.Transform(calculatedPosition);
break;
case PositionAlignment.LeftBottom:
calculatedPosition = transform.Transform(new Point(0.0, target.ActualHeight));
break;
case PositionAlignment.RightTop:
calculatedPosition = transform.Transform(new Point(target.ActualWidth, 0.0));
break;
case PositionAlignment.RightBottom:
calculatedPosition = transform.Transform(new Point(target.ActualWidth, target.ActualHeight));
break;
case PositionAlignment.Center:
calculatedPosition = transform.Transform(calculatedPosition);
x = ((target.ActualWidth * transform.Matrix.M11) - window.Width) / 2.0;
y = ((target.ActualHeight * transform.Matrix.M22) - window.Height) / 2.0;
calculatedPosition.Offset(x, y);
break;
case PositionAlignment.CenterHorizontal:
calculatedPosition = transform.Transform(calculatedPosition);
x = ((target.ActualWidth * transform.Matrix.M11) - window.Width) / 2.0;
calculatedPosition.Offset(x, 0.0);
break;
case PositionAlignment.CenterVertical:
calculatedPosition = transform.Transform(calculatedPosition);
y = ((target.ActualHeight * transform.Matrix.M22) - window.Height) / 2.0;
calculatedPosition.Offset(0.0, y);
break;
default:
Fx.Assert(string.Format(CultureInfo.CurrentCulture, "ExtensionWindowPlacement.Position = '{0}' is not supported", alignment));
return;
}
}
}
SetWindowPosition(window, calculatedPosition);
}
}
internal void SetWindowPosition(ExtensionWindow window, Point position)
{
Func<double, double, double, double, double> CalculateInBoundsValue =
(pos, size, limit, modifier) =>
{
if (this.AutoExpandCanvas)
{
return pos - modifier;
}
else
{
pos = Math.Max(0.0, pos);
return pos + size > limit ? limit - size : pos;
}
};
//in case of AutoExpandCanvas == false:
// - do not allow placing window outside surface bounds
//in case of AutoExpandCanvas == true:
// - include possible negative canvas offset
position.X = CalculateInBoundsValue(position.X, window.DesiredSize.Width, this.ActualWidth, this.selectedChild.Value.X);
position.Y = CalculateInBoundsValue(position.Y, window.DesiredSize.Height, this.ActualHeight, this.selectedChild.Value.Y);
//update its position on canvas
ExtensionSurface.SetPosition(window, position);
bool requiresMeasure = false;
if (this.AutoExpandCanvas)
{
requiresMeasure = true;
this.canvasOffset.X = 0;
this.canvasOffset.Y = 0;
foreach (UIElement item in this.Children)
{
FrameworkElement child = item as FrameworkElement;
if (null != child)
{
Point p = ExtensionSurface.GetPosition(child);
this.canvasOffset.X = Math.Min(this.canvasOffset.X, p.X);
this.canvasOffset.Y = Math.Min(this.canvasOffset.Y, p.Y);
}
}
this.canvasOffset.X = Math.Abs(this.canvasOffset.X);
this.canvasOffset.Y = Math.Abs(this.canvasOffset.Y);
}
if (requiresMeasure)
{
this.InvalidateMeasure();
}
else
{
this.InvalidateArrange();
}
}
void EnsureWindowIsVisible(ExtensionWindow window)
{
SetWindowPosition(window, ExtensionSurface.GetPosition(window));
}
internal void SetSize(ExtensionWindow window, Size size)
{
Point pos = ExtensionSurface.GetPosition(window);
if (!this.AutoExpandCanvas)
{
if (IsGreater(pos.X, size.Width, this.ActualWidth))
{
size.Width = this.ActualWidth - pos.X;
}
if (IsGreater(pos.Y, size.Height, this.ActualHeight))
{
size.Height = this.ActualHeight - pos.Y;
}
}
System.Diagnostics.Debug.WriteLine("SetSize oldSize (" + window.Width + "," + window.Height + ") newSize (" + size.Width + "," + size.Height + ")");
window.Width = size.Width;
window.Height = size.Height;
if (this.AutoExpandCanvas)
{
// this.InvalidateMeasure();
}
}
protected override Size ArrangeOverride(Size arrangeSize)
{
foreach (UIElement child in this.Children)
{
//get (left, top) coorinates
Point pos = ExtensionSurface.GetPosition(child);
//include eventual negative offset (panel wouldn't display elements with negative coorinates by default)
pos.Offset(this.canvasOffset.X, this.canvasOffset.Y);
//request child to rearange itself in given rectangle
child.Arrange(new Rect(pos, child.DesiredSize));
}
System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "ArrangeOverride Size({0},{1})", arrangeSize.Width, arrangeSize.Height));
return arrangeSize;
}
protected override Size MeasureOverride(Size constraint)
{
Size result;
if (this.AutoExpandCanvas)
{
double panelWidth = 0.0;
double panelHeight = 0.0;
//initially assume that whole content fits in rectangle with coordinates (0,0, ActualWidth, ActualHeight)
double offsetMinusX = 0.0;
double offsetMinusY = 0.0;
double offsetPlusX = this.rearangeStartSize.Width;
double offsetPlusY = this.rearangeStartSize.Height;
foreach (UIElement item in this.Children)
{
FrameworkElement child = item as FrameworkElement;
if (null != child)
{
child.Measure(constraint);
//get child's position
Point pos = ExtensionSurface.GetPosition(child);
//calculate the minimum value of panel's (left,top) corner
offsetMinusX = Math.Min(offsetMinusX, pos.X);
offsetMinusY = Math.Min(offsetMinusY, pos.Y);
//calculate the maximum value of panel's (right, bottom) corner
offsetPlusX = Math.Max(offsetPlusX, pos.X + child.DesiredSize.Width);
offsetPlusY = Math.Max(offsetPlusY, pos.Y + child.DesiredSize.Height);
}
}
//get required panel's width and height
panelWidth = Math.Abs(offsetPlusX - offsetMinusX);
panelHeight = Math.Abs(offsetPlusY - offsetMinusY);
this.actualPanelRect.Location = new Point(offsetMinusX, offsetMinusY);
this.actualPanelRect.Size = new Size(panelWidth, panelHeight);
//return it as result
result = new Size(panelWidth, panelHeight);
}
else
{
result = base.MeasureOverride(constraint);
}
System.Diagnostics.Debug.WriteLine("MO constraint:" + constraint.Width + "," + constraint.Height + " new: " + result.Width + "," + result.Height);
return result;
}
public void SelectWindow(ExtensionWindow window)
{
if (null != window && this.Children.Contains(window))
{
this.selectedChild = new KeyValuePair<FrameworkElement, Point>(window, this.canvasOffset);
this.rearangeStartSize.Width = this.ActualWidth;
this.rearangeStartSize.Height = this.ActualHeight;
Panel.SetZIndex(window, ++this.currentZIndex);
}
}
void OnExtensionWindowClosed(object sender, RoutedEventArgs args)
{
ExtensionWindow window = args.Source as ExtensionWindow;
if (null != window)
{
//remove window from children collection
this.Children.Remove(window);
}
}
public static void SetPlacementTarget(DependencyObject container, FrameworkElement value)
{
container.SetValue(PlacementTargetProperty, value);
}
public static FrameworkElement GetPlacementTarget(DependencyObject container)
{
return (FrameworkElement)container.GetValue(PlacementTargetProperty);
}
public static void SetAlignment(DependencyObject container, PositionAlignment value)
{
container.SetValue(AlignmentProperty, value);
}
public static PositionAlignment GetAlignment(DependencyObject container)
{
return (PositionAlignment)container.GetValue(AlignmentProperty);
}
public static void SetMode(DependencyObject container, PlacementMode value)
{
container.SetValue(ModeProperty, value);
}
public static PlacementMode GetMode(DependencyObject container)
{
return (PlacementMode)container.GetValue(ModeProperty);
}
public static void SetPosition(DependencyObject container, Point value)
{
container.SetValue(PositionProperty, value);
}
public static Point GetPosition(DependencyObject container)
{
return (Point)container.GetValue(PositionProperty);
}
[SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "Suppress to avoid unnecessary changes.")]
public enum PlacementMode
{
Relative, Absolute
}
[SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "Suppress to avoid unnecessary changes.")]
public enum PositionAlignment
{
LeftTop, LeftBottom, RightTop, RightBottom, Center, CenterHorizontal, CenterVertical
};
}
}
|