|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// Description:
// Defines a Canvas-like class which is used by InkCanvas for the layout.
//
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace MS.Internal.Controls
{
/// <summary>
/// A subclass of Panel which does layout for InkCanvas.
/// </summary>
internal class InkCanvasInnerCanvas : Panel
{
//------------------------------------------------------
//
// Cnostructors
//
//------------------------------------------------------
#region Constructors
internal InkCanvasInnerCanvas(InkCanvas inkCanvas)
{
Debug.Assert(inkCanvas != null);
_inkCanvas = inkCanvas;
}
// No default constructor
private InkCanvasInnerCanvas() { }
#endregion Constructors
//------------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
#region Protected Methods
/// <summary>
/// Override OnVisualChildrenChanged
/// </summary>
/// <param name="visualAdded"></param>
/// <param name="visualRemoved"></param>
protected internal override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
UIElement removedElement = visualRemoved as UIElement;
// If there is an element being removed, we should make sure to update our selected elements list..
if (removedElement != null)
{
InkCanvas.InkCanvasSelection.RemoveElement(removedElement);
}
//resurface this on the containing InkCanvas
InkCanvas.RaiseOnVisualChildrenChanged(visualAdded, visualRemoved);
}
/// <summary>
/// Override of <seealso cref="FrameworkElement.MeasureOverride" />
/// The code is similar to Canvas.MeasureOverride. The only difference we have is that
/// InkCanvasInnerCanvas does report the size based on its children's sizes.
/// </summary>
/// <param name="constraint">Constraint size.</param>
/// <returns>Computed desired size.</returns>
protected override Size MeasureOverride(Size constraint)
{
Size childConstraint = new Size(Double.PositiveInfinity, Double.PositiveInfinity);
Size newSize = new Size();
foreach (UIElement child in InternalChildren)
{
if (child == null) { continue; }
child.Measure(childConstraint);
// NOTICE-2006/02/03-WAYNEZEN,
// We only honor Left and/or Top property for the measure.
// For Right/Bottom, only the child.Width/Height will be used. Those properties will be used by the arrange
// but not the measure.
double left = (double)InkCanvas.GetLeft(child);
if (!double.IsNaN(left))
{
newSize.Width = Math.Max(newSize.Width, left + child.DesiredSize.Width);
}
else
{
newSize.Width = Math.Max(newSize.Width, child.DesiredSize.Width);
}
double top = (double)InkCanvas.GetTop(child);
if (!double.IsNaN(top))
{
newSize.Height = Math.Max(newSize.Height, top + child.DesiredSize.Height);
}
else
{
newSize.Height = Math.Max(newSize.Height, child.DesiredSize.Height);
}
}
return newSize;
}
/// <summary>
/// Canvas computes a position for each of its children taking into account their margin and
/// attached Canvas properties: Top, Left.
///
/// Canvas will also arrange each of its children.
/// This code is same as the Canvas'.
/// </summary>
/// <param name="arrangeSize">Size that Canvas will assume to position children.</param>
protected override Size ArrangeOverride(Size arrangeSize)
{
//Canvas arranges children at their DesiredSize.
//This means that Margin on children is actually respected and added
//to the size of layout partition for a child.
//Therefore, is Margin is 10 and Left is 20, the child's ink will start at 30.
foreach (UIElement child in InternalChildren)
{
if (child == null) { continue; }
double x = 0;
double y = 0;
//Compute offset of the child:
//If Left is specified, then Right is ignored
//If Left is not specified, then Right is used
//If both are not there, then 0
double left = (double)InkCanvas.GetLeft(child);
if (!double.IsNaN(left))
{
x = left;
}
else
{
double right = (double)InkCanvas.GetRight(child);
if (!double.IsNaN(right))
{
x = arrangeSize.Width - child.DesiredSize.Width - right;
}
}
double top = (double)InkCanvas.GetTop(child);
if (!double.IsNaN(top))
{
y = top;
}
else
{
double bottom = (double)InkCanvas.GetBottom(child);
if (!double.IsNaN(bottom))
{
y = arrangeSize.Height - child.DesiredSize.Height - bottom;
}
}
child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
}
return arrangeSize;
}
/// <summary>
/// OnChildDesiredSizeChanged
/// </summary>
/// <param name="child"></param>
protected override void OnChildDesiredSizeChanged(UIElement child)
{
base.OnChildDesiredSizeChanged(child);
// Invalid InkCanvasInnerCanvas' measure.
InvalidateMeasure();
}
/// <summary>
/// Override CreateUIElementCollection method.
/// The logical parent of InnerCanvas will be set to InkCanvas instead.
/// </summary>
/// <param name="logicalParent"></param>
/// <returns></returns>
protected override UIElementCollection CreateUIElementCollection(FrameworkElement logicalParent)
{
// Replace the logical parent of the InnerCanvas children with our InkCanvas.
return base.CreateUIElementCollection(_inkCanvas);
}
/// <summary>
/// Returns LogicalChildren
/// </summary>
protected internal override IEnumerator LogicalChildren
{
get
{
// InnerCanvas won't have any logical children publicly.
return EmptyEnumerator.Instance;
}
}
/// <summary>
/// The overridden GetLayoutClip method
/// </summary>
/// <returns>Geometry to use as additional clip if ClipToBounds=true</returns>
protected override Geometry GetLayoutClip(Size layoutSlotSize)
{
//
// By default an FE will clip its content if the ink size exceeds the layout size (the final arrange size).
// Since we are auto growing, the ink size is same as the desired size. So it ends up the strokes will be clipped
// regardless ClipToBounds is set or not.
// We override the GetLayoutClip method so that we can bypass the default layout clip if ClipToBounds is set to false.
if (ClipToBounds)
{
return base.GetLayoutClip(layoutSlotSize);
}
else
return null;
}
#endregion Protected Methods
//------------------------------------------------------
//
// Internal Methods
//
//------------------------------------------------------
#region Internal Methods
/// <summary>
/// Hit test on the children
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
internal UIElement HitTestOnElements(Point point)
{
UIElement hitElement = null;
// Do hittest.
HitTestResult hitTestResult = VisualTreeHelper.HitTest(this, point);
// Now find out which element is hit if there is a result.
if (hitTestResult != null)
{
Visual visual = hitTestResult.VisualHit as Visual;
System.Windows.Media.Media3D.Visual3D visual3D = hitTestResult.VisualHit as System.Windows.Media.Media3D.Visual3D;
DependencyObject currentObject = null;
if (visual != null)
{
currentObject = visual;
}
else if (visual3D != null)
{
currentObject = visual3D;
}
while (currentObject != null)
{
DependencyObject parent = VisualTreeHelper.GetParent(currentObject);
if (parent == InkCanvas.InnerCanvas)
{
// Break when we hit the inner canvas in the visual tree.
hitElement = currentObject as UIElement;
Debug.Assert(Children.Contains(hitElement), "The hit element should be a child of InnerCanvas.");
break;
}
else
{
currentObject = parent;
}
}
}
return hitElement;
}
/// <summary>
/// Returns the private logical children
/// </summary>
internal IEnumerator PrivateLogicalChildren
{
get
{
// Return the logical children of the base - Canvas
return base.LogicalChildren;
}
}
/// <summary>
/// Returns the associated InkCanvas
/// </summary>
internal InkCanvas InkCanvas
{
get
{
return _inkCanvas;
}
}
#endregion Internal Methods
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
#endregion Private Methods
//------------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
#region Private Fields
// The host InkCanvas
private InkCanvas _inkCanvas;
#endregion Private Fields
}
}
|