Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / MS / Internal / Ink / InkCanvasSelection.cs / 1 / InkCanvasSelection.cs
//---------------------------------------------------------------------------- // // File: InkCanvasSelection.cs // // Description: // Element that represents InkCanvas selection // // History: // 9/01/2004 [....]: Created // // Copyright (C) 2003 by Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using MS.Utility; using MS.Internal; using MS.Internal.Controls; using MS.Internal.Ink; using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.ComponentModel; using System.ComponentModel.Design; using System.Windows; using System.Windows.Media; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Controls; using System.Windows.Markup; using System.Windows.Threading; namespace MS.Internal.Ink { ////// InkCanvasSelection /// internal sealed class InkCanvasSelection { //------------------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------------------- #region Constructors ////// InkCanvasSelection has an internal constructor to prevent direct instantiation /// /// inkCanvas internal InkCanvasSelection(InkCanvas inkCanvas) { //validate if (inkCanvas == null) { throw new ArgumentNullException("inkCanvas"); } _inkCanvas = inkCanvas; _inkCanvas.FeedbackAdorner.UpdateBounds(Rect.Empty); } #endregion Constructors //-------------------------------------------------------------------------------- // // Internal Properties // //------------------------------------------------------------------------------- #region Internal Properties ////// Returns the collection of selected strokes /// internal StrokeCollection SelectedStrokes { get { if ( _selectedStrokes == null ) { _selectedStrokes = new StrokeCollection(); _areStrokesChanged = true; } return _selectedStrokes; } } ////// Returns the collection of selected elements /// internal ReadOnlyCollectionSelectedElements { get { if ( _selectedElements == null ) { _selectedElements = new List (); } return new ReadOnlyCollection (_selectedElements); } } /// /// Indicates whether there is a selection in the current InkCanvas. /// internal bool HasSelection { get { return SelectedStrokes.Count != 0 || SelectedElements.Count != 0; } } ////// Returns the selection bounds /// internal Rect SelectionBounds { get { return Rect.Union(GetStrokesBounds(), GetElementsUnionBounds()); } } #endregion Internal Properties //-------------------------------------------------------------------------------- // // Internal Methods // //-------------------------------------------------------------------------------- #region Internal Methods ////// Start a feedback rubberband. /// This method is called from SelectionEditingBehavior.OnActivated /// /// /// internal void StartFeedbackAdorner(Rect feedbackRect, InkCanvasSelectionHitResult activeSelectionHitResult) { Debug.Assert( _inkCanvas.EditingCoordinator.UserIsEditing == true ); Debug.Assert(activeSelectionHitResult != InkCanvasSelectionHitResult.None, "activeSelectionHitResult cannot be InkCanvasSelectionHitResult.None."); _activeSelectionHitResult = activeSelectionHitResult; AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(_inkCanvas.InnerCanvas); // Until the next start, we are going to use this adorner regardless user will change feedback adorner or not during the editing. InkCanvasFeedbackAdorner feedbackAdorner = _inkCanvas.FeedbackAdorner; // The feedback adorner shouldn't have been connected to the adorner layer yet. Debug.Assert(VisualTreeHelper.GetParent(feedbackAdorner) == null, "feedbackAdorner shouldn't be added to tree."); // Now, attach the feedback adorner to the adorner layer. Then update its bounds adornerLayer.Add(feedbackAdorner); feedbackAdorner.UpdateBounds(feedbackRect); } ////// This method updates the feedback rubberband. /// This method is called from SelectionEditingBehavior.OnMouseMove. /// /// internal void UpdateFeedbackAdorner(Rect feedbackRect) { Debug.Assert(VisualTreeHelper.GetParent(_inkCanvas.FeedbackAdorner) == AdornerLayer.GetAdornerLayer(_inkCanvas.InnerCanvas), "feedbackAdorner should have been added to tree."); // Update the feedback bounds _inkCanvas.FeedbackAdorner.UpdateBounds(feedbackRect); } ////// Ends a feedback rubberband /// This method is called from SelectionEditingBehavior.OnMoveUp /// /// internal void EndFeedbackAdorner(Rect finalRectangle) { AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(_inkCanvas.InnerCanvas); InkCanvasFeedbackAdorner feedbackAdorner = _inkCanvas.FeedbackAdorner; Debug.Assert(VisualTreeHelper.GetParent(feedbackAdorner) == adornerLayer, "feedbackAdorner should have been added to tree."); // Reset the feedback bounds and detach it from the adorner layer. feedbackAdorner.UpdateBounds(Rect.Empty); adornerLayer.Remove(feedbackAdorner); // Commit the new rectange of the selection. CommitChanges(finalRectangle, true); _activeSelectionHitResult = null; } ////// Set the new selection to this helper class. /// /// /// /// internal void Select(StrokeCollection strokes, IListelements, bool raiseSelectionChanged) { // Can't be null. Debug.Assert(strokes != null && elements != null); // // check to see if we're different. If not, there is no need to raise the changed event // bool strokesAreDifferent; bool elementsAreDifferent; int count = 0; SelectionIsDifferentThanCurrent(strokes, out strokesAreDifferent, elements, out elementsAreDifferent); // Besides the differences of both the stroks and the elements, there is a chance that the selected strokes and elements // have been removed from the InkCanvas. Meanwhile, we may still hold the reference of _inkCanvasSelection which is empty. // So, we should make sure to remove _inkCanvasSelection in this case. if ( strokesAreDifferent || elementsAreDifferent ) { if ( strokesAreDifferent && SelectedStrokes.Count != 0 ) { // PERF-2006/05/02-[....], // Unsubscribe the event fisrt so that reseting IsSelected won't call into our handler. // // quit listening to changes // QuitListeningToStrokeChanges(); count = SelectedStrokes.Count; for ( int i = 0; i < count; i++ ) { SelectedStrokes[i].IsSelected = false; } } _selectedStrokes = strokes; _areStrokesChanged = true; _selectedElements = new List (elements); // PERF-2006/05/02-[....], // Update IsSelected property before adding the event handlers. // if ( _inkCanvas.ActiveEditingMode == InkCanvasEditingMode.Select ) { // count = strokes.Count; for ( int i = 0; i < count; i++ ) { strokes[i].IsSelected = true; } } // Add a LayoutUpdated handler if it's necessary. UpdateCanvasLayoutUpdatedHandler(); // Update our selection adorner manually UpdateSelectionAdorner(); ListenToStrokeChanges(); // // And finally... raise the SelectionChanged event // if ( raiseSelectionChanged ) { _inkCanvas.RaiseSelectionChanged(EventArgs.Empty); } } } /// /// Our internal method to commit the current editing. /// Called by EndFeedbackAdorner or InkCanvas.PasteFromDataObject /// internal void CommitChanges(Rect finalRectangle, bool raiseEvent) { // The follow moving or resizing raises SelectionMove or SelectionResize event // The out-side code could throw exception in the their handlers. We use try/finally block to protect our status. Rect selectionBounds = SelectionBounds; // if ( selectionBounds.IsEmpty ) { return; } try { QuitListeningToStrokeChanges( ); if ( raiseEvent ) { // // see if we resized // if ( !DoubleUtil.AreClose(finalRectangle.Height, selectionBounds.Height) || !DoubleUtil.AreClose(finalRectangle.Width, selectionBounds.Width) ) { // // we resized // CommitResizeChange(finalRectangle); } else if ( !DoubleUtil.AreClose(finalRectangle.Top, selectionBounds.Top) || !DoubleUtil.AreClose(finalRectangle.Left, selectionBounds.Left) ) { // // we moved // CommitMoveChange(finalRectangle); } } else { // // Just perform the move without raising any events. // MoveSelection(selectionBounds, finalRectangle); } } finally { ListenToStrokeChanges( ); } } ////// Try to remove an element from the selected elements list. /// Called by: /// OnCanvasLayoutUpdated /// /// internal void RemoveElement(UIElement removedElement) { // No selected element. Bail out if ( _selectedElements == null || _selectedElements.Count == 0 ) { return; } if ( _selectedElements.Remove(removedElement) ) { // The element existed and was removed successfully. // Now if there is no selected element, we should remove our LayoutUpdated handler. if ( _selectedElements.Count == 0 ) { UpdateCanvasLayoutUpdatedHandler(); UpdateSelectionAdorner(); } } } ////// UpdateElementBounds: /// Called by InkCanvasSelection.MoveSelection /// /// /// internal void UpdateElementBounds(UIElement element, Matrix transform) { UpdateElementBounds(element, element, transform); } ////// UpdateElementBounds: /// Called by InkCanvasSelection.UpdateElementBounds /// ClipboardProcessor.CopySelectionInXAML /// /// /// /// internal void UpdateElementBounds(UIElement originalElement, UIElement updatedElement, Matrix transform) { if ( originalElement.DependencyObjectType.Id == updatedElement.DependencyObjectType.Id ) { // Get the transform from element to Canvas GeneralTransform elementToCanvas = originalElement.TransformToAncestor(_inkCanvas.InnerCanvas); //cast to a FrameworkElement, nothing inherits from UIElement besides it right now FrameworkElement frameworkElement = originalElement as FrameworkElement; Size size; Thickness thickness = new Thickness(); if ( frameworkElement == null ) { // Get the element's render size. size = originalElement.RenderSize; } else { size = new Size(frameworkElement.ActualWidth, frameworkElement.ActualHeight); thickness = frameworkElement.Margin; } Rect elementBounds = new Rect(0, 0, size.Width, size.Height); // Rect in element space elementBounds = elementToCanvas.TransformBounds(elementBounds); // Rect in Canvas space // Now apply the matrix to the element bounds Rect newBounds = Rect.Transform(elementBounds, transform); if ( !DoubleUtil.AreClose(elementBounds.Width, newBounds.Width) ) { if ( frameworkElement == null ) { Size newSize = originalElement.RenderSize; newSize.Width = newBounds.Width; updatedElement.RenderSize = newSize; } else { ((FrameworkElement)updatedElement).Width = newBounds.Width; } } if ( !DoubleUtil.AreClose(elementBounds.Height, newBounds.Height) ) { if ( frameworkElement == null ) { Size newSize = originalElement.RenderSize; newSize.Height = newBounds.Height; updatedElement.RenderSize = newSize; } else { ( (FrameworkElement)updatedElement ).Height = newBounds.Height; } } double left = InkCanvas.GetLeft(originalElement); double top = InkCanvas.GetTop(originalElement); double right = InkCanvas.GetRight(originalElement); double bottom = InkCanvas.GetBottom(originalElement); Point originalPosition = new Point(); // Default as (0, 0) if ( !double.IsNaN(left) ) { originalPosition.X = left; } else if ( !double.IsNaN(right) ) { originalPosition.X = right; } if ( !double.IsNaN(top) ) { originalPosition.Y = top; } else if ( !double.IsNaN(bottom) ) { originalPosition.Y = bottom; } Point newPosition = originalPosition * transform; if ( !double.IsNaN(left) ) { InkCanvas.SetLeft(updatedElement, newPosition.X - thickness.Left); // Left wasn't auto } else if ( !double.IsNaN(right) ) { // NOTICE-2005/05/05-[....] // Canvas.RightProperty means the distance between element right side and its parent Canvas // right side. The same definition is applied to Canvas.BottomProperty InkCanvas.SetRight(updatedElement, ( right - ( newPosition.X - originalPosition.X ) )); // Right wasn't not auto } else { InkCanvas.SetLeft(updatedElement, newPosition.X - thickness.Left); // Both Left and Right were aut. Modify Left by default. } if ( !double.IsNaN(top) ) { InkCanvas.SetTop(updatedElement, newPosition.Y - thickness.Top); // Top wasn't auto } else if ( !double.IsNaN(bottom) ) { InkCanvas.SetBottom(updatedElement, ( bottom - ( newPosition.Y - originalPosition.Y ) )); // Bottom wasn't not auto } else { InkCanvas.SetTop(updatedElement, newPosition.Y - thickness.Top); // Both Top and Bottom were aut. Modify Left by default. } } else { Debug.Assert(false, "The updatedElement has to be the same type as the originalElement."); } } internal void TransformStrokes(StrokeCollection strokes, Matrix matrix) { strokes.Transform(matrix, false /*Don't apply the transform to StylusTip*/); } internal InkCanvasSelectionHitResult HitTestSelection(Point pointOnInkCanvas) { // If Selection is moving or resizing now, we should returns the active hit result. if ( _activeSelectionHitResult.HasValue ) { return _activeSelectionHitResult.Value; } // No selection at all. Just return None. if ( !HasSelection ) { return InkCanvasSelectionHitResult.None; } // Get the transform from InkCanvas to SelectionAdorner GeneralTransform inkCanvasToSelectionAdorner = _inkCanvas.TransformToDescendant(_inkCanvas.SelectionAdorner); Point pointOnSelectionAdorner = inkCanvasToSelectionAdorner.Transform(pointOnInkCanvas); InkCanvasSelectionHitResult hitResult = _inkCanvas.SelectionAdorner.SelectionHandleHitTest(pointOnSelectionAdorner); // If the hit test returns Selection and there is one and only one element is selected. // We have to check whether we hit on inside the element. if ( hitResult == InkCanvasSelectionHitResult.Selection && SelectedElements.Count == 1 && SelectedStrokes.Count == 0 ) { GeneralTransform transformToInnerCanvas = _inkCanvas.TransformToDescendant(_inkCanvas.InnerCanvas); Point pointOnInnerCanvas = transformToInnerCanvas.Transform(pointOnInkCanvas); // Try to find out whether we hit the single selement. If so, just return None. if ( HasHitSingleSelectedElement(pointOnInnerCanvas) ) { hitResult = InkCanvasSelectionHitResult.None; } } return hitResult; } ////// Private helper to used to determine if the current selection /// is different than the selection passed /// /// strokes /// strokesAreDifferent /// elements /// elementsAreDifferent internal void SelectionIsDifferentThanCurrent(StrokeCollection strokes, out bool strokesAreDifferent, IListelements, out bool elementsAreDifferent) { strokesAreDifferent = false; elementsAreDifferent = false; if ( SelectedStrokes.Count == 0 ) { if ( strokes.Count > 0 ) { strokesAreDifferent = true; } } else if ( !InkCanvasSelection.StrokesAreEqual(SelectedStrokes, strokes) ) { strokesAreDifferent = true; } if ( SelectedElements.Count == 0 ) { if ( elements.Count > 0 ) { elementsAreDifferent = true; } } else if ( !InkCanvasSelection.FrameworkElementArraysAreEqual(elements, SelectedElements) ) { elementsAreDifferent = true; } } #endregion Internal Methods //------------------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------------------- #region Private Methods /// /// HasHitSingleSelectedElement /// This method hits test against the current selection. If the point is inside the single selected element, /// it will return true. Otherwise, return false. /// /// ///private bool HasHitSingleSelectedElement(Point pointOnInnerCanvas) { bool hasHit = false; if ( SelectedElements.Count == 1 ) { IEnumerator enumerator = SelectedElementsBoundsEnumerator.GetEnumerator(); if ( enumerator.MoveNext() ) { Rect elementRect = enumerator.Current; hasHit = elementRect.Contains(pointOnInnerCanvas); } else { Debug.Assert(false, "An unexpected single selected Element"); } } return hasHit; } /// /// Private helper to stop listening to stroke changes /// private void QuitListeningToStrokeChanges() { if ( _inkCanvas.Strokes != null ) { _inkCanvas.Strokes.StrokesChanged -= new StrokeCollectionChangedEventHandler(this.OnStrokeCollectionChanged); } foreach ( Stroke s in SelectedStrokes ) { s.Invalidated -= new EventHandler(this.OnStrokeInvalidated); } } ////// Private helper to listen to stroke changes /// private void ListenToStrokeChanges() { if ( _inkCanvas.Strokes != null ) { _inkCanvas.Strokes.StrokesChanged += new StrokeCollectionChangedEventHandler(this.OnStrokeCollectionChanged); } foreach ( Stroke s in SelectedStrokes ) { s.Invalidated += new EventHandler(this.OnStrokeInvalidated); } } ////// Helper method that take a finalRectangle and raised the appropriate /// events on the InkCanvas. Handles cancellation /// private void CommitMoveChange(Rect finalRectangle) { Rect selectionBounds = SelectionBounds; // //*** MOVED *** // InkCanvasSelectionEditingEventArgs args = new InkCanvasSelectionEditingEventArgs(selectionBounds, finalRectangle); _inkCanvas.RaiseSelectionMoving(args); if ( !args.Cancel ) { if ( finalRectangle != args.NewRectangle ) { finalRectangle = args.NewRectangle; } // // perform the move // MoveSelection(selectionBounds, finalRectangle); // // raise the 'changed' event // _inkCanvas.RaiseSelectionMoved(EventArgs.Empty); } } ////// Helper method that take a finalRectangle and raised the appropriate /// events on the InkCanvas. Handles cancellation /// private void CommitResizeChange(Rect finalRectangle) { Rect selectionBounds = SelectionBounds; // // *** RESIZED *** // InkCanvasSelectionEditingEventArgs args = new InkCanvasSelectionEditingEventArgs(selectionBounds, finalRectangle); _inkCanvas.RaiseSelectionResizing(args); if ( !args.Cancel ) { if ( finalRectangle != args.NewRectangle ) { finalRectangle = args.NewRectangle; } // // perform the move // MoveSelection(selectionBounds, finalRectangle); // // raise the 'changed' event // _inkCanvas.RaiseSelectionResized(EventArgs.Empty); } } ////// Private helper that moves all selected elements from the previous location /// to the new one /// /// /// private void MoveSelection(Rect previousRect, Rect newRect) { // // compute our transform, but first remove our artificial border // Matrix matrix = InkCanvasSelection.MapRectToRect(newRect, previousRect); // // transform the elements // int count = SelectedElements.Count; IListelements = SelectedElements; for ( int i = 0; i < count ; i++ ) { UpdateElementBounds(elements[i], matrix); } // // transform the strokes // if ( SelectedStrokes.Count > 0 ) { TransformStrokes(SelectedStrokes, matrix); // Flag the change _areStrokesChanged = true; } if ( SelectedElements.Count == 0 ) { // If there is no element in the current selection, the inner canvas won't have layout changes. // So there is no LayoutUpdated event. We have to manually update the selection bounds. UpdateSelectionAdorner(); } // _inkCanvas.BringIntoView(newRect); } /// /// A handler which receives LayoutUpdated from the layout manager. Since the selected elements can be animated, /// moved, resized or even removed programmatically from the InkCanvas, we have to monitor /// the any layout changes caused by those actions. /// /// /// private void OnCanvasLayoutUpdated(object sender, EventArgs e) { Debug.Assert( SelectedElements.Count != 0, "The handler only can be hooked up when there are elements being selected" ); // Update the selection adorner when receive the layout changed UpdateSelectionAdorner(); } ////// Our own internal listener for packet changes /// private void OnStrokeInvalidated(object sender, EventArgs e) { OnStrokeCollectionChanged(sender, new StrokeCollectionChangedEventArgs(new StrokeCollection(), new StrokeCollection())); } ////// Our own internal listener for strokes changed. /// This is used so that if someone deletes or modifies a stroke /// we are currently displaying for selection, we can update our size /// private void OnStrokeCollectionChanged(object target, StrokeCollectionChangedEventArgs e) { // If the strokes only get added to the InkCanvas, we don't have to update our internal selected strokes. if ( e.Added.Count != 0 && e.Removed.Count == 0 ) { return; } foreach (Stroke s in e.Removed) { if ( SelectedStrokes.Contains(s) ) { s.Invalidated -= new EventHandler(this.OnStrokeInvalidated); s.IsSelected = false; // Now remove the stroke from our private collection. SelectedStrokes.Remove(s); } } // Mark the strokes change _areStrokesChanged = true; UpdateSelectionAdorner(); } ////// Get the cached bounding boxes to be updated. /// private Rect GetStrokesBounds() { // Update the strokes bounds if they are changed. if ( _areStrokesChanged ) { _cachedStrokesBounds = SelectedStrokes.Count != 0 ? SelectedStrokes.GetBounds( ) : Rect.Empty; _areStrokesChanged = false; } return _cachedStrokesBounds; } ////// Get the bounding boxes of the selected elements. /// ///private List GetElementsBounds() { List rects = new List (); if ( SelectedElements.Count != 0 ) { // The private SelectedElementsBounds property will ensure the size is got after rendering's done. foreach ( Rect elementRect in SelectedElementsBoundsEnumerator ) { rects.Add(elementRect); } } return rects; } /// /// Get the union box of the selected elements. /// ///private Rect GetElementsUnionBounds() { if ( SelectedElements.Count == 0 ) { return Rect.Empty; } Rect elementsBounds = Rect.Empty; // The private SelectedElementsBounds property will ensure the size is got after rendering's done. foreach ( Rect elementRect in SelectedElementsBoundsEnumerator ) { elementsBounds.Union(elementRect); } return elementsBounds; } /// /// Update the selection adorners state. /// The method is called by: /// MoveSelection /// OnCanvasLayoutUpdated /// OnStrokeCollectionChanged /// RemoveElement /// Select /// private void UpdateSelectionAdorner() { // The Selection Adorner will be visible all the time unless the ActiveEditingMode is None. if ( _inkCanvas.ActiveEditingMode != InkCanvasEditingMode.None ) { Debug.Assert(_inkCanvas.SelectionAdorner.Visibility == Visibility.Visible, "SelectionAdorner should be always visible except under the None ActiveEditingMode"); _inkCanvas.SelectionAdorner.UpdateSelectionWireFrame(GetStrokesBounds(), GetElementsBounds()); } else { Debug.Assert(_inkCanvas.SelectionAdorner.Visibility == Visibility.Collapsed, "SelectionAdorner should be collapsed except if the ActiveEditingMode is None"); } } ////// Ensure the rendering is valid. /// Called by: /// SelectedElementsBounds /// private void EnusreElementsBounds() { InkCanvasInnerCanvas innerCanvas = _inkCanvas.InnerCanvas; // Make sure the arrange is valid. If not, we invoke UpdateLayout now. if ( !innerCanvas.IsMeasureValid || !innerCanvas.IsArrangeValid ) { innerCanvas.UpdateLayout(); } } ////// Utility for computing a transformation that maps one rect to another /// /// Destination Rectangle /// Source Rectangle ///Transform that maps source rectangle to destination rectangle private static Matrix MapRectToRect(Rect target, Rect source) { if(source.IsEmpty) throw new ArgumentOutOfRangeException("source", SR.Get(SRID.InvalidDiameter)); /* In the horizontal direction: M11*source.left + Dx = target.left M11*source.right + Dx = target.right Subtracting the equations yields: M11*(source.right - source.left) = target.right - target.left hence: M11 = (target.right - target.left) / (source.right - source.left) Having computed M11, solve the first equation for Dx: Dx = target.left - M11*source.left */ double m11 = target.Width / source.Width; double dx = target.Left - m11 * source.Left; // Now do the same thing in the vertical direction double m22 = target.Height / source.Height; double dy = target.Top - m22 * source.Top; /* We are looking for the transformation that takes one upright rectangle to another. This involves neither rotation nor shear, so: */ return new Matrix(m11, 0, 0, m22, dx, dy); } ////// Adds/Removes the LayoutUpdated handler according to whether there is selected elements or not. /// private void UpdateCanvasLayoutUpdatedHandler() { // If there are selected elements, we should create a LayoutUpdated handler in order to monitor their arrange changes. // Otherwise, we don't have to listen the LayoutUpdated if there is no selected element at all. if ( SelectedElements.Count != 0 ) { // Make sure we hook up the event handler. if ( _layoutUpdatedHandler == null ) { _layoutUpdatedHandler = new EventHandler(OnCanvasLayoutUpdated); _inkCanvas.InnerCanvas.LayoutUpdated += _layoutUpdatedHandler; } } else { // Remove the handler if there is one. if ( _layoutUpdatedHandler != null ) { _inkCanvas.InnerCanvas.LayoutUpdated -= _layoutUpdatedHandler; _layoutUpdatedHandler = null; } } } ////// Private helper method used to determine if two stroke collection either /// 1) are both null /// or /// 2) both refer to the identical set of strokes /// private static bool StrokesAreEqual(StrokeCollection strokes1, StrokeCollection strokes2) { if ( strokes1 == null && strokes2 == null ) { return true; } // // we know that one of the stroke collections is // not null. If the other one is, they're not // equal // if ( strokes1 == null || strokes2 == null ) { return false; } if ( strokes1.Count != strokes2.Count ) { return false; } foreach ( Stroke s in strokes1 ) { if ( !strokes2.Contains(s) ) { return false; } } return true; } ////// Private helper method used to determine if two collection either /// 1) are both null /// or /// 2) both refer to the identical set of elements /// private static bool FrameworkElementArraysAreEqual(IListelements1, IList elements2) { if ( elements1 == null && elements2 == null ) { return true; } // // we know that one of the stroke collections is // not null. If the other one is, they're not // equal // if ( elements1 == null || elements2 == null ) { return false; } if ( elements1.Count != elements2.Count ) { return false; } foreach ( UIElement e in elements1 ) { if ( !elements2.Contains(e) ) { return false; } } return true; } #endregion Private Methods //------------------------------------------------------------------------------- // // Private Properties // //------------------------------------------------------------------------------- #region Private Properties /// /// Iterates the bounding boxes of the selected elements relevent to their parent (InnerCanvas) /// private IEnumerableSelectedElementsBoundsEnumerator { get { // Ensure the elements have been rendered completely. EnusreElementsBounds(); InkCanvasInnerCanvas innerCanvas = _inkCanvas.InnerCanvas; foreach ( UIElement element in SelectedElements ) { // Get the transform from element to Canvas GeneralTransform elementToCanvas = element.TransformToAncestor(innerCanvas); // Get the element's render size. Size size = element.RenderSize; Rect rect = new Rect(0, 0, size.Width, size.Height); // Rect in element space rect = elementToCanvas.TransformBounds(rect); // Rect in Canvas space yield return rect; } } } #endregion Private Properties //------------------------------------------------------------------------------- // // Private Fields // //-------------------------------------------------------------------------------- #region Private Fields /// /// Our logical parent. /// private InkCanvas _inkCanvas; ////// The collection of strokes this selectedinkelement represents /// private StrokeCollection _selectedStrokes; ////// The last known rectangle before the current edit. used to revert /// when events are cancelled and expose the value to the InkCanvas.GetSelectionBounds /// private Rect _cachedStrokesBounds; private bool _areStrokesChanged; ////// The collection of elements this selectedinkelement represents /// private List_selectedElements; private EventHandler _layoutUpdatedHandler; private Nullable _activeSelectionHitResult; #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
- NetworkStream.cs
- XPathNavigatorKeyComparer.cs
- DrawingContextDrawingContextWalker.cs
- EmptyCollection.cs
- ObjectAnimationUsingKeyFrames.cs
- NativeStructs.cs
- NestedContainer.cs
- OrderPreservingMergeHelper.cs
- OracleSqlParser.cs
- Exceptions.cs
- SiteMapNodeCollection.cs
- BackgroundWorker.cs
- SmtpFailedRecipientException.cs
- SpellerHighlightLayer.cs
- AssemblyUtil.cs
- ListViewAutomationPeer.cs
- OleDbMetaDataFactory.cs
- DataGridToolTip.cs
- XmlAttributes.cs
- JsonUriDataContract.cs
- DescendantBaseQuery.cs
- ComplexLine.cs
- ZipIOExtraField.cs
- CallTemplateAction.cs
- PlainXmlDeserializer.cs
- XmlDictionaryWriter.cs
- MonitoringDescriptionAttribute.cs
- DefaultPropertyAttribute.cs
- EnumUnknown.cs
- ProcessInputEventArgs.cs
- SoapSchemaExporter.cs
- HostingEnvironment.cs
- ReceiveActivity.cs
- HttpCapabilitiesEvaluator.cs
- ObjectAnimationBase.cs
- ToolStripTextBox.cs
- QueryOutputWriterV1.cs
- WebPartZoneCollection.cs
- OdbcInfoMessageEvent.cs
- ColumnHeader.cs
- TreeNodeStyleCollection.cs
- FieldTemplateFactory.cs
- XmlNotation.cs
- FactoryMaker.cs
- ProcessProtocolHandler.cs
- HttpRequestCacheValidator.cs
- XDeferredAxisSource.cs
- ListViewItemMouseHoverEvent.cs
- OleAutBinder.cs
- TrackingStringDictionary.cs
- EmptyQuery.cs
- ExtenderProvidedPropertyAttribute.cs
- Crypto.cs
- EventDescriptorCollection.cs
- DatePickerDateValidationErrorEventArgs.cs
- ManifestResourceInfo.cs
- FixUpCollection.cs
- Span.cs
- DependencyObjectPropertyDescriptor.cs
- ZipIOZip64EndOfCentralDirectoryBlock.cs
- XmlHierarchicalDataSourceView.cs
- ItemType.cs
- LineUtil.cs
- TypeDependencyAttribute.cs
- BindingCollection.cs
- FormsAuthenticationCredentials.cs
- ExceptionHandlers.cs
- DbConnectionPool.cs
- ServiceContractDetailViewControl.cs
- RuleInfoComparer.cs
- DropShadowBitmapEffect.cs
- FormViewInsertEventArgs.cs
- SizeValueSerializer.cs
- MatrixTransform3D.cs
- NegotiateStream.cs
- ShapingWorkspace.cs
- Byte.cs
- SynthesizerStateChangedEventArgs.cs
- GridViewRow.cs
- GridItem.cs
- ObjectItemNoOpAssemblyLoader.cs
- SqlDuplicator.cs
- ActivityTrace.cs
- CodeArgumentReferenceExpression.cs
- WindowsStatusBar.cs
- RedirectionProxy.cs
- ScriptHandlerFactory.cs
- MonthCalendar.cs
- WebPartCancelEventArgs.cs
- ControlLocalizer.cs
- SimpleTableProvider.cs
- SectionRecord.cs
- WpfKnownMemberInvoker.cs
- RuntimeCompatibilityAttribute.cs
- ItemCheckEvent.cs
- SoapServerMethod.cs
- VariantWrapper.cs
- CheckoutException.cs
- CookieParameter.cs
- Claim.cs