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; Listpoints = 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. // Listpoints = 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(); ListelementsToSelect = 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 ListHitTestForElements() { 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, ListelementsToSelect) { 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(Listpoints) { 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
- HttpHandlerAction.cs
- OracleParameterBinding.cs
- PlaceHolder.cs
- MarkerProperties.cs
- ExtendedPropertyDescriptor.cs
- DataGridViewCellStyleConverter.cs
- WebBrowser.cs
- ToolStrip.cs
- GroupItem.cs
- DragStartedEventArgs.cs
- DataGridCommandEventArgs.cs
- SecureUICommand.cs
- EdmItemCollection.OcAssemblyCache.cs
- ThreadInterruptedException.cs
- LinearGradientBrush.cs
- GridItemCollection.cs
- ExtensionSimplifierMarkupObject.cs
- DataGridItemEventArgs.cs
- BaseCollection.cs
- KoreanLunisolarCalendar.cs
- SymbolTable.cs
- CurrentTimeZone.cs
- XNodeValidator.cs
- PolicyLevel.cs
- SHA256Managed.cs
- Subtree.cs
- TemplateBamlRecordReader.cs
- HttpCookiesSection.cs
- WebSysDisplayNameAttribute.cs
- Single.cs
- PromptBuilder.cs
- DesignerRegion.cs
- TaskFileService.cs
- OrderedDictionary.cs
- Size.cs
- LongSumAggregationOperator.cs
- IndependentlyAnimatedPropertyMetadata.cs
- ExpandableObjectConverter.cs
- LineGeometry.cs
- ISFClipboardData.cs
- SQLChars.cs
- Latin1Encoding.cs
- AsymmetricSignatureFormatter.cs
- DeflateStream.cs
- AspNetCacheProfileAttribute.cs
- StringReader.cs
- NextPreviousPagerField.cs
- ShaperBuffers.cs
- NetworkInformationException.cs
- DesignTimeValidationFeature.cs
- ChtmlTextWriter.cs
- DataServices.cs
- Int16AnimationBase.cs
- iisPickupDirectory.cs
- httpapplicationstate.cs
- TemplateApplicationHelper.cs
- EarlyBoundInfo.cs
- ParameterModifier.cs
- SettingsProperty.cs
- StrokeCollectionConverter.cs
- Propagator.ExtentPlaceholderCreator.cs
- ObjectStateFormatter.cs
- RequestedSignatureDialog.cs
- MenuItemBinding.cs
- TextElementAutomationPeer.cs
- KeyValueConfigurationElement.cs
- TreeChangeInfo.cs
- DaylightTime.cs
- AutoCompleteStringCollection.cs
- EdmConstants.cs
- ExpressionBindingCollection.cs
- EncoderBestFitFallback.cs
- UnsafeNativeMethods.cs
- _ChunkParse.cs
- RelationshipFixer.cs
- OleDbConnectionPoolGroupProviderInfo.cs
- ColorIndependentAnimationStorage.cs
- FixedPageStructure.cs
- DBNull.cs
- Panel.cs
- VersionPair.cs
- OleDbCommand.cs
- ControlCollection.cs
- sqlser.cs
- MachineKeySection.cs
- VerificationAttribute.cs
- MetadataItemCollectionFactory.cs
- KnownTypesProvider.cs
- DrawingBrush.cs
- TextSelectionProcessor.cs
- SHA1CryptoServiceProvider.cs
- ChannelBinding.cs
- SwitchElementsCollection.cs
- FormatterServices.cs
- QuaternionRotation3D.cs
- BehaviorDragDropEventArgs.cs
- ContravarianceAdapter.cs
- _ChunkParse.cs
- SynchronizationScope.cs
- EditorZone.cs