285 lines
7.7 KiB
C#
285 lines
7.7 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Represents a panel on which elements are arranged in coordinates in viewport space.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="ViewportRectPanel"/> class.
|
|
/// </summary>
|
|
public ViewportPanel()
|
|
{
|
|
}
|
|
|
|
#region Panel methods override
|
|
|
|
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 res = availableSize;
|
|
|
|
DataRect ownViewportBounds = GetViewportBounds(child);
|
|
if (!ownViewportBounds.IsEmpty)
|
|
{
|
|
res = 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);
|
|
|
|
res = new Size(hasViewportWidth ? screenBounds.Width : selfWidth,
|
|
hasViewportHeight ? screenBounds.Height : selfHeight);
|
|
|
|
if (hasMinScreenWidth && res.Width < minScreenWidth)
|
|
{
|
|
res.Width = minScreenWidth;
|
|
}
|
|
}
|
|
|
|
if (res.Width.IsNaN()) res.Width = 0;
|
|
if (res.Height.IsNaN()) res.Height = 0;
|
|
|
|
return res;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|