8ab7e00fe935f5707c767038a8e9a7e98b2162ad
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / WorkflowViewElement.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 #pragma warning disable 618
6
7 namespace System.Activities.Presentation
8 {
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;
16     using System.Runtime;
17     using System.Windows;
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;
24     using System.Timers;
25     using System.Windows.Threading;
26     using System.Text;
27     using System.Linq;
28     using System.Activities.Presentation.Internal.PropertyEditing;
29
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
34     {
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)));
49
50         const string ExpandViewStateKey = "IsExpanded";
51         internal const string PinnedViewStateKey = "IsPinned";
52         Timer breadCrumbTimer;
53         int lastMouseButtonDownTimeStamp;
54
55         internal string CustomItemStatus { get; set; }
56
57         static void OnExpandStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
58         {
59             WorkflowViewElement viewElement = obj as WorkflowViewElement;
60             viewElement.OnExpandStateChanged((bool)e.NewValue);
61         }
62
63         static void OnPinStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
64         {
65             WorkflowViewElement viewElement = obj as WorkflowViewElement;
66             viewElement.OnPinStateChanged((bool)e.NewValue);
67         }
68
69
70         static void OnContextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
71         {
72             WorkflowViewElement viewElement = obj as WorkflowViewElement;
73             viewElement.OnContextChanged();
74         }
75
76         static void OnShowExpandedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
77         {
78             WorkflowViewElement viewElement = obj as WorkflowViewElement;
79             viewElement.OnShowExpandedChanged((bool)e.NewValue);
80         }
81
82         static void OnReadOnlyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
83         {
84             WorkflowViewElement viewElement = obj as WorkflowViewElement;
85             viewElement.OnReadOnlyChanged((bool)e.NewValue);
86         }
87
88         protected virtual void OnShowExpandedChanged(bool newValue)
89         {
90         }
91
92         protected virtual void OnReadOnlyChanged(bool isReadOnly)
93         {
94         }
95
96         internal void ForceCollapse()
97         {
98             this.ExpandState = false;
99             if (this.Designer.ShouldExpandAll)
100             {
101                 this.PinState = true;
102             }
103         }
104
105         void OnContextChanged()
106         {
107             //Setting the binding here so that we have a handle to DesignerView.
108             SetShowExpandedBindings();
109         }
110
111         void OnExpandStateChanged(bool newValue)
112         {
113             if (this.ModelItem != null && this.Context != null)
114             {
115                 this.ViewStateService.StoreViewState(this.ModelItem, ExpandViewStateKey, newValue);
116             }
117         }
118
119         void OnPinStateChanged(bool newValue)
120         {
121             if (this.ModelItem != null && this.Context != null)
122             {
123                 this.ViewStateService.StoreViewState(this.ModelItem, WorkflowViewElement.PinnedViewStateKey, newValue);
124             }
125         }
126
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;
135
136         public WorkflowViewElement()
137         {
138             this.Collapsible = true;
139             this.IsAncestorOfRootDesigner = false;
140             this.DragHandle = this;
141
142             this.Loaded += (sender, eventArgs) =>
143                 {
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)
146                     {
147                         this.Designer.RegisterViewElement(this);
148                     }
149
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)
157                     {
158                         ((IModelTreeItem)this.ModelItem).SetCurrentView(this);
159                     }
160
161                     //Get the ExpandState from ViewState.
162                     if (this.ModelItem != null)
163                     {
164                         object expandCollapseViewState = this.ViewStateService.RetrieveViewState(this.ModelItem, ExpandViewStateKey);
165                         object pinViewState = this.ViewStateService.RetrieveViewState(this.ModelItem, WorkflowViewElement.PinnedViewStateKey);
166                         if (expandCollapseViewState != null)
167                         {
168                             this.ExpandState = (bool)expandCollapseViewState;
169                         }
170                         if (pinViewState != null)
171                         {
172                             this.PinState = (bool)pinViewState;
173                         }
174                     }
175                     this.UseLayoutRounding = true;
176                 };
177
178             this.Unloaded += (sender, eventArgs) =>
179                 {
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)
182                     {
183                         this.Designer.UnregisterViewElement(this);
184                     }
185
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();
190                 };
191         }
192
193         protected override void OnInitialized(EventArgs e)
194         {
195             base.OnInitialized(e);
196
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);
202         }
203
204         void SetShowExpandedBindings()
205         {
206             MultiBinding multiBinding = new MultiBinding();
207             //Bind to ModelItem
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);
228             //Bind to PinState
229             Binding pinStateBinding = new Binding();
230             pinStateBinding.Source = this;
231             pinStateBinding.Path = new PropertyPath(WorkflowViewElement.PinStateProperty);
232             //Bind to self
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);
247
248             multiBinding.Mode = BindingMode.OneWay;
249             multiBinding.Converter = new ShowExpandedMultiValueConverter();
250             BindingOperations.SetBinding(this, WorkflowViewElement.ShowExpandedProperty, multiBinding );
251         }
252
253         [Fx.Tag.KnownXamlExternal]
254         public EditingContext Context
255         {
256             get { return (EditingContext)GetValue(ContextProperty); }
257             set { SetValue(ContextProperty, value); }
258         }
259
260         public bool ExpandState
261         {
262             get { return (bool)GetValue(ExpandStateProperty); }
263             set { SetValue(ExpandStateProperty, value); }
264         }
265
266         public bool PinState
267         {
268             get { return (bool)GetValue(PinStateProperty); }
269             set { SetValue(PinStateProperty, value); }
270         }
271
272         //This guides us whether to show the expand collapse button for this ViewElement or not.
273         public bool Collapsible
274         {
275             get;
276             set;
277         }
278
279         public bool IsRootDesigner
280         {
281             get { return (bool)GetValue(IsRootDesignerProperty); }
282             internal set { SetValue(IsRootDesignerProperty, value); }
283         }
284
285         internal bool IsAncestorOfRootDesigner
286         {
287             get;
288             set;
289         }
290         
291         public bool ShowExpanded
292         {
293             get { return (bool)GetValue(ShowExpandedProperty); }
294         }
295
296         internal bool DoesParentAlwaysExpandChild()
297         {
298             return ViewUtilities.DoesParentAlwaysExpandChildren(this.ModelItem, this.Context);
299         }
300
301         internal bool DoesParentAlwaysCollapseChildren()
302         {
303             return ViewUtilities.DoesParentAlwaysCollapseChildren(this.ModelItem, this.Context);
304         }
305
306
307         [Fx.Tag.KnownXamlExternal]
308         public ModelItem ModelItem
309         {
310             get { return (ModelItem)GetValue(ModelItemProperty); }
311             set { SetValue(ModelItemProperty, value); }
312         }
313
314         public FrameworkElement DragHandle
315         {
316             get;
317             set;
318         }
319
320         protected bool IsReadOnly
321         {
322             get { return (bool)GetValue(IsReadOnlyProperty); }
323             private set { SetValue(IsReadOnlyProperty, value); }
324         }
325
326         internal ICompositeView ActiveCompositeView
327         {
328             get
329             {
330                 if (!this.ShowExpanded)
331                 {
332                     return null;
333                 }
334                 ICompositeView activeContainer = null;
335                 if (null != this.compositeViews && null != this.lastActivationElement)
336                 {
337                     activeContainer = this.compositeViews.Find(p =>
338                         {
339                             Visual visual = p as Visual;
340                             return (null != visual &&
341                                 visual == this.lastActivationElement.FindCommonVisualAncestor(visual));
342                         });
343                 }
344                 activeContainer = activeContainer ?? this.DefaultCompositeView;
345
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)));
350
351                 return activeContainer;
352             }
353         }
354
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
358         {
359             get
360             {
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");
364                 return viewService;
365             }
366         }
367
368         // Shortcut to ViewStateService in editingcontext.services
369         protected internal ViewStateService ViewStateService
370         {
371             get
372             {
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;
376             }
377         }
378
379         protected internal DesignerView Designer
380         {
381             get
382             {
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");
385                 return designer;
386             }
387         }
388
389         protected IList<ICompositeView> CompositeViews
390         {
391             get { return this.compositeViews; }
392         }
393
394         protected ICompositeView DefaultCompositeView
395         {
396             get { return this.defaultCompositeView; }
397         }
398
399         internal bool DraggingMultipleItemsEnabled
400         {
401             get { return this.Context.Services.GetService<DesignerConfigurationService>().MultipleItemsDragDropEnabled; }
402         }
403
404         protected virtual string GetAutomationIdMemberName()
405         {
406             return null;
407         }
408
409         protected virtual string GetAutomationHelpText()
410         {
411             return string.Empty;
412         }
413
414         protected internal virtual string GetAutomationItemStatus()
415         {
416             if (this.CustomItemStatus == null)
417             {
418                 return string.Empty;
419             }
420             else
421             {
422                 if (!this.CustomItemStatus.EndsWith(" ", StringComparison.Ordinal))
423                 {
424                     this.CustomItemStatus += " ";
425                 }
426                 return this.CustomItemStatus;
427             }
428         }
429
430         protected override AutomationPeer OnCreateAutomationPeer()
431         {
432             return new WorkflowViewElementAutomationPeer(this);
433         }
434
435         protected internal virtual void OnEditAnnotation()
436         {
437             return;
438         }
439
440         public void RegisterDefaultCompositeView(ICompositeView container)
441         {
442             if (null == container)
443             {
444                 throw FxTrace.Exception.AsError(new ArgumentNullException("container"));
445             }
446
447             this.defaultCompositeView = container;
448
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()));
453         }
454
455         public void UnregisterDefaultCompositeView(ICompositeView container)
456         {
457             if (null == container)
458             {
459                 throw FxTrace.Exception.AsError(new ArgumentNullException("container"));
460             }
461
462             if (object.Equals(this.defaultCompositeView, container))
463             {
464                 this.defaultCompositeView = null;
465             }
466         }
467
468         public void RegisterCompositeView(ICompositeView container)
469         {
470             if (null == container)
471             {
472                 throw FxTrace.Exception.AsError(new ArgumentNullException("container"));
473             }
474
475             if (null == this.CompositeViews)
476             {
477                 this.compositeViews = new List<ICompositeView>();
478             }
479             if (!this.compositeViews.Contains(container))
480             {
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()));
485
486                 this.compositeViews.Add(container);
487             }
488         }
489
490         public void UnregisterCompositeView(ICompositeView container)
491         {
492             if (null == container)
493             {
494                 throw FxTrace.Exception.AsError(new ArgumentNullException("container"));
495             }
496
497             if (null != this.compositeViews && this.compositeViews.Contains(container))
498             {
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));
503
504                 this.compositeViews.Remove(container);
505                 if (0 == this.compositeViews.Count)
506                 {
507                     this.compositeViews = null;
508                 }
509             }
510         }
511
512         void OnGotFocusEvent(object sender, RoutedEventArgs e)
513         {
514             DesignerView designerView = this.Context.Services.GetService<DesignerView>();
515             if (!e.Handled && this.ModelItem != null && !designerView.IsMultipleSelectionMode)
516             {
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)
524                 {
525                     becomesSelection = false;
526                 }
527                 else if (this.rightMouseClick)
528                 {
529                     foreach (ModelItem item in selection.SelectedObjects)
530                     {
531                         if (item == this.ModelItem)
532                         {
533                             becomesSelection = false;
534                             break;
535                         }
536                     }
537                 }
538
539                 //When there is only one selected model item, we want to change the selection when we tab into other items.
540                 if (becomesSelection)
541                 {
542                     Selection.SelectOnly(this.Context, this.ModelItem);
543                 }
544
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));
548
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))
552                 {
553                     this.lastActivationElement = e.OriginalSource as UIElement;
554                 }
555                 e.Handled = true;
556             }
557             else
558             {
559                 this.lastActivationElement = null;
560             }
561         }
562
563         protected virtual void OnModelItemChanged(object newItem)
564         {
565         }
566
567         protected virtual void OnContextMenuLoaded(ContextMenu menu)
568         {
569         }
570
571         void MakeRootDesigner()
572         {
573             DesignerView designerView = this.Context.Services.GetService<DesignerView>();
574             if (!this.Equals(designerView.RootDesigner))
575             {
576                 designerView.MakeRootDesigner(this.ModelItem);
577                 this.leftMouseButtonDown = false;
578             }
579         }
580
581         protected override void OnMouseDown(MouseButtonEventArgs e)
582         {
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;
590
591             if (this.ModelItem == null)
592             {
593                 return;
594             }
595
596             if (e.LeftButton == MouseButtonState.Pressed)
597             {
598                 this.leftMouseButtonDown = true;
599                 this.lastMouseDownPoint = e.GetPosition(this);
600                 if (!Keyboard.IsKeyDown(Key.LeftCtrl) && !Keyboard.IsKeyDown(Key.RightCtrl))
601                 {
602                     this.CaptureMouse();
603                 }
604                 else
605                 {
606                     shouldToggle = true;
607                 }
608
609                 shouldSetFocus = Keyboard.FocusedElement != this;
610                 e.Handled = true;
611                 this.Designer.ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled = true;
612                 shouldUpdateLastActivationPoint = true;
613             }
614
615             if (e.LeftButton == MouseButtonState.Pressed && e.ClickCount == 2 && this.Designer.lastClickedDesigner == this)
616             {
617                 this.MakeRootDesigner();
618                 Mouse.Capture(null);
619                 e.Handled = true;
620                 this.Designer.ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled = false;
621             }
622
623             if (e.RightButton == MouseButtonState.Pressed)
624             {
625                 rightMouseClick = true;
626                 this.lastMouseDownPoint = e.GetPosition(this);
627                 if (!Keyboard.IsKeyDown(Key.LeftCtrl) && !Keyboard.IsKeyDown(Key.RightCtrl))
628                 {
629                     shouldSetFocus = Keyboard.FocusedElement != this;
630                 }
631                 else
632                 {
633                     rightMouseClickWithCtrlDown = true;
634                 }
635
636                 e.Handled = true;
637                 shouldUpdateLastActivationPoint = true;
638             }
639
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));
643
644             base.OnMouseDown(e);
645
646             if (shouldSetFocus)
647             {
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);
653             }
654
655             bool isSelected = this.Context.Items.GetValue<Selection>().SelectedObjects.Contains<ModelItem>(this.ModelItem);
656
657             if (shouldToggle)
658             {
659                 if (!isSelected)
660                 {
661                     Selection.Toggle(this.Context, this.ModelItem);
662                 }
663                 else
664                 {
665                     this.shouldChangeSelectionOnMouseUp = true;
666                 }
667             }
668             else
669             {
670                 if (!rightMouseClickWithCtrlDown)
671                 {
672                     if (rightMouseClick)
673                     {
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)
678                         {
679                             if (item == this.ModelItem)
680                             {
681                                 alreadySelected = true;
682                                 break;
683                             }
684                         }
685                         if (!alreadySelected)
686                         {
687                             Selection.SelectOnly(this.Context, this.ModelItem);
688                         }
689                     }
690                     else if (this.leftMouseButtonDown)
691                     {
692                         if (!isSelected)
693                         {
694                             Selection.SelectOnly(this.Context, this.ModelItem);
695                         }
696                         else
697                         {
698                             this.shouldChangeSelectionOnMouseUp = true;
699                         }
700                     }
701                 }
702             }
703
704             this.rightMouseClickWithCtrlDown = false;
705             this.rightMouseClick = false;
706
707             if (shouldUpdateLastActivationPoint)
708             {
709                 this.lastActivationElement = e.OriginalSource as UIElement;
710             }
711
712             if (e.LeftButton == MouseButtonState.Pressed)
713             {
714                 this.Designer.lastClickedDesigner = this;
715             }
716         }
717
718         bool AllowDragging()
719         {
720             Selection selection = this.Context.Items.GetValue<Selection>();
721             
722             if (!this.DraggingMultipleItemsEnabled)
723             {
724                 if (selection.SelectionCount != 1)
725                 {
726                     return false;
727                 }
728             }
729
730             return selection.SelectedObjects.All<ModelItem>((p) =>
731                 {
732                     return p != null && p != p.Root && p.View != null && ((WorkflowViewElement)p.View).IsVisible &&
733                         (DragDropHelper.GetCompositeView((WorkflowViewElement)p.View) as ICompositeView) != null;
734                 });
735         }
736
737         protected override void OnMouseMove(MouseEventArgs e)
738         {
739             // if model item is removed, uncapture mouse and return
740             if (this.ModelItem != null && this.ModelItem != this.ModelItem.Root && this.ModelItem.Parent == null)
741             {
742                 if (this.IsMouseCaptured)
743                 {
744                     Mouse.Capture(null);
745                 }
746                 return;
747             }
748
749             if (e.LeftButton == MouseButtonState.Pressed &&
750                this.leftMouseButtonDown &&
751                this.ModelItem != null &&
752                this.IsMouseOnDragHandle(this.lastMouseDownPoint) &&
753                e.Timestamp - this.lastMouseButtonDownTimeStamp > 100)
754             {
755                 //get new position
756                 Point newPosition = e.GetPosition(this);
757                 //calculate distance
758                 Vector difference = newPosition - this.lastMouseDownPoint;
759                 if (Math.Abs(difference.X) > SystemParameters.MinimumHorizontalDragDistance ||
760                     Math.Abs(difference.Y) > SystemParameters.MinimumVerticalDragDistance)
761                 {
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))
765                     {
766                         if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
767                         {
768                             Selection.Toggle(this.Context, this.ModelItem);
769                         }
770                         else
771                         {
772                             Selection.SelectOnly(this.Context, this.ModelItem);
773                         }
774                     }
775
776                     if (this.AllowDragging())
777                     {
778                         //if mouse is caputured - release capture, drag&drop infrastructure will take care now for tracking mouse move
779                         if (this.IsMouseCaptured)
780                         {
781                             Mouse.Capture(null);
782                             StartDragging();
783                         }
784
785                         this.leftMouseButtonDown = false;
786                         e.Handled = true;
787                     }
788                 }
789             }
790             
791             base.OnMouseMove(e);
792         }
793
794         private bool IsMouseOnDragHandle(Point mousePoint)
795         {
796             if (this.DragHandle != null)
797             {
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))
802                 {
803                     return true;
804                 }
805             }
806             return false;
807         }
808
809         protected override void OnMouseUp(MouseButtonEventArgs e)
810         {
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));
815
816             if (this.leftMouseButtonDown)
817             {
818                 if (this.IsMouseCaptured)
819                 {
820                     Mouse.Capture(null);
821                 }
822
823                 if (!this.Designer.SuppressSelectionOnMouseUp && this.shouldChangeSelectionOnMouseUp)
824                 {
825                     if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
826                     {
827                         Selection.Toggle(this.Context, this.ModelItem);
828                     }
829                     else
830                     {
831                         Selection.SelectOnly(this.Context, this.ModelItem);
832                     }
833                 }
834
835                 this.leftMouseButtonDown = false;
836                 e.Handled = true;
837             }
838
839             this.shouldChangeSelectionOnMouseUp = false;
840             base.OnMouseUp(e);
841         }
842
843         void OnBreadCrumbTimerElapsed(object sender, ElapsedEventArgs e)
844         {
845             this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
846                 {
847                     this.breadCrumbTimer.Stop();
848                     this.MakeRootDesigner();
849                 }));
850         }
851
852         void OnDrag(DragEventArgs e)
853         {
854             if (!e.Handled)
855             {
856                 e.Effects = DragDropEffects.None;
857                 e.Handled = true;
858             }
859         }
860
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)
864         {
865             this.OnDrag(e);
866             base.OnDragEnter(e);
867         }
868
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)
872         {
873             this.OnDrag(e);
874             base.OnDragOver(e);
875         }
876
877         protected override void OnPreviewDragEnter(DragEventArgs e)
878         {
879             if (this.ShowExpanded == false)
880             {
881                 this.breadCrumbTimer.Start();
882             }
883             base.OnPreviewDragEnter(e);
884         }
885
886         protected override void OnPreviewDragLeave(DragEventArgs e)
887         {
888             this.breadCrumbTimer.Stop();
889             base.OnPreviewDragLeave(e);
890         }
891
892         protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
893         {
894             this.breadCrumbTimer.Stop();
895             base.OnPreviewMouseUp(e);
896         }
897
898         static void OnModelItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
899         {
900             WorkflowViewElement viewElement = (WorkflowViewElement)dependencyObject;
901             viewElement.OnModelItemChanged(e.NewValue);
902         }
903
904         void BeginDropAnimation(WorkflowViewElement target)
905         {
906             DropAnimation opacityAnimation = new DropAnimation();
907             target.BeginAnimation(FrameworkElement.OpacityProperty, opacityAnimation);
908         }
909
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.")]
914
915         void StartDragging()
916         {
917             try
918             {
919                 using (ModelEditingScope editingScope = this.ModelItem.BeginEdit(SR.MoveEditingScopeDescription, true))
920                 {
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);
927                     
928                     // Save the source containers for the dragged items
929                     foreach (ModelItem modelItem in modelItemsToDrag)
930                     {
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))
940                         {
941                             return;
942                         }
943                     }
944                     
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);
949
950
951                     DataObject dataObject = DragDropHelper.DoDragMoveImpl(draggedViews, referencePoint);
952                     IEnumerable<WorkflowViewElement> movedViewElements = DragDropHelper.GetDragDropMovedViewElements(dataObject);
953
954                     // once drag drop is done make sure the CompositeView is notified of the change in data
955                     if (movedViewElements != null)
956                     {
957                         Dictionary<ICompositeView, List<ModelItem>> containerMovedModelItemList = new Dictionary<ICompositeView, List<ModelItem>>();
958                         
959                         // Create containerMovedModelItemList
960                         foreach (WorkflowViewElement view in movedViewElements)
961                         {
962                             ICompositeView compView = DragDropHelper.GetCompositeView(view) as ICompositeView;
963                             Fx.Assert(compView != null, "not an ICompositeView");
964                             if (!containerMovedModelItemList.ContainsKey(compView))
965                             {
966                                 containerMovedModelItemList.Add(compView, new List<ModelItem>());
967                             }
968                             containerMovedModelItemList[compView].Add(view.ModelItem);
969                         }
970
971                         // Call OnItemsMoved to notify the source container.
972                         foreach (KeyValuePair<ICompositeView, List<ModelItem>> pair in containerMovedModelItemList)
973                         {
974                             if (pair.Key is IMultipleDragEnabledCompositeView)
975                             {
976                                 ((IMultipleDragEnabledCompositeView)pair.Key).OnItemsMoved(pair.Value);
977                             }
978                             else
979                             {
980                                 if (pair.Value.Count >= 2)
981                                 {
982                                     throw FxTrace.Exception.AsError(
983                                         new InvalidOperationException(SR.Error_MovingMoreThanOneItemsFromICompositeView));
984                                 }
985                                 pair.Key.OnItemMoved(pair.Value[0]);
986                             }
987                         }
988
989                         // animation
990                         foreach (WorkflowViewElement view in movedViewElements)
991                         {
992                             BeginDropAnimation(view);
993                         }
994                     }
995                     // the drop target is using old DragDropHelper API and did not set the moved view elements
996                     else
997                     {
998                         DragDropEffects executedDragDropEffect = DragDropHelper.GetDragDropCompletedEffects(dataObject);
999                         if (executedDragDropEffect == DragDropEffects.Move)
1000                         {
1001                             if (modelItemsToDrag.Count() == 1)
1002                             {
1003                                 ModelItem movedItem = modelItemsToDrag.First<ModelItem>();
1004                                 sourceContainers[movedItem].OnItemMoved(movedItem);
1005                                 BeginDropAnimation((WorkflowViewElement)movedItem.View);
1006                             }
1007                             else
1008                             {
1009                                 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.DraggingMulitpleItemsError));
1010                             }
1011                         }
1012                     }
1013                     editingScope.Complete();
1014
1015                     bool dropHappened = movedViewElements != null
1016                         || DragDropHelper.GetDragDropCompletedEffects(dataObject) == DragDropEffects.Move;
1017                     if (dropHappened)
1018                     {
1019                         // add the selected objects back into selection.
1020                         this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)(() =>
1021                         {
1022
1023                             foreach (ModelItem item in selectedObjects)
1024                             {
1025                                 // We need only the first one
1026                                 IInputElement viewToFocus = item == null ? null : item.View as IInputElement;
1027                                 if (viewToFocus != null)
1028                                 {
1029                                     Keyboard.Focus(viewToFocus);
1030                                     break;
1031                                 }
1032                             }
1033                             this.Context.Items.SetValue(new Selection(selectedObjects));
1034                         }));
1035                     }
1036                 }
1037             }
1038             catch (Exception e)
1039             {
1040                 ErrorReporting.ShowErrorMessage(e.Message);
1041             }
1042         }
1043
1044         private Point GetRelativeLocation(IEnumerable<WorkflowViewElement> draggedViews)
1045         {
1046             HashSet<WorkflowViewElement> viewElements = new HashSet<WorkflowViewElement>(draggedViews);
1047             if (!viewElements.Contains(this))
1048             {
1049                 viewElements.Add(this);
1050             }
1051             Dictionary<WorkflowViewElement, Point> locations = DragDropHelper.GetViewElementRelativeLocations(viewElements);
1052             return locations[this];
1053         }
1054
1055         internal void NotifyContextMenuLoaded(ContextMenu menu)
1056         {
1057             if (null != menu)
1058             {
1059                 OnContextMenuLoaded(menu);
1060             }
1061         }
1062
1063         class WorkflowViewElementAutomationPeer : UIElementAutomationPeer
1064         {
1065             WorkflowViewElement owner;
1066
1067             public WorkflowViewElementAutomationPeer(WorkflowViewElement owner)
1068                 : base(owner)
1069             {
1070                 this.owner = owner;
1071             }
1072
1073             protected override AutomationControlType GetAutomationControlTypeCore()
1074             {
1075                 return AutomationControlType.Custom;
1076             }
1077
1078
1079             protected override string GetAutomationIdCore()
1080             {
1081                 string automationId = this.GetClassNameCore();
1082                 string automationIdVariablePartMemberName = owner.GetAutomationIdMemberName();
1083                 if (!string.IsNullOrEmpty(automationIdVariablePartMemberName))
1084                 {
1085                     ModelItem modelItem = this.owner.ModelItem;
1086                     string variablePartOfAutomationId = string.Empty;
1087                     if (modelItem != null)
1088                     {
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)
1092                         {
1093                             variablePartOfAutomationId = property.Value.GetCurrentValue().ToString();
1094                         }
1095                     }
1096                     automationId = variablePartOfAutomationId + "(" + this.GetClassNameCore() + ")";
1097                 }
1098                 return automationId;
1099             }
1100
1101             protected override string GetNameCore()
1102             {
1103                 Type itemType = null;
1104                 if (this.owner.ModelItem != null)
1105                 {
1106                     itemType = this.owner.ModelItem.ItemType;
1107                 }
1108                 else
1109                 {
1110                     itemType = this.owner.GetType();
1111                 }
1112
1113                 if (itemType.IsGenericType)
1114                 {
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);
1120                     name.Append('[');
1121                     foreach (Type argument in argumentTypes)
1122                     {
1123                         name.Append(argument.Name);
1124                         name.Append(',');
1125                     }
1126                     name.Replace(',', ']', name.Length - 1, 1);
1127                     return name.ToString();
1128                 }
1129                 else
1130                 {
1131                     return itemType.Name;
1132                 }
1133             }
1134
1135             protected override string GetHelpTextCore()
1136             {
1137                 return this.owner.GetAutomationHelpText();
1138             }
1139
1140             protected override string GetItemStatusCore()
1141             {
1142                 return this.owner.GetAutomationItemStatus();
1143             }
1144
1145             protected override string GetClassNameCore()
1146             {
1147                 return this.owner.GetType().Name;
1148             }
1149         }
1150     }
1151
1152 }