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; using Microsoft.Research.DynamicDataDisplay.Common.Auxiliary; using System.ComponentModel; namespace Microsoft.Research.DynamicDataDisplay.Charts { /// /// Represents a panel on which elements are arranged in coordinates in viewport space. /// public partial class ViewportPanel : IndividualArrangePanel { static ViewportPanel() { Type thisType = typeof(ViewportPanel); Plotter.PlotterProperty.OverrideMetadata(thisType, new FrameworkPropertyMetadata { PropertyChangedCallback = OnPlotterChanged }); } private static void OnPlotterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ViewportPanel panel = (ViewportPanel)d; panel.OnPlotterChanged((Plotter)e.NewValue, (Plotter)e.OldValue); } private void OnPlotterChanged(Plotter currPlotter, Plotter prevPlotter) { if (currPlotter != null) { Plotter2D plotter2d = (Plotter2D)currPlotter; viewport = plotter2d.Viewport; } else { this.viewport = null; } } /// /// Initializes a new instance of the class. /// public ViewportPanel() { } #region Panel methods override protected override void OnChildDesiredSizeChanged(UIElement child) { base.OnChildDesiredSizeChanged(child); InvalidateMeasure(); } protected internal override void OnChildAdded(FrameworkElement child) { InvalidatePosition(child); } protected virtual void InvalidatePosition(FrameworkElement child) { if (viewport == null) return; var transform = GetTransform(availableSize); Size elementSize = GetElementSize(child, AvailableSize, transform); child.Measure(elementSize); Rect bounds = GetElementScreenBounds(transform, child); if (!bounds.IsNaN()) { child.Arrange(bounds); } } private Size availableSize; protected Size AvailableSize { get { return availableSize; } set { availableSize = value; } } protected override Size MeasureOverride(Size availableSize) { this.availableSize = availableSize; var transform = GetTransform(availableSize); foreach (FrameworkElement child in InternalChildren) { if (child != null) { Size elementSize = GetElementSize(child, availableSize, transform); child.Measure(elementSize); } } if (availableSize.Width.IsInfinite()) availableSize.Width = 0; if (availableSize.Height.IsInfinite()) availableSize.Height = 0; return availableSize; } protected virtual Size GetElementSize(FrameworkElement child, Size availableSize, CoordinateTransform transform) { Size result = availableSize; DataRect ownViewportBounds = GetViewportBounds(child); if (!ownViewportBounds.IsEmpty) { result = ownViewportBounds.ViewportToScreen(transform).Size; } else { double viewportWidth = GetViewportWidth(child); double viewportHeight = GetViewportHeight(child); bool hasViewportWidth = viewportWidth.IsNotNaN(); bool hasViewportHeight = viewportHeight.IsNotNaN(); double minScreenWidth = GetMinScreenWidth(child); bool hasMinScreenWidth = minScreenWidth.IsNotNaN(); double selfWidth = child.Width.IsNotNaN() ? child.Width : availableSize.Width; double width = hasViewportWidth ? viewportWidth : selfWidth; double selfHeight = child.Height.IsNotNaN() ? child.Height : availableSize.Height; double height = hasViewportHeight ? viewportHeight : selfHeight; if (width < 0) width = 0; if (height < 0) height = 0; DataRect bounds = new DataRect(new Size(width, height)); Rect screenBounds = bounds.ViewportToScreen(transform); result = new Size(hasViewportWidth ? screenBounds.Width : selfWidth, hasViewportHeight ? screenBounds.Height : selfHeight); if (hasMinScreenWidth && result.Width < minScreenWidth) { result.Width = minScreenWidth; } } if (result.Width.IsNaN()) result.Width = 0; if (result.Height.IsNaN()) result.Height = 0; return result; } protected Rect GetElementScreenBounds(CoordinateTransform transform, UIElement child) { Rect screenBounds = GetElementScreenBoundsCore(transform, child); DataRect viewportBounds = screenBounds.ScreenToViewport(transform); DataRect prevViewportBounds = GetActualViewportBounds(child); SetPrevActualViewportBounds(child, prevViewportBounds); SetActualViewportBounds(child, viewportBounds); return screenBounds; } protected virtual Rect GetElementScreenBoundsCore(CoordinateTransform transform, UIElement child) { Rect bounds = new Rect(0, 0, 1, 1); DataRect ownViewportBounds = GetViewportBounds(child); if (!ownViewportBounds.IsEmpty) { bounds = ownViewportBounds.ViewportToScreen(transform); } else { double viewportX = GetX(child); double viewportY = GetY(child); if (viewportX.IsNaN() || viewportY.IsNaN()) { //Debug.WriteLine("ViewportRectPanel: Position is not set!"); return bounds; } double viewportWidth = GetViewportWidth(child); if (viewportWidth < 0) viewportWidth = 0; double viewportHeight = GetViewportHeight(child); if (viewportHeight < 0) viewportHeight = 0; bool hasViewportWidth = viewportWidth.IsNotNaN(); bool hasViewportHeight = viewportHeight.IsNotNaN(); DataRect r = new DataRect(new Size(hasViewportWidth ? viewportWidth : child.DesiredSize.Width, hasViewportHeight ? viewportHeight : child.DesiredSize.Height)); r = r.ViewportToScreen(transform); double screenWidth = hasViewportWidth ? r.Width : child.DesiredSize.Width; double screenHeight = hasViewportHeight ? r.Height : child.DesiredSize.Height; double minScreenWidth = GetMinScreenWidth(child); bool hasMinScreemWidth = minScreenWidth.IsNotNaN(); if (hasViewportWidth && screenWidth < minScreenWidth) screenWidth = minScreenWidth; Point location = new Point(viewportX, viewportY).ViewportToScreen(transform); double screenX = location.X; double screenY = location.Y; HorizontalAlignment horizAlignment = GetViewportHorizontalAlignment(child); switch (horizAlignment) { case HorizontalAlignment.Stretch: case HorizontalAlignment.Center: screenX -= screenWidth / 2; break; case HorizontalAlignment.Left: break; case HorizontalAlignment.Right: screenX -= screenWidth; break; } VerticalAlignment vertAlignment = GetViewportVerticalAlignment(child); switch (vertAlignment) { case VerticalAlignment.Bottom: screenY -= screenHeight; break; case VerticalAlignment.Center: case VerticalAlignment.Stretch: screenY -= screenHeight / 2; break; case VerticalAlignment.Top: break; default: break; } bounds = new Rect(screenX, screenY, screenWidth, screenHeight); } // applying screen offset double screenOffsetX = GetScreenOffsetX(child); if (screenOffsetX.IsNaN()) screenOffsetX = 0; double screenOffsetY = GetScreenOffsetY(child); if (screenOffsetY.IsNaN()) screenOffsetY = 0; Vector screenOffset = new Vector(screenOffsetX, screenOffsetY); bounds.Offset(screenOffset); return bounds; } protected override Size ArrangeOverride(Size finalSize) { var transform = GetTransform(finalSize); foreach (UIElement child in InternalChildren) { if (child != null) { Rect bounds = GetElementScreenBounds(transform, child); if (!bounds.IsNaN()) { child.Arrange(bounds); } } } return finalSize; } private CoordinateTransform GetTransform(Size size) { return viewport.Transform.WithRects(ViewportPanel.GetViewportBounds(this), new Rect(size)); } #endregion private Viewport2D viewport; } }