Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / Ink / LassoSelectionBehavior.cs / 1305600 / LassoSelectionBehavior.cs
//#define DEBUG_LASSO_FEEDBACK // DO NOT LEAVE ENABLED IN CHECKED IN CODE
//----------------------------------------------------------------------------
//
// File: LassoSelectionBehavior.cs
//
// Description:
// LassoSelectionBehavior
//
// Authors: samgeo
//
// Copyright (C) 2003 by Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
using MS.Internal.Controls;
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Input;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Ink;
using System.Windows.Interop;
using System.Windows.Navigation;
using System.Windows.Media;
namespace MS.Internal.Ink
{
///
/// Eraser Behavior
///
internal sealed class LassoSelectionBehavior : StylusEditingBehavior
{
//-------------------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------------------
#region Constructors
internal LassoSelectionBehavior(EditingCoordinator editingCoordinator, InkCanvas inkCanvas)
: base(editingCoordinator, inkCanvas)
{
}
#endregion Constructors
//--------------------------------------------------------------------------------
//
// Protected Methods
//
//-------------------------------------------------------------------------------
#region Protected Methods
///
/// Overrides SwitchToMode as the following expectations
/// 31. From Select To InkAndGesture
/// Lasso is discarded. After mode change ink is being collected, gesture event fires. If its not a gesture, StrokeCollected event fires.
/// 32. From Select To GestureOnly
/// Lasso is discarded. After mode change ink is being collected. On StylusUp gesture event fires. Stroke gets removed on StylusUp even if its not a gesture.
/// 33. From Select To EraseByPoint
/// Lasso is discarded. PointErasing is performed after changing the mode.
/// 34. From Select To EraseByStroke
/// Lasso is discarded. StrokeErasing is performed after changing the mode.
/// 35. From Select To Ink
/// Ink collection starts when changing the mode.
/// 36. From Select To None
/// Nothing gets selected.
///
///
protected override void OnSwitchToMode(InkCanvasEditingMode mode)
{
Debug.Assert(EditingCoordinator.IsInMidStroke, "SwitchToMode should only be called in a mid-stroke");
switch ( mode )
{
case InkCanvasEditingMode.Ink:
case InkCanvasEditingMode.InkAndGesture:
case InkCanvasEditingMode.GestureOnly:
{
// Discard the lasso
Commit(false);
// Change the mode. The dynamic renderer will be reset automatically.
EditingCoordinator.ChangeStylusEditingMode(this, mode);
break;
}
case InkCanvasEditingMode.EraseByPoint:
case InkCanvasEditingMode.EraseByStroke:
{
// Discard the lasso
Commit(false);
// Change the mode
EditingCoordinator.ChangeStylusEditingMode(this, mode);
break;
}
case InkCanvasEditingMode.Select:
{
Debug.Assert(false, "Cannot switch from Select to Select in mid-stroke");
break;
}
case InkCanvasEditingMode.None:
{
// Discard the lasso.
Commit(false);
// Change to the None mode
EditingCoordinator.ChangeStylusEditingMode(this, mode);
break;
}
default:
Debug.Assert(false, "Unknown InkCanvasEditingMode!");
break;
}
}
///
/// StylusInputBegin
///
/// stylusPoints
/// true if the source eventArgs.UserInitiated flag was set to true
protected override void StylusInputBegin(StylusPointCollection stylusPoints, bool userInitiated)
{
Debug.Assert(stylusPoints.Count != 0, "An empty stylusPoints has been passed in.");
_disableLasso = false;
bool startLasso = false;
List points = new List();
for ( int x = 0; x < stylusPoints.Count; x++ )
{
Point point = (Point)( stylusPoints[x] );
if ( x == 0 )
{
_startPoint = point;
points.Add(point);
continue;
}
if ( !startLasso )
{
// If startLasso hasn't be flagged, we should check if the distance between two points is greater than
// our tolerance. If so, we should flag startLasso.
Vector vector = point - _startPoint;
double distanceSquared = vector.LengthSquared;
if ( DoubleUtil.GreaterThan(distanceSquared, LassoHelper.MinDistanceSquared) )
{
points.Add(point);
startLasso = true;
}
}
else
{
// The flag is set. We just add the point.
points.Add(point);
}
}
// Start Lasso if it isn't a tap selection.
if ( startLasso )
{
StartLasso(points);
}
}
///
/// StylusInputContinue
///
/// stylusPoints
/// true if the source eventArgs.UserInitiated flag was set to true
protected override void StylusInputContinue(StylusPointCollection stylusPoints, bool userInitiated)
{
// Check whether Lasso has started.
if ( _lassoHelper != null )
{
//
// pump packets to the LassoHelper, it will convert them into an array of equidistant
// lasso points, render those lasso point and return them to hit test against.
//
List points = new List();
for ( int x = 0; x < stylusPoints.Count; x++ )
{
points.Add((Point)stylusPoints[x]);
}
Point[] lassoPoints = _lassoHelper.AddPoints(points);
if ( 0 != lassoPoints.Length )
{
_incrementalLassoHitTester.AddPoints(lassoPoints);
}
}
else if ( !_disableLasso )
{
// If Lasso hasn't start and been disabled, we should try to start it when it is needed.
bool startLasso = false;
List points = new List();
for ( int x = 0; x < stylusPoints.Count; x++ )
{
Point point = (Point)( stylusPoints[x] );
if ( !startLasso )
{
// If startLasso hasn't be flagged, we should check if the distance between two points is greater than
// our tolerance. If so, we should flag startLasso.
Vector vector = point - _startPoint;
double distanceSquared = vector.LengthSquared;
if ( DoubleUtil.GreaterThan(distanceSquared, LassoHelper.MinDistanceSquared) )
{
points.Add(point);
startLasso = true;
}
}
else
{
// The flag is set. We just add the point.
points.Add(point);
}
}
// Start Lasso if it isn't a tap selection.
if ( startLasso )
{
StartLasso(points);
}
}
}
///
/// StylusInputEnd
///
/// commit
protected override void StylusInputEnd(bool commit)
{
// Initialize with empty selection
StrokeCollection selectedStrokes = new StrokeCollection();
List elementsToSelect = new List();
if ( _lassoHelper != null )
{
// This is a lasso selection.
//
// end dynamic rendering
//
selectedStrokes = InkCanvas.EndDynamicSelection(_lassoHelper.Visual);
//
// hit test for elements
//
// NOTE: HitTestForElements uses the _lassoHelper so it must be alive at this point
elementsToSelect = HitTestForElements();
_incrementalLassoHitTester.SelectionChanged -= new LassoSelectionChangedEventHandler(OnSelectionChanged);
_incrementalLassoHitTester.EndHitTesting();
_incrementalLassoHitTester = null;
_lassoHelper = null;
}
else
{
// This is a tap selection.
// Now try the tap selection
Stroke tappedStroke;
UIElement tappedElement;
TapSelectObject(_startPoint, out tappedStroke, out tappedElement);
// If we have a pre-selected object, we should select it now.
if ( tappedStroke != null )
{
Debug.Assert(tappedElement == null);
selectedStrokes = new StrokeCollection();
selectedStrokes.Add(tappedStroke);
}
else if ( tappedElement != null )
{
Debug.Assert(tappedStroke == null);
elementsToSelect.Add(tappedElement);
}
}
SelfDeactivate();
if ( commit )
{
InkCanvas.ChangeInkCanvasSelection(selectedStrokes, elementsToSelect.ToArray());
}
}
///
/// OnCommitWithoutStylusInput
///
///
protected override void OnCommitWithoutStylusInput(bool commit)
{
// We only deactivate LSB in this case.
SelfDeactivate();
}
///
/// Get the current cursor for this editing mode
///
///
protected override Cursor GetCurrentCursor()
{
// By default return cross cursor.
return Cursors.Cross;
}
#endregion Protected Methods
//--------------------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------------------
#region Private Methods
///
/// Private event handler that updates which strokes are actually selected
///
///
///
private void OnSelectionChanged(object sender, LassoSelectionChangedEventArgs e)
{
this.InkCanvas.UpdateDynamicSelection(e.SelectedStrokes, e.DeselectedStrokes);
}
///
/// Private helper that will hit test for elements
///
private List HitTestForElements()
{
List elementsToSelect = new List();
if ( this.InkCanvas.Children.Count == 0 )
{
return elementsToSelect;
}
for (int x = 0; x < this.InkCanvas.Children.Count; x++)
{
UIElement uiElement = this.InkCanvas.Children[x];
HitTestElement(InkCanvas.InnerCanvas, uiElement, elementsToSelect);
}
return elementsToSelect;
}
///
/// Private helper that will turn an element in any nesting level into a stroke
/// in the InkCanvas's coordinate space. This method calls itself recursively
///
private void HitTestElement(InkCanvasInnerCanvas parent, UIElement uiElement, List elementsToSelect)
{
ElementCornerPoints elementPoints = LassoSelectionBehavior.GetTransformedElementCornerPoints(parent, uiElement);
if (elementPoints.Set != false)
{
Point[] points = GeneratePointGrid(elementPoints);
//
// perform hit testing against our lasso
//
System.Diagnostics.Debug.Assert(null != _lassoHelper);
if (_lassoHelper.ArePointsInLasso(points, _percentIntersectForElements))
{
elementsToSelect.Add(uiElement);
}
}
//
// we used to recurse into the childrens children. That is no longer necessary
//
}
///
/// Private helper that takes an element and transforms it's 4 points
/// into the InkCanvas's space
///
private static ElementCornerPoints GetTransformedElementCornerPoints(InkCanvasInnerCanvas canvas, UIElement childElement)
{
Debug.Assert(canvas != null);
Debug.Assert(childElement != null);
Debug.Assert(canvas.CheckAccess());
ElementCornerPoints elementPoints = new ElementCornerPoints();
elementPoints.Set = false;
if (childElement.Visibility != Visibility.Visible)
{
//
// this little one's not worth it...
//
return elementPoints;
}
//
// get the transform from us to our parent InkCavas
//
GeneralTransform parentTransform = childElement.TransformToAncestor(canvas);
//
parentTransform.TryTransform(new Point(0, 0), out elementPoints.UpperLeft);
parentTransform.TryTransform(new Point(childElement.RenderSize.Width, 0), out elementPoints.UpperRight);
parentTransform.TryTransform(new Point(0, childElement.RenderSize.Height), out elementPoints.LowerLeft);
parentTransform.TryTransform(new Point(childElement.RenderSize.Width, childElement.RenderSize.Height), out elementPoints.LowerRight);
elementPoints.Set = true;
return elementPoints;
}
///
/// Private helper that will generate a grid of points 5 px apart given the elements bounding points
/// this works with any affline transformed points
///
private Point[] GeneratePointGrid(ElementCornerPoints elementPoints)
{
if (!elementPoints.Set)
{
return new Point[]{};
}
ArrayList pointArray = new ArrayList();
UpdatePointDistances(elementPoints);
//
// add our original points
//
pointArray.Add(elementPoints.UpperLeft);
pointArray.Add(elementPoints.UpperRight);
FillInPoints(pointArray, elementPoints.UpperLeft, elementPoints.UpperRight);
pointArray.Add(elementPoints.LowerLeft);
pointArray.Add(elementPoints.LowerRight);
FillInPoints(pointArray, elementPoints.LowerLeft, elementPoints.LowerRight);
FillInGrid( pointArray,
elementPoints.UpperLeft,
elementPoints.UpperRight,
elementPoints.LowerRight,
elementPoints.LowerLeft);
Point[] retPointArray = new Point[pointArray.Count];
pointArray.CopyTo(retPointArray);
return retPointArray;
}
///
/// Private helper that fills in the points between two points by calling itself
/// recursively in a divide and conquer fashion
///
private void FillInPoints(ArrayList pointArray, Point point1, Point point2)
{
// this algorithm improves perf by 20%
if(!PointsAreCloseEnough(point1, point2))
{
Point midPoint = LassoSelectionBehavior.GeneratePointBetweenPoints(point1, point2);
pointArray.Add(midPoint);
if(!PointsAreCloseEnough(point1, midPoint))
{
FillInPoints(pointArray, point1, midPoint);
}
//sort the right
if(!PointsAreCloseEnough(midPoint, point2))
{
FillInPoints(pointArray, midPoint, point2);
}
}
}
///
/// Private helper that fills in the points between four points by calling itself
/// recursively in a divide and conquer fashion
///
private void FillInGrid(ArrayList pointArray,
Point upperLeft,
Point upperRight,
Point lowerRight,
Point lowerLeft)
{
// this algorithm improves perf by 20%
if(!PointsAreCloseEnough(upperLeft, lowerLeft))
{
Point midPointLeft = LassoSelectionBehavior.GeneratePointBetweenPoints(upperLeft, lowerLeft);
Point midPointRight = LassoSelectionBehavior.GeneratePointBetweenPoints(upperRight, lowerRight);
pointArray.Add(midPointLeft);
pointArray.Add(midPointRight);
FillInPoints(pointArray, midPointLeft, midPointRight);
if(!PointsAreCloseEnough(upperLeft, midPointLeft))
{
FillInGrid(pointArray, upperLeft, upperRight, midPointRight, midPointLeft);
}
//sort the right
if(!PointsAreCloseEnough(midPointLeft, lowerLeft))
{
FillInGrid(pointArray, midPointLeft, midPointRight, lowerRight, lowerLeft);
}
}
}
///
/// Private helper that will generate a new point between two points
///
private static Point GeneratePointBetweenPoints(Point point1, Point point2)
{
//
// compute the new point in the middle of the previous two
//
double maxX = point1.X > point2.X ? point1.X : point2.X;
double minX = point1.X < point2.X ? point1.X : point2.X;
double maxY = point1.Y > point2.Y ? point1.Y : point2.Y;
double minY = point1.Y < point2.Y ? point1.Y : point2.Y;
return new Point( (minX + ((maxX - minX) * 0.5f)),
(minY + ((maxY - minY) * 0.5f)));
}
///
/// Private helper used to determine if we're close enough between two points
///
private bool PointsAreCloseEnough(Point point1, Point point2)
{
double x = point1.X - point2.X;
double y = point1.Y - point2.Y;
if ((x < _xDiff && x > -_xDiff) && (y < _yDiff && y > -_yDiff))
{
return true;
}
return false;
}
///
/// Used to calc the diff between points on the x and y axis
///
private void UpdatePointDistances(ElementCornerPoints elementPoints)
{
//
// calc the x and y diffs
//
double width = elementPoints.UpperLeft.X - elementPoints.UpperRight.X;
if (width < 0)
{
width = -width;
}
double height = elementPoints.UpperLeft.Y - elementPoints.LowerLeft.Y;
if (height < 0)
{
height = -height;
}
_xDiff = width * 0.25f;
if (_xDiff > _maxThreshold)
{
_xDiff = _maxThreshold;
}
else if (_xDiff < _minThreshold)
{
_xDiff = _minThreshold;
}
_yDiff = height * 0.25f;
if (_yDiff > _maxThreshold)
{
_yDiff = _maxThreshold;
}
else if (_yDiff < _minThreshold)
{
_yDiff = _minThreshold;
}
}
///
/// StartLasso
///
///
private void StartLasso(List points)
{
Debug.Assert(!_disableLasso && _lassoHelper == null, "StartLasso is called unexpectedly.");
if ( InkCanvas.ClearSelectionRaiseSelectionChanging() // If user cancels clearing the selection, we shouldn't initiate Lasso.
// NTRAID:WINDOWS#1534264-2006/02/28-WAYNEZEN
// If the active editng mode is no longer as Select, we shouldn't activate LassoSelectionBehavior.
// Note the order really matters here. This checking has to be done
// after ClearSelectionRaiseSelectionChanging is invoked.
&& EditingCoordinator.ActiveEditingMode == InkCanvasEditingMode.Select )
{
//
// obtain a dynamic hit-tester for selecting with lasso
//
_incrementalLassoHitTester =
this.InkCanvas.Strokes.GetIncrementalLassoHitTester(_percentIntersectForInk);
//
// add event handler
//
_incrementalLassoHitTester.SelectionChanged += new LassoSelectionChangedEventHandler(OnSelectionChanged);
//
// start dynamic rendering
//
_lassoHelper = new LassoHelper();
InkCanvas.BeginDynamicSelection(_lassoHelper.Visual);
Point[] lassoPoints = _lassoHelper.AddPoints(points);
if ( 0 != lassoPoints.Length )
{
_incrementalLassoHitTester.AddPoints(lassoPoints);
}
}
else
{
// If we fail on clearing the selection or switching to Select mode, we should just disable lasso.
_disableLasso = true;
}
}
///
/// Pre-Select the object which user taps on.
///
///
///
///
private void TapSelectObject(Point point, out Stroke tappedStroke, out UIElement tappedElement)
{
tappedStroke = null;
tappedElement = null;
StrokeCollection hitTestStrokes = InkCanvas.Strokes.HitTest(point, 5.0d);
if ( hitTestStrokes.Count > 0 )
{
tappedStroke = hitTestStrokes[hitTestStrokes.Count - 1];
}
else
{
GeneralTransform transformToInnerCanvas = InkCanvas.TransformToVisual(InkCanvas.InnerCanvas);
Point pointOnInnerCanvas = transformToInnerCanvas.Transform(point);
// Try to find out whether we have a pre-select object.
tappedElement = InkCanvas.InnerCanvas.HitTestOnElements(pointOnInnerCanvas);
}
}
#endregion Private Methods
//-------------------------------------------------------------------------------
//
// Private Fields
//
//--------------------------------------------------------------------------------
#region Private Fields
private Point _startPoint;
private bool _disableLasso;
private LassoHelper _lassoHelper = null;
private IncrementalLassoHitTester _incrementalLassoHitTester;
private double _xDiff;
private double _yDiff;
private const double _maxThreshold = 50f;
private const double _minThreshold = 15f;
private const int _percentIntersectForInk = 80;
private const int _percentIntersectForElements = 60;
#if DEBUG_LASSO_FEEDBACK
private ContainerVisual _tempVisual;
#endif
///
/// Private struct
///
private struct ElementCornerPoints
{
internal Point UpperLeft;
internal Point UpperRight;
internal Point LowerRight;
internal Point LowerLeft;
internal bool Set;
}
#endregion Private Fields
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- TextWriterTraceListener.cs
- FuncCompletionCallbackWrapper.cs
- CodeDelegateCreateExpression.cs
- CompoundFileStreamReference.cs
- NativeMethods.cs
- PopOutPanel.cs
- Item.cs
- loginstatus.cs
- DbDataAdapter.cs
- CompositeActivityMarkupSerializer.cs
- EventData.cs
- FactoryMaker.cs
- DataStreamFromComStream.cs
- MailWriter.cs
- SessionStateContainer.cs
- CLRBindingWorker.cs
- PropertyChangedEventArgs.cs
- CompatibleIComparer.cs
- IPHostEntry.cs
- HandlerBase.cs
- QuotedPrintableStream.cs
- AcceptorSessionSymmetricMessageSecurityProtocol.cs
- MatrixAnimationUsingKeyFrames.cs
- DBCSCodePageEncoding.cs
- MachineKeySection.cs
- ButtonPopupAdapter.cs
- RelatedView.cs
- ConfigurationLockCollection.cs
- SuppressMessageAttribute.cs
- InvokeSchedule.cs
- Messages.cs
- WebServiceHandler.cs
- StoryFragments.cs
- ProfilePropertyMetadata.cs
- ZipIOCentralDirectoryDigitalSignature.cs
- Mappings.cs
- TreeNodeClickEventArgs.cs
- DBSchemaTable.cs
- DocumentEventArgs.cs
- SizeConverter.cs
- BitmapMetadataEnumerator.cs
- ServiceContractViewControl.Designer.cs
- StylusOverProperty.cs
- QueryContinueDragEventArgs.cs
- XPathBinder.cs
- SequentialOutput.cs
- PlanCompilerUtil.cs
- PreviewKeyDownEventArgs.cs
- RegexFCD.cs
- DataGridViewImageColumn.cs
- SqlReferenceCollection.cs
- QueryPageSettingsEventArgs.cs
- VideoDrawing.cs
- RegisteredExpandoAttribute.cs
- ColorMatrix.cs
- CompensationDesigner.cs
- TypeNameConverter.cs
- ColumnMapVisitor.cs
- HtmlAnchor.cs
- Evaluator.cs
- DoubleUtil.cs
- figurelengthconverter.cs
- StaticExtension.cs
- NamespaceDisplayAutomationPeer.cs
- ChannelPoolSettingsElement.cs
- SuppressMergeCheckAttribute.cs
- ContentValidator.cs
- ZipQueryOperator.cs
- Splitter.cs
- SchemaImporterExtensionElement.cs
- FrameworkContentElement.cs
- MonthChangedEventArgs.cs
- SqlConnectionPoolProviderInfo.cs
- PieceDirectory.cs
- XpsFilter.cs
- PartialClassGenerationTask.cs
- MaterialCollection.cs
- ErrorLog.cs
- TimelineClockCollection.cs
- PasswordBox.cs
- Operand.cs
- ForceCopyBuildProvider.cs
- ReflectionUtil.cs
- GeneralTransform3D.cs
- PageParserFilter.cs
- TCPClient.cs
- OleDbInfoMessageEvent.cs
- EventLogger.cs
- CodeNamespaceCollection.cs
- MethodSignatureGenerator.cs
- URLString.cs
- KeyBinding.cs
- Button.cs
- RichTextBox.cs
- TextMessageEncodingElement.cs
- SequentialUshortCollection.cs
- sqlnorm.cs
- CodeDOMUtility.cs
- UnsignedPublishLicense.cs
- XmlWellformedWriter.cs