//
//
//
//
// $Revision$
//
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using ICSharpCode.WpfDesign.Adorners;
using ICSharpCode.WpfDesign.Designer.Controls;
namespace ICSharpCode.WpfDesign.Designer
{
sealed class DesignPanel : Decorator, IDesignPanel
{
#region Hit Testing
///
/// this element is always hit (unless HitTestVisible is set to false)
///
sealed class EatAllHitTestRequests : UIElement
{
protected override GeometryHitTestResult HitTestCore(GeometryHitTestParameters hitTestParameters)
{
return new GeometryHitTestResult(this, IntersectionDetail.FullyContains);
}
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
return new PointHitTestResult(this, hitTestParameters.HitPoint);
}
}
static void RunHitTest(Visual reference, Point point, HitTestFilterCallback filterCallback, HitTestResultCallback resultCallback)
{
VisualTreeHelper.HitTest(reference, filterCallback, resultCallback,
new PointHitTestParameters(point));
}
static HitTestFilterBehavior FilterHitTestInvisibleElements(DependencyObject potentialHitTestTarget)
{
UIElement element = potentialHitTestTarget as UIElement;
if (element != null) {
if (!(element.IsHitTestVisible && element.Visibility == Visibility.Visible)) {
return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
}
}
return HitTestFilterBehavior.Continue;
}
///
/// Performs a custom hit testing lookup for the specified mouse event args.
///
public DesignPanelHitTestResult HitTest(Point mousePosition, bool testAdorners, bool testDesignSurface)
{
DesignPanelHitTestResult result = DesignPanelHitTestResult.NoHit;
HitTest(mousePosition, testAdorners, testDesignSurface,
delegate(DesignPanelHitTestResult r) {
result = r;
return false;
});
return result;
}
///
/// Performs a hit test on the design surface, raising for each match.
/// Hit testing continues while the callback returns true.
///
public void HitTest(Point mousePosition, bool testAdorners, bool testDesignSurface, Predicate callback)
{
if (mousePosition.X < 0 || mousePosition.Y < 0 || mousePosition.X > this.RenderSize.Width || mousePosition.Y > this.RenderSize.Height) {
return;
}
// First try hit-testing on the adorner layer.
bool continueHitTest = true;
if (testAdorners) {
RunHitTest(
_adornerLayer, mousePosition, FilterHitTestInvisibleElements,
delegate(HitTestResult result) {
if (result != null && result.VisualHit != null && result.VisualHit is Visual) {
DesignPanelHitTestResult customResult = new DesignPanelHitTestResult((Visual)result.VisualHit);
DependencyObject obj = result.VisualHit;
while (obj != null && obj != _adornerLayer) {
AdornerPanel adorner = obj as AdornerPanel;
if (adorner != null) {
customResult.AdornerHit = adorner;
}
obj = VisualTreeHelper.GetParent(obj);
}
continueHitTest = callback(customResult);
return continueHitTest ? HitTestResultBehavior.Continue : HitTestResultBehavior.Stop;
} else {
return HitTestResultBehavior.Continue;
}
});
}
if (continueHitTest && testDesignSurface) {
RunHitTest(
this.Child, mousePosition, delegate { return HitTestFilterBehavior.Continue; },
delegate(HitTestResult result) {
if (result != null && result.VisualHit != null && result.VisualHit is Visual) {
DesignPanelHitTestResult customResult = new DesignPanelHitTestResult((Visual)result.VisualHit);
ViewService viewService = _context.Services.View;
DependencyObject obj = result.VisualHit;
while (obj != null) {
if ((customResult.ModelHit = viewService.GetModel(obj)) != null)
break;
obj = VisualTreeHelper.GetParent(obj);
}
if (customResult.ModelHit == null) {
customResult.ModelHit = _context.RootItem;
}
continueHitTest = callback(customResult);
return continueHitTest ? HitTestResultBehavior.Continue : HitTestResultBehavior.Stop;
} else {
return HitTestResultBehavior.Continue;
}
}
);
}
}
#endregion
#region Fields + Constructor
DesignContext _context;
readonly EatAllHitTestRequests _eatAllHitTestRequests;
readonly AdornerLayer _adornerLayer;
public DesignPanel()
{
this.Focusable = true;
this.AllowDrop = true;
DesignerProperties.SetIsInDesignMode(this, true);
_eatAllHitTestRequests = new EatAllHitTestRequests();
_eatAllHitTestRequests.MouseDown += delegate {
// ensure the design panel has focus while the user is interacting with it
this.Focus();
};
_eatAllHitTestRequests.AllowDrop = true;
_adornerLayer = new AdornerLayer(this);
}
#endregion
#region Properties
///
/// Gets/Sets the design context.
///
public DesignContext Context {
get { return _context; }
set { _context = value; }
}
public ICollection Adorners {
get {
return _adornerLayer.Adorners;
}
}
///
/// Gets/Sets if the design content is visible for hit-testing purposes.
///
public bool IsContentHitTestVisible {
get { return !_eatAllHitTestRequests.IsHitTestVisible; }
set { _eatAllHitTestRequests.IsHitTestVisible = !value; }
}
///
/// Gets/Sets if the adorner layer is visible for hit-testing purposes.
///
public bool IsAdornerLayerHitTestVisible {
get { return _adornerLayer.IsHitTestVisible; }
set { _adornerLayer.IsHitTestVisible = value; }
}
#endregion
#region Visual Child Management
public override UIElement Child {
get { return base.Child; }
set {
if (base.Child == value)
return;
if (value == null) {
// Child is being set from some value to null
// remove _adornerLayer and _eatAllHitTestRequests
RemoveVisualChild(_adornerLayer);
RemoveVisualChild(_eatAllHitTestRequests);
} else if (base.Child == null) {
// Child is being set from null to some value
AddVisualChild(_adornerLayer);
AddVisualChild(_eatAllHitTestRequests);
}
base.Child = value;
}
}
protected override Visual GetVisualChild(int index)
{
if (base.Child != null) {
if (index == 0)
return base.Child;
else if (index == 1)
return _eatAllHitTestRequests;
else if (index == 2)
return _adornerLayer;
}
return base.GetVisualChild(index);
}
protected override int VisualChildrenCount {
get {
if (base.Child != null)
return 3;
else
return base.VisualChildrenCount;
}
}
protected override Size MeasureOverride(Size constraint)
{
Size result = base.MeasureOverride(constraint);
if (this.Child != null) {
_adornerLayer.Measure(constraint);
_eatAllHitTestRequests.Measure(constraint);
}
return result;
}
protected override Size ArrangeOverride(Size arrangeSize)
{
Size result = base.ArrangeOverride(arrangeSize);
if (this.Child != null) {
Rect r = new Rect(new Point(0, 0), arrangeSize);
_adornerLayer.Arrange(r);
_eatAllHitTestRequests.Arrange(r);
}
return result;
}
#endregion
protected override void OnQueryCursor(QueryCursorEventArgs e)
{
base.OnQueryCursor(e);
if (_context != null) {
Cursor cursor = _context.Services.Tool.CurrentTool.Cursor;
if (cursor != null) {
e.Cursor = cursor;
e.Handled = true;
}
}
}
}
}