using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Controls; using System.Windows; using System.Windows.Media; using System.Diagnostics; using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary; namespace Microsoft.Research.DynamicDataDisplay.Charts { public class StackCanvas : Panel { public StackCanvas() { } #region EndCoordinate attached property [AttachedPropertyBrowsableForChildren] public static double GetEndCoordinate(DependencyObject obj) { return (double)obj.GetValue(EndCoordinateProperty); } public static void SetEndCoordinate(DependencyObject obj, double value) { obj.SetValue(EndCoordinateProperty, value); } public static readonly DependencyProperty EndCoordinateProperty = DependencyProperty.RegisterAttached( "EndCoordinate", typeof(double), typeof(StackCanvas), new PropertyMetadata(Double.NaN, OnCoordinateChanged)); #endregion #region Coordinate attached property [AttachedPropertyBrowsableForChildren] public static double GetCoordinate(DependencyObject obj) { return (double)obj.GetValue(CoordinateProperty); } public static void SetCoordinate(DependencyObject obj, double value) { obj.SetValue(CoordinateProperty, value); } public static readonly DependencyProperty CoordinateProperty = DependencyProperty.RegisterAttached( "Coordinate", typeof(double), typeof(StackCanvas), new PropertyMetadata(0.0, OnCoordinateChanged)); private static void OnCoordinateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { UIElement reference = d as UIElement; if (reference != null) { StackCanvas parent = VisualTreeHelper.GetParent(reference) as StackCanvas; if (parent != null) { parent.InvalidateArrange(); } } } #endregion #region AxisPlacement property public AxisPlacement Placement { get { return (AxisPlacement)GetValue(PlacementProperty); } set { SetValue(PlacementProperty, value); } } public static readonly DependencyProperty PlacementProperty = DependencyProperty.Register( "Placement", typeof(AxisPlacement), typeof(StackCanvas), new FrameworkPropertyMetadata( AxisPlacement.Bottom, FrameworkPropertyMetadataOptions.AffectsArrange)); #endregion private bool IsHorizontal { get { return Placement == AxisPlacement.Top || Placement == AxisPlacement.Bottom; } } protected override Size MeasureOverride(Size constraint) { Size availableSize = constraint; Size size = new Size(); bool isHorizontal = IsHorizontal; if (isHorizontal) { availableSize.Width = Double.PositiveInfinity; size.Width = constraint.Width; } else { availableSize.Height = Double.PositiveInfinity; size.Height = constraint.Height; } // measuring all children and determining self width and height foreach (UIElement element in base.Children) { if (element != null) { Size childSize = GetChildSize(element, availableSize); element.Measure(childSize); Size desiredSize = element.DesiredSize; if (isHorizontal) { size.Height = Math.Max(size.Height, desiredSize.Height); } else { size.Width = Math.Max(size.Width, desiredSize.Width); } } } if (Double.IsPositiveInfinity(size.Width)) size.Width = 0; if (Double.IsPositiveInfinity(size.Height)) size.Height = 0; return size; } private Size GetChildSize(UIElement element, Size availableSize) { var coordinate = GetCoordinate(element); var endCoordinate = GetEndCoordinate(element); if (coordinate.IsNotNaN() && endCoordinate.IsNotNaN()) { if (Placement.IsBottomOrTop()) { availableSize.Width = Math.Abs(endCoordinate - coordinate); } else { availableSize.Height = Math.Abs(endCoordinate - coordinate); } } return availableSize; } protected override Size ArrangeOverride(Size finalSize) { bool isHorizontal = IsHorizontal; foreach (FrameworkElement element in base.Children) { if (element == null) { continue; } Size elementSize = element.DesiredSize; double x = 0.0; double y = 0.0; switch (Placement) { case AxisPlacement.Left: x = finalSize.Width - elementSize.Width; break; case AxisPlacement.Right: x = 0; break; case AxisPlacement.Top: y = finalSize.Height - elementSize.Height; break; case AxisPlacement.Bottom: y = 0; break; default: break; } double coordinate = GetCoordinate(element); if (!Double.IsNaN(GetEndCoordinate(element))) { double endCoordinate = GetEndCoordinate(element); double size = endCoordinate - coordinate; if (size < 0) { size = -size; coordinate -= size; } if (isHorizontal) elementSize.Width = size; else elementSize.Height = size; } // shift for common tick labels, not for major ones. if (isHorizontal) { x = coordinate; if (element.HorizontalAlignment == HorizontalAlignment.Center) x = coordinate - elementSize.Width / 2; } else { y = coordinate; if (element.VerticalAlignment == VerticalAlignment.Center) y = coordinate - elementSize.Height / 2; else if (element.VerticalAlignment == VerticalAlignment.Bottom) y = coordinate - elementSize.Height; else if (element.VerticalAlignment == VerticalAlignment.Top) y = coordinate; } Rect bounds = new Rect(new Point(x, y), elementSize); element.Arrange(bounds); } return finalSize; } } }