1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
5 #pragma warning disable 618
7 namespace System.Activities.Presentation
9 using System.Activities.Presentation.Model;
10 using System.Activities.Presentation.Services;
11 using System.Activities.Presentation.View;
12 using System.Collections.Generic;
13 using System.ComponentModel;
14 using System.Diagnostics.CodeAnalysis;
15 using System.Globalization;
18 using System.Windows.Automation.Peers;
19 using System.Windows.Controls;
20 using System.Windows.Input;
21 using System.Windows.Media;
22 using System.Windows.Controls.Primitives;
23 using System.Windows.Data;
25 using System.Windows.Threading;
28 using System.Activities.Presentation.Internal.PropertyEditing;
30 // This is the base class of all things visual that are associated with ModelItems.
31 // e.g state designer, workflowelement designer, activity designe etc.
32 // This provides access to the ModelItem attached to it, and the EditingContext.
33 public class WorkflowViewElement : ContentControl, ICompositeViewEvents
35 public static readonly DependencyProperty ModelItemProperty =
36 DependencyProperty.Register("ModelItem", typeof(ModelItem), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(WorkflowViewElement.OnModelItemChanged)));
37 public static readonly DependencyProperty ContextProperty =
38 DependencyProperty.Register("Context", typeof(EditingContext), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(WorkflowViewElement.OnContextChanged)));
39 public static readonly DependencyProperty ExpandStateProperty =
40 DependencyProperty.Register("ExpandState", typeof(bool), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(WorkflowViewElement.OnExpandStateChanged)));
41 public static readonly DependencyProperty PinStateProperty =
42 DependencyProperty.Register("PinState", typeof(bool), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(WorkflowViewElement.OnPinStateChanged)));
43 public static readonly DependencyProperty ShowExpandedProperty =
44 DependencyProperty.Register("ShowExpanded", typeof(bool), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(WorkflowViewElement.OnShowExpandedChanged)));
45 internal readonly static DependencyProperty IsRootDesignerProperty =
46 DependencyProperty.Register("IsRootDesigner", typeof(bool), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(false));
47 static readonly DependencyProperty IsReadOnlyProperty =
48 DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(WorkflowViewElement.OnReadOnlyChanged)));
50 const string ExpandViewStateKey = "IsExpanded";
51 internal const string PinnedViewStateKey = "IsPinned";
52 Timer breadCrumbTimer;
53 int lastMouseButtonDownTimeStamp;
55 internal string CustomItemStatus { get; set; }
57 static void OnExpandStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
59 WorkflowViewElement viewElement = obj as WorkflowViewElement;
60 viewElement.OnExpandStateChanged((bool)e.NewValue);
63 static void OnPinStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
65 WorkflowViewElement viewElement = obj as WorkflowViewElement;
66 viewElement.OnPinStateChanged((bool)e.NewValue);
70 static void OnContextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
72 WorkflowViewElement viewElement = obj as WorkflowViewElement;
73 viewElement.OnContextChanged();
76 static void OnShowExpandedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
78 WorkflowViewElement viewElement = obj as WorkflowViewElement;
79 viewElement.OnShowExpandedChanged((bool)e.NewValue);
82 static void OnReadOnlyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
84 WorkflowViewElement viewElement = obj as WorkflowViewElement;
85 viewElement.OnReadOnlyChanged((bool)e.NewValue);
88 protected virtual void OnShowExpandedChanged(bool newValue)
92 protected virtual void OnReadOnlyChanged(bool isReadOnly)
96 internal void ForceCollapse()
98 this.ExpandState = false;
99 if (this.Designer.ShouldExpandAll)
101 this.PinState = true;
105 void OnContextChanged()
107 //Setting the binding here so that we have a handle to DesignerView.
108 SetShowExpandedBindings();
111 void OnExpandStateChanged(bool newValue)
113 if (this.ModelItem != null && this.Context != null)
115 this.ViewStateService.StoreViewState(this.ModelItem, ExpandViewStateKey, newValue);
119 void OnPinStateChanged(bool newValue)
121 if (this.ModelItem != null && this.Context != null)
123 this.ViewStateService.StoreViewState(this.ModelItem, WorkflowViewElement.PinnedViewStateKey, newValue);
127 bool leftMouseButtonDown = false;
128 Point lastMouseDownPoint;
129 UIElement lastActivationElement;
130 List<ICompositeView> compositeViews;
131 ICompositeView defaultCompositeView;
132 bool rightMouseClickWithCtrlDown = false;
133 bool rightMouseClick = false;
134 bool shouldChangeSelectionOnMouseUp = false;
136 public WorkflowViewElement()
138 this.Collapsible = true;
139 this.IsAncestorOfRootDesigner = false;
140 this.DragHandle = this;
142 this.Loaded += (sender, eventArgs) =>
144 // When the designer is loaded in Cider, the Context is not available and thus we cannot access the DesignerView (nor is it necessary)
145 if (this.Context != null)
147 this.Designer.RegisterViewElement(this);
150 this.GotFocus += new RoutedEventHandler(OnGotFocusEvent);
151 this.breadCrumbTimer = new Timer(2000);
152 this.breadCrumbTimer.Elapsed += new ElapsedEventHandler(OnBreadCrumbTimerElapsed);
153 this.breadCrumbTimer.AutoReset = false;
154 this.lastActivationElement = null;
155 this.SetValue(CutCopyPasteHelper.ChildContainersProperty, null);
156 if (this.ModelItem != null)
158 ((IModelTreeItem)this.ModelItem).SetCurrentView(this);
161 //Get the ExpandState from ViewState.
162 if (this.ModelItem != null)
164 object expandCollapseViewState = this.ViewStateService.RetrieveViewState(this.ModelItem, ExpandViewStateKey);
165 object pinViewState = this.ViewStateService.RetrieveViewState(this.ModelItem, WorkflowViewElement.PinnedViewStateKey);
166 if (expandCollapseViewState != null)
168 this.ExpandState = (bool)expandCollapseViewState;
170 if (pinViewState != null)
172 this.PinState = (bool)pinViewState;
175 this.UseLayoutRounding = true;
178 this.Unloaded += (sender, eventArgs) =>
180 // When the designer is loaded in Cider, the Context is not available and thus we cannot access the DesignerView (nor is it necessary)
181 if (this.Context != null)
183 this.Designer.UnregisterViewElement(this);
186 this.GotFocus -= new RoutedEventHandler(OnGotFocusEvent);
187 Fx.Assert(this.breadCrumbTimer != null, "The timer should not be null.");
188 this.breadCrumbTimer.Elapsed -= new ElapsedEventHandler(OnBreadCrumbTimerElapsed);
189 this.breadCrumbTimer.Close();
193 protected override void OnInitialized(EventArgs e)
195 base.OnInitialized(e);
197 Binding readOnlyBinding = new Binding();
198 readOnlyBinding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DesignerView), 1);
199 readOnlyBinding.Path = new PropertyPath(DesignerView.IsReadOnlyProperty);
200 readOnlyBinding.Mode = BindingMode.OneWay;
201 this.SetBinding(IsReadOnlyProperty, readOnlyBinding);
204 void SetShowExpandedBindings()
206 MultiBinding multiBinding = new MultiBinding();
208 Binding modelItemBinding = new Binding();
209 modelItemBinding.Source = this;
210 modelItemBinding.Path = new PropertyPath(WorkflowViewElement.ModelItemProperty);
211 //Bind to IsRootDesigner
212 Binding isRootDesignerBinding = new Binding();
213 isRootDesignerBinding.Source = this;
214 isRootDesignerBinding.Path = new PropertyPath(WorkflowViewElement.IsRootDesignerProperty);
215 //Bind to DesignerView.ExpandAll
216 Binding expandAllBinding = new Binding();
217 DesignerView view = this.Context.Services.GetService<DesignerView>();
218 expandAllBinding.Source = view;
219 expandAllBinding.Path = new PropertyPath(DesignerView.ShouldExpandAllProperty);
220 //Bind to DesignerView.CollapseAll
221 Binding collapseAllBinding = new Binding();
222 collapseAllBinding.Source = view;
223 collapseAllBinding.Path = new PropertyPath(DesignerView.ShouldCollapseAllProperty);
224 //Bind to ExpandState
225 Binding expandStateBinding = new Binding();
226 expandStateBinding.Source = this;
227 expandStateBinding.Path = new PropertyPath(WorkflowViewElement.ExpandStateProperty);
229 Binding pinStateBinding = new Binding();
230 pinStateBinding.Source = this;
231 pinStateBinding.Path = new PropertyPath(WorkflowViewElement.PinStateProperty);
233 Binding selfBinding = new Binding();
234 selfBinding.Source = this;
235 //Bind to container (to recalculate on drag-drop.)
236 Binding containerBinding = new Binding();
237 containerBinding.Source = this;
238 containerBinding.Path = new PropertyPath(DragDropHelper.DragSourceProperty);
239 multiBinding.Bindings.Add(modelItemBinding);
240 multiBinding.Bindings.Add(isRootDesignerBinding);
241 multiBinding.Bindings.Add(expandAllBinding);
242 multiBinding.Bindings.Add(collapseAllBinding);
243 multiBinding.Bindings.Add(expandStateBinding);
244 multiBinding.Bindings.Add(pinStateBinding);
245 multiBinding.Bindings.Add(selfBinding);
246 multiBinding.Bindings.Add(containerBinding);
248 multiBinding.Mode = BindingMode.OneWay;
249 multiBinding.Converter = new ShowExpandedMultiValueConverter();
250 BindingOperations.SetBinding(this, WorkflowViewElement.ShowExpandedProperty, multiBinding );
253 [Fx.Tag.KnownXamlExternal]
254 public EditingContext Context
256 get { return (EditingContext)GetValue(ContextProperty); }
257 set { SetValue(ContextProperty, value); }
260 public bool ExpandState
262 get { return (bool)GetValue(ExpandStateProperty); }
263 set { SetValue(ExpandStateProperty, value); }
268 get { return (bool)GetValue(PinStateProperty); }
269 set { SetValue(PinStateProperty, value); }
272 //This guides us whether to show the expand collapse button for this ViewElement or not.
273 public bool Collapsible
279 public bool IsRootDesigner
281 get { return (bool)GetValue(IsRootDesignerProperty); }
282 internal set { SetValue(IsRootDesignerProperty, value); }
285 internal bool IsAncestorOfRootDesigner
291 public bool ShowExpanded
293 get { return (bool)GetValue(ShowExpandedProperty); }
296 internal bool DoesParentAlwaysExpandChild()
298 return ViewUtilities.DoesParentAlwaysExpandChildren(this.ModelItem, this.Context);
301 internal bool DoesParentAlwaysCollapseChildren()
303 return ViewUtilities.DoesParentAlwaysCollapseChildren(this.ModelItem, this.Context);
307 [Fx.Tag.KnownXamlExternal]
308 public ModelItem ModelItem
310 get { return (ModelItem)GetValue(ModelItemProperty); }
311 set { SetValue(ModelItemProperty, value); }
314 public FrameworkElement DragHandle
320 protected bool IsReadOnly
322 get { return (bool)GetValue(IsReadOnlyProperty); }
323 private set { SetValue(IsReadOnlyProperty, value); }
326 internal ICompositeView ActiveCompositeView
330 if (!this.ShowExpanded)
334 ICompositeView activeContainer = null;
335 if (null != this.compositeViews && null != this.lastActivationElement)
337 activeContainer = this.compositeViews.Find(p =>
339 Visual visual = p as Visual;
340 return (null != visual &&
341 visual == this.lastActivationElement.FindCommonVisualAncestor(visual));
344 activeContainer = activeContainer ?? this.DefaultCompositeView;
346 System.Diagnostics.Debug.WriteLine(string.Format(
347 CultureInfo.InvariantCulture,
348 "Active ICompositeView in '{0}' is '{1}'",
349 this.GetType().Name, activeContainer == null ? "<null>" : activeContainer.GetHashCode().ToString(CultureInfo.InvariantCulture)));
351 return activeContainer;
355 // useful shortcuts for things we know we will be using a lot.
356 // Shortcut to viewservice in editingcontext.services
357 protected internal ViewService ViewService
361 Fx.Assert(this.Context != null, "Context should not be null ");
362 ViewService viewService = this.Context.Services.GetService<ViewService>();
363 Fx.Assert(viewService != null, "View service should never be null if we are in a valid view tree");
368 // Shortcut to ViewStateService in editingcontext.services
369 protected internal ViewStateService ViewStateService
373 ViewStateService viewStateService = this.Context.Services.GetService<ViewStateService>();
374 Fx.Assert(viewStateService != null, "ViewState service should never be null if we are in a valid view tree");
375 return viewStateService;
379 protected internal DesignerView Designer
383 DesignerView designer = this.Context.Services.GetService<DesignerView>();
384 Fx.Assert(designer != null, "DesignerView service should never be null if we are in a valid state");
389 protected IList<ICompositeView> CompositeViews
391 get { return this.compositeViews; }
394 protected ICompositeView DefaultCompositeView
396 get { return this.defaultCompositeView; }
399 internal bool DraggingMultipleItemsEnabled
401 get { return this.Context.Services.GetService<DesignerConfigurationService>().MultipleItemsDragDropEnabled; }
404 protected virtual string GetAutomationIdMemberName()
409 protected virtual string GetAutomationHelpText()
414 protected internal virtual string GetAutomationItemStatus()
416 if (this.CustomItemStatus == null)
422 if (!this.CustomItemStatus.EndsWith(" ", StringComparison.Ordinal))
424 this.CustomItemStatus += " ";
426 return this.CustomItemStatus;
430 protected override AutomationPeer OnCreateAutomationPeer()
432 return new WorkflowViewElementAutomationPeer(this);
435 protected internal virtual void OnEditAnnotation()
440 public void RegisterDefaultCompositeView(ICompositeView container)
442 if (null == container)
444 throw FxTrace.Exception.AsError(new ArgumentNullException("container"));
447 this.defaultCompositeView = container;
449 System.Diagnostics.Debug.WriteLine(string.Format(
450 CultureInfo.InvariantCulture,
451 "Default ICompositeView of type '{0}' for '{1}' loaded. hashcode = {2}",
452 this.defaultCompositeView.GetType().Name, this.GetType().Name, this.defaultCompositeView.GetHashCode()));
455 public void UnregisterDefaultCompositeView(ICompositeView container)
457 if (null == container)
459 throw FxTrace.Exception.AsError(new ArgumentNullException("container"));
462 if (object.Equals(this.defaultCompositeView, container))
464 this.defaultCompositeView = null;
468 public void RegisterCompositeView(ICompositeView container)
470 if (null == container)
472 throw FxTrace.Exception.AsError(new ArgumentNullException("container"));
475 if (null == this.CompositeViews)
477 this.compositeViews = new List<ICompositeView>();
479 if (!this.compositeViews.Contains(container))
481 System.Diagnostics.Debug.WriteLine(string.Format(
482 CultureInfo.InvariantCulture,
483 "ICompositeView of type '{0}' for '{1}' loaded. hashcode = {2}",
484 container.GetType().Name, this.GetType().Name, container.GetHashCode()));
486 this.compositeViews.Add(container);
490 public void UnregisterCompositeView(ICompositeView container)
492 if (null == container)
494 throw FxTrace.Exception.AsError(new ArgumentNullException("container"));
497 if (null != this.compositeViews && this.compositeViews.Contains(container))
499 System.Diagnostics.Debug.WriteLine(string.Format(
500 CultureInfo.InvariantCulture,
501 "ICompositeView of type '{0}' for '{1}' unloaded",
502 container.GetType().Name, this.GetType().Name));
504 this.compositeViews.Remove(container);
505 if (0 == this.compositeViews.Count)
507 this.compositeViews = null;
512 void OnGotFocusEvent(object sender, RoutedEventArgs e)
514 DesignerView designerView = this.Context.Services.GetService<DesignerView>();
515 if (!e.Handled && this.ModelItem != null && !designerView.IsMultipleSelectionMode)
517 Selection selection = this.Context.Items.GetValue<Selection>();
518 //update selection when following conditions apply:
519 //1. We're not trying to open context menu using right click + ctrl key.
520 //2. We're not clicking with left mouse - selection will be updated on left mouse button up
521 //3. Current selection does not contain this.ModelItem if it's mouse right click
522 bool becomesSelection = true;
523 if (this.rightMouseClickWithCtrlDown || this.leftMouseButtonDown)
525 becomesSelection = false;
527 else if (this.rightMouseClick)
529 foreach (ModelItem item in selection.SelectedObjects)
531 if (item == this.ModelItem)
533 becomesSelection = false;
539 //When there is only one selected model item, we want to change the selection when we tab into other items.
540 if (becomesSelection)
542 Selection.SelectOnly(this.Context, this.ModelItem);
545 System.Diagnostics.Debug.WriteLine(
546 string.Format(CultureInfo.InvariantCulture, "{0} ) WorkflowViewElement.OnGotFocusEvent ({1}, selection: {2}, becomesSelection {3}, raisedBy {4})",
547 DateTime.Now.ToLocalTime(), this.GetType().Name, selection.SelectionCount, becomesSelection, e.OriginalSource));
549 //do not override last activation element if we get a reference to this (this will be passed as original source
550 //whenever focus is set manualy - by direct call to Keyboard.SetFocus)
551 if (!object.Equals(this, e.OriginalSource))
553 this.lastActivationElement = e.OriginalSource as UIElement;
559 this.lastActivationElement = null;
563 protected virtual void OnModelItemChanged(object newItem)
567 protected virtual void OnContextMenuLoaded(ContextMenu menu)
571 void MakeRootDesigner()
573 DesignerView designerView = this.Context.Services.GetService<DesignerView>();
574 if (!this.Equals(designerView.RootDesigner))
576 designerView.MakeRootDesigner(this.ModelItem);
577 this.leftMouseButtonDown = false;
581 protected override void OnMouseDown(MouseButtonEventArgs e)
583 this.lastMouseButtonDownTimeStamp = e.Timestamp;
584 bool shouldSetFocus = false;
585 bool shouldUpdateLastActivationPoint = false;
586 this.leftMouseButtonDown = false;
587 this.rightMouseClickWithCtrlDown = false;
588 this.rightMouseClick = false;
589 bool shouldToggle = false;
591 if (this.ModelItem == null)
596 if (e.LeftButton == MouseButtonState.Pressed)
598 this.leftMouseButtonDown = true;
599 this.lastMouseDownPoint = e.GetPosition(this);
600 if (!Keyboard.IsKeyDown(Key.LeftCtrl) && !Keyboard.IsKeyDown(Key.RightCtrl))
609 shouldSetFocus = Keyboard.FocusedElement != this;
611 this.Designer.ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled = true;
612 shouldUpdateLastActivationPoint = true;
615 if (e.LeftButton == MouseButtonState.Pressed && e.ClickCount == 2 && this.Designer.lastClickedDesigner == this)
617 this.MakeRootDesigner();
620 this.Designer.ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled = false;
623 if (e.RightButton == MouseButtonState.Pressed)
625 rightMouseClick = true;
626 this.lastMouseDownPoint = e.GetPosition(this);
627 if (!Keyboard.IsKeyDown(Key.LeftCtrl) && !Keyboard.IsKeyDown(Key.RightCtrl))
629 shouldSetFocus = Keyboard.FocusedElement != this;
633 rightMouseClickWithCtrlDown = true;
637 shouldUpdateLastActivationPoint = true;
640 System.Diagnostics.Debug.WriteLine(
641 string.Format(CultureInfo.InvariantCulture, "{0} ) WorkflowViewElement.OnMouseDown ({1}, shouldSetFocus {2}, mouseCaptured {3}, raisedBy {4})",
642 DateTime.Now.ToLocalTime(), this.GetType().Name, shouldSetFocus, this.IsMouseCaptured, e.OriginalSource));
648 System.Diagnostics.Debug.WriteLine(
649 string.Format(CultureInfo.InvariantCulture, "{0} ) WorkflowViewElement.OnMouseDown.SetFocus ({1})",
650 DateTime.Now.ToLocalTime(), this.GetType().Name));
651 //attempt to set focused and keyboard focused element to new designer
652 Keyboard.Focus(this);
655 bool isSelected = this.Context.Items.GetValue<Selection>().SelectedObjects.Contains<ModelItem>(this.ModelItem);
661 Selection.Toggle(this.Context, this.ModelItem);
665 this.shouldChangeSelectionOnMouseUp = true;
670 if (!rightMouseClickWithCtrlDown)
674 // if it's right mouse click without ctrl, change selection only if the current item is not selected.
675 bool alreadySelected = false;
676 Selection selection = this.Context.Items.GetValue<Selection>();
677 foreach (ModelItem item in selection.SelectedObjects)
679 if (item == this.ModelItem)
681 alreadySelected = true;
685 if (!alreadySelected)
687 Selection.SelectOnly(this.Context, this.ModelItem);
690 else if (this.leftMouseButtonDown)
694 Selection.SelectOnly(this.Context, this.ModelItem);
698 this.shouldChangeSelectionOnMouseUp = true;
704 this.rightMouseClickWithCtrlDown = false;
705 this.rightMouseClick = false;
707 if (shouldUpdateLastActivationPoint)
709 this.lastActivationElement = e.OriginalSource as UIElement;
712 if (e.LeftButton == MouseButtonState.Pressed)
714 this.Designer.lastClickedDesigner = this;
720 Selection selection = this.Context.Items.GetValue<Selection>();
722 if (!this.DraggingMultipleItemsEnabled)
724 if (selection.SelectionCount != 1)
730 return selection.SelectedObjects.All<ModelItem>((p) =>
732 return p != null && p != p.Root && p.View != null && ((WorkflowViewElement)p.View).IsVisible &&
733 (DragDropHelper.GetCompositeView((WorkflowViewElement)p.View) as ICompositeView) != null;
737 protected override void OnMouseMove(MouseEventArgs e)
739 // if model item is removed, uncapture mouse and return
740 if (this.ModelItem != null && this.ModelItem != this.ModelItem.Root && this.ModelItem.Parent == null)
742 if (this.IsMouseCaptured)
749 if (e.LeftButton == MouseButtonState.Pressed &&
750 this.leftMouseButtonDown &&
751 this.ModelItem != null &&
752 this.IsMouseOnDragHandle(this.lastMouseDownPoint) &&
753 e.Timestamp - this.lastMouseButtonDownTimeStamp > 100)
756 Point newPosition = e.GetPosition(this);
758 Vector difference = newPosition - this.lastMouseDownPoint;
759 if (Math.Abs(difference.X) > SystemParameters.MinimumHorizontalDragDistance ||
760 Math.Abs(difference.Y) > SystemParameters.MinimumVerticalDragDistance)
762 Selection selection = this.Context.Items.GetValue<Selection>();
763 // If the current model item is not selected, add it to the selection.
764 if (!selection.SelectedObjects.Contains(this.ModelItem))
766 if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
768 Selection.Toggle(this.Context, this.ModelItem);
772 Selection.SelectOnly(this.Context, this.ModelItem);
776 if (this.AllowDragging())
778 //if mouse is caputured - release capture, drag&drop infrastructure will take care now for tracking mouse move
779 if (this.IsMouseCaptured)
785 this.leftMouseButtonDown = false;
794 private bool IsMouseOnDragHandle(Point mousePoint)
796 if (this.DragHandle != null)
798 GeneralTransform transform = this.DragHandle.TransformToAncestor(this);
799 Point dragHandleLocation = transform.Transform(new Point(0, 0));
800 Rect dragHandleRect = new Rect(dragHandleLocation, new Size(this.DragHandle.ActualWidth, this.DragHandle.ActualHeight));
801 if (dragHandleRect.Contains(mousePoint))
809 protected override void OnMouseUp(MouseButtonEventArgs e)
811 System.Diagnostics.Debug.WriteLine(
812 CultureInfo.InvariantCulture,
813 string.Format(CultureInfo.CurrentUICulture, "{0} ) WorkflowViewElement.OnMouseUp ({1}, mouseCaptured {2})",
814 DateTime.Now.ToLocalTime(), this.GetType().Name, this.IsMouseCaptured));
816 if (this.leftMouseButtonDown)
818 if (this.IsMouseCaptured)
823 if (!this.Designer.SuppressSelectionOnMouseUp && this.shouldChangeSelectionOnMouseUp)
825 if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
827 Selection.Toggle(this.Context, this.ModelItem);
831 Selection.SelectOnly(this.Context, this.ModelItem);
835 this.leftMouseButtonDown = false;
839 this.shouldChangeSelectionOnMouseUp = false;
843 void OnBreadCrumbTimerElapsed(object sender, ElapsedEventArgs e)
845 this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
847 this.breadCrumbTimer.Stop();
848 this.MakeRootDesigner();
852 void OnDrag(DragEventArgs e)
856 e.Effects = DragDropEffects.None;
861 // This is to set the cursor to the forbidden icon when dragging to the designer.
862 // It doesn't affect the drag-drop behavior of components that have AllowDrop == ture within the designer.
863 protected override void OnDragEnter(DragEventArgs e)
869 // This is to set the cursor to the forbidden icon when dragging within the designer.
870 // It doesn't affect the drag-drop behavior of components that have AllowDrop == ture within the designer.
871 protected override void OnDragOver(DragEventArgs e)
877 protected override void OnPreviewDragEnter(DragEventArgs e)
879 if (this.ShowExpanded == false)
881 this.breadCrumbTimer.Start();
883 base.OnPreviewDragEnter(e);
886 protected override void OnPreviewDragLeave(DragEventArgs e)
888 this.breadCrumbTimer.Stop();
889 base.OnPreviewDragLeave(e);
892 protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
894 this.breadCrumbTimer.Stop();
895 base.OnPreviewMouseUp(e);
898 static void OnModelItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
900 WorkflowViewElement viewElement = (WorkflowViewElement)dependencyObject;
901 viewElement.OnModelItemChanged(e.NewValue);
904 void BeginDropAnimation(WorkflowViewElement target)
906 DropAnimation opacityAnimation = new DropAnimation();
907 target.BeginAnimation(FrameworkElement.OpacityProperty, opacityAnimation);
910 [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DoNotCatchGeneralExceptionTypes,
911 Justification = "Catching all exceptions to avoid VS Crash.")]
912 [SuppressMessage("Reliability", "Reliability108",
913 Justification = "Catching all exceptions to avoid VS crash.")]
919 using (ModelEditingScope editingScope = this.ModelItem.BeginEdit(SR.MoveEditingScopeDescription, true))
921 HashSet<WorkflowViewElement> draggedViews = new HashSet<WorkflowViewElement>();
922 Dictionary<ModelItem, ICompositeView> sourceContainers = new Dictionary<ModelItem, ICompositeView>();
923 HashSet<ICompositeView> compViewSet = new HashSet<ICompositeView>();
924 Selection selection = this.Context.Items.GetValue<Selection>();
925 IEnumerable<ModelItem> selectedObjects = selection.SelectedObjects;
926 IEnumerable<ModelItem> modelItemsToDrag = DragDropHelper.GetModelItemsToDrag(selectedObjects);
928 // Save the source containers for the dragged items
929 foreach (ModelItem modelItem in modelItemsToDrag)
931 WorkflowViewElement view = (WorkflowViewElement)modelItem.View;
932 draggedViews.Add(view);
933 ICompositeView container = DragDropHelper.GetCompositeView(view) as ICompositeView;
934 sourceContainers.Add(modelItem, container);
935 // If Add returns true => the container is added the first time, which is always ok
936 // If Add returns false => the container is added more than once
937 // it must be a IMultipleDragEnabledCompositeView, otherwise, return, because
938 // we don't support dragging from ICompositeView.
939 if (!compViewSet.Add(container) && !(container is IMultipleDragEnabledCompositeView))
945 // Calculate the anchor point for the dragged items
946 Point relativeLocation = GetRelativeLocation(draggedViews);
947 Point referencePoint = this.lastMouseDownPoint;
948 referencePoint.Offset(relativeLocation.X, relativeLocation.Y);
951 DataObject dataObject = DragDropHelper.DoDragMoveImpl(draggedViews, referencePoint);
952 IEnumerable<WorkflowViewElement> movedViewElements = DragDropHelper.GetDragDropMovedViewElements(dataObject);
954 // once drag drop is done make sure the CompositeView is notified of the change in data
955 if (movedViewElements != null)
957 Dictionary<ICompositeView, List<ModelItem>> containerMovedModelItemList = new Dictionary<ICompositeView, List<ModelItem>>();
959 // Create containerMovedModelItemList
960 foreach (WorkflowViewElement view in movedViewElements)
962 ICompositeView compView = DragDropHelper.GetCompositeView(view) as ICompositeView;
963 Fx.Assert(compView != null, "not an ICompositeView");
964 if (!containerMovedModelItemList.ContainsKey(compView))
966 containerMovedModelItemList.Add(compView, new List<ModelItem>());
968 containerMovedModelItemList[compView].Add(view.ModelItem);
971 // Call OnItemsMoved to notify the source container.
972 foreach (KeyValuePair<ICompositeView, List<ModelItem>> pair in containerMovedModelItemList)
974 if (pair.Key is IMultipleDragEnabledCompositeView)
976 ((IMultipleDragEnabledCompositeView)pair.Key).OnItemsMoved(pair.Value);
980 if (pair.Value.Count >= 2)
982 throw FxTrace.Exception.AsError(
983 new InvalidOperationException(SR.Error_MovingMoreThanOneItemsFromICompositeView));
985 pair.Key.OnItemMoved(pair.Value[0]);
990 foreach (WorkflowViewElement view in movedViewElements)
992 BeginDropAnimation(view);
995 // the drop target is using old DragDropHelper API and did not set the moved view elements
998 DragDropEffects executedDragDropEffect = DragDropHelper.GetDragDropCompletedEffects(dataObject);
999 if (executedDragDropEffect == DragDropEffects.Move)
1001 if (modelItemsToDrag.Count() == 1)
1003 ModelItem movedItem = modelItemsToDrag.First<ModelItem>();
1004 sourceContainers[movedItem].OnItemMoved(movedItem);
1005 BeginDropAnimation((WorkflowViewElement)movedItem.View);
1009 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.DraggingMulitpleItemsError));
1013 editingScope.Complete();
1015 bool dropHappened = movedViewElements != null
1016 || DragDropHelper.GetDragDropCompletedEffects(dataObject) == DragDropEffects.Move;
1019 // add the selected objects back into selection.
1020 this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)(() =>
1023 foreach (ModelItem item in selectedObjects)
1025 // We need only the first one
1026 IInputElement viewToFocus = item == null ? null : item.View as IInputElement;
1027 if (viewToFocus != null)
1029 Keyboard.Focus(viewToFocus);
1033 this.Context.Items.SetValue(new Selection(selectedObjects));
1040 ErrorReporting.ShowErrorMessage(e.Message);
1044 private Point GetRelativeLocation(IEnumerable<WorkflowViewElement> draggedViews)
1046 HashSet<WorkflowViewElement> viewElements = new HashSet<WorkflowViewElement>(draggedViews);
1047 if (!viewElements.Contains(this))
1049 viewElements.Add(this);
1051 Dictionary<WorkflowViewElement, Point> locations = DragDropHelper.GetViewElementRelativeLocations(viewElements);
1052 return locations[this];
1055 internal void NotifyContextMenuLoaded(ContextMenu menu)
1059 OnContextMenuLoaded(menu);
1063 class WorkflowViewElementAutomationPeer : UIElementAutomationPeer
1065 WorkflowViewElement owner;
1067 public WorkflowViewElementAutomationPeer(WorkflowViewElement owner)
1073 protected override AutomationControlType GetAutomationControlTypeCore()
1075 return AutomationControlType.Custom;
1079 protected override string GetAutomationIdCore()
1081 string automationId = this.GetClassNameCore();
1082 string automationIdVariablePartMemberName = owner.GetAutomationIdMemberName();
1083 if (!string.IsNullOrEmpty(automationIdVariablePartMemberName))
1085 ModelItem modelItem = this.owner.ModelItem;
1086 string variablePartOfAutomationId = string.Empty;
1087 if (modelItem != null)
1089 ModelProperty property = modelItem.Properties[automationIdVariablePartMemberName];
1090 Fx.Assert(property != null, "property to use for Automation ID variable part missing ? are you using the right property Name?");
1091 if (property.Value != null)
1093 variablePartOfAutomationId = property.Value.GetCurrentValue().ToString();
1096 automationId = variablePartOfAutomationId + "(" + this.GetClassNameCore() + ")";
1098 return automationId;
1101 protected override string GetNameCore()
1103 Type itemType = null;
1104 if (this.owner.ModelItem != null)
1106 itemType = this.owner.ModelItem.ItemType;
1110 itemType = this.owner.GetType();
1113 if (itemType.IsGenericType)
1115 //append the argument types for generic types
1116 //we expect the single level of generic is sufficient for the screen reader, so we're no going into
1117 //nesting of generic types
1118 Type[] argumentTypes = itemType.GetGenericArguments();
1119 StringBuilder name = new StringBuilder(itemType.Name);
1121 foreach (Type argument in argumentTypes)
1123 name.Append(argument.Name);
1126 name.Replace(',', ']', name.Length - 1, 1);
1127 return name.ToString();
1131 return itemType.Name;
1135 protected override string GetHelpTextCore()
1137 return this.owner.GetAutomationHelpText();
1140 protected override string GetItemStatusCore()
1142 return this.owner.GetAutomationItemStatus();
1145 protected override string GetClassNameCore()
1147 return this.owner.GetType().Name;