File: cdf\src\NetFx40\Tools\System.Activities.Presentation\System\Activities\Presentation\WorkflowViewElement.cs
Project: ndp\System.Data.csproj (System.Data)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
 
#pragma warning disable 618
 
namespace System.Activities.Presentation
{
    using System.Activities.Presentation.Model;
    using System.Activities.Presentation.Services;
    using System.Activities.Presentation.View;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Runtime;
    using System.Windows;
    using System.Windows.Automation.Peers;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Controls.Primitives;
    using System.Windows.Data;
    using System.Timers;
    using System.Windows.Threading;
    using System.Text;
    using System.Linq;
    using System.Activities.Presentation.Internal.PropertyEditing;
 
    // This is the base class of all things visual that are associated with ModelItems.
    // e.g state designer, workflowelement designer, activity designe etc.
    // This provides access to the ModelItem attached to it,  and the EditingContext.
    public class WorkflowViewElement : ContentControl, ICompositeViewEvents
    {
        public static readonly DependencyProperty ModelItemProperty =
            DependencyProperty.Register("ModelItem", typeof(ModelItem), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(WorkflowViewElement.OnModelItemChanged)));
        public static readonly DependencyProperty ContextProperty =
            DependencyProperty.Register("Context", typeof(EditingContext), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(WorkflowViewElement.OnContextChanged)));
        public static readonly DependencyProperty ExpandStateProperty = 
            DependencyProperty.Register("ExpandState", typeof(bool), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(WorkflowViewElement.OnExpandStateChanged)));
        public static readonly DependencyProperty PinStateProperty =
            DependencyProperty.Register("PinState", typeof(bool), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(WorkflowViewElement.OnPinStateChanged)));
        public static readonly DependencyProperty ShowExpandedProperty =
            DependencyProperty.Register("ShowExpanded", typeof(bool), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(WorkflowViewElement.OnShowExpandedChanged)));
        internal readonly static DependencyProperty IsRootDesignerProperty =
            DependencyProperty.Register("IsRootDesigner", typeof(bool), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(false));
        static readonly DependencyProperty IsReadOnlyProperty =
            DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(WorkflowViewElement), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(WorkflowViewElement.OnReadOnlyChanged)));
 
        const string ExpandViewStateKey = "IsExpanded";
        internal const string PinnedViewStateKey = "IsPinned";
        Timer breadCrumbTimer;
        int lastMouseButtonDownTimeStamp;
 
        internal string CustomItemStatus { get; set; }
 
        static void OnExpandStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            WorkflowViewElement viewElement = obj as WorkflowViewElement;
            viewElement.OnExpandStateChanged((bool)e.NewValue);
        }
 
        static void OnPinStateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            WorkflowViewElement viewElement = obj as WorkflowViewElement;
            viewElement.OnPinStateChanged((bool)e.NewValue);
        }
 
 
        static void OnContextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            WorkflowViewElement viewElement = obj as WorkflowViewElement;
            viewElement.OnContextChanged();
        }
 
        static void OnShowExpandedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            WorkflowViewElement viewElement = obj as WorkflowViewElement;
            viewElement.OnShowExpandedChanged((bool)e.NewValue);
        }
 
        static void OnReadOnlyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            WorkflowViewElement viewElement = obj as WorkflowViewElement;
            viewElement.OnReadOnlyChanged((bool)e.NewValue);
        }
 
        protected virtual void OnShowExpandedChanged(bool newValue)
        {
        }
 
        protected virtual void OnReadOnlyChanged(bool isReadOnly)
        {
        }
 
        internal void ForceCollapse()
        {
            this.ExpandState = false;
            if (this.Designer.ShouldExpandAll)
            {
                this.PinState = true;
            }
        }
 
        void OnContextChanged()
        {
            //Setting the binding here so that we have a handle to DesignerView.
            SetShowExpandedBindings();
        }
 
        void OnExpandStateChanged(bool newValue)
        {
            if (this.ModelItem != null && this.Context != null)
            {
                this.ViewStateService.StoreViewState(this.ModelItem, ExpandViewStateKey, newValue);
            }
        }
 
        void OnPinStateChanged(bool newValue)
        {
            if (this.ModelItem != null && this.Context != null)
            {
                this.ViewStateService.StoreViewState(this.ModelItem, WorkflowViewElement.PinnedViewStateKey, newValue);
            }
        }
 
        bool leftMouseButtonDown = false;
        Point lastMouseDownPoint;
        UIElement lastActivationElement;
        List<ICompositeView> compositeViews;
        ICompositeView defaultCompositeView;
        bool rightMouseClickWithCtrlDown = false;
        bool rightMouseClick = false;
        bool shouldChangeSelectionOnMouseUp = false;
 
        public WorkflowViewElement()
        {
            this.Collapsible = true;
            this.IsAncestorOfRootDesigner = false;
            this.DragHandle = this;
 
            this.Loaded += (sender, eventArgs) =>
                {
                    // When the designer is loaded in Cider, the Context is not available and thus we cannot access the DesignerView (nor is it necessary)
                    if (this.Context != null)
                    {
                        this.Designer.RegisterViewElement(this);
                    }
 
                    this.GotFocus += new RoutedEventHandler(OnGotFocusEvent);
                    this.breadCrumbTimer = new Timer(2000);
                    this.breadCrumbTimer.Elapsed += new ElapsedEventHandler(OnBreadCrumbTimerElapsed);
                    this.breadCrumbTimer.AutoReset = false;
                    this.lastActivationElement = null;
                    this.SetValue(CutCopyPasteHelper.ChildContainersProperty, null);
                    if (this.ModelItem != null)
                    {
                        ((IModelTreeItem)this.ModelItem).SetCurrentView(this);
                    }
 
                    //Get the ExpandState from ViewState.
                    if (this.ModelItem != null)
                    {
                        object expandCollapseViewState = this.ViewStateService.RetrieveViewState(this.ModelItem, ExpandViewStateKey);
                        object pinViewState = this.ViewStateService.RetrieveViewState(this.ModelItem, WorkflowViewElement.PinnedViewStateKey);
                        if (expandCollapseViewState != null)
                        {
                            this.ExpandState = (bool)expandCollapseViewState;
                        }
                        if (pinViewState != null)
                        {
                            this.PinState = (bool)pinViewState;
                        }
                    }
                    this.UseLayoutRounding = true;
                };
 
            this.Unloaded += (sender, eventArgs) =>
                {
                    // When the designer is loaded in Cider, the Context is not available and thus we cannot access the DesignerView (nor is it necessary)
                    if (this.Context != null)
                    {
                        this.Designer.UnregisterViewElement(this);
                    }
 
                    this.GotFocus -= new RoutedEventHandler(OnGotFocusEvent);
                    Fx.Assert(this.breadCrumbTimer != null, "The timer should not be null.");
                    this.breadCrumbTimer.Elapsed -= new ElapsedEventHandler(OnBreadCrumbTimerElapsed);
                    this.breadCrumbTimer.Close();
                };
        }
 
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
 
            Binding readOnlyBinding = new Binding();
            readOnlyBinding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DesignerView), 1);
            readOnlyBinding.Path = new PropertyPath(DesignerView.IsReadOnlyProperty);
            readOnlyBinding.Mode = BindingMode.OneWay;
            this.SetBinding(IsReadOnlyProperty, readOnlyBinding);
        }
 
        void SetShowExpandedBindings()
        {
            MultiBinding multiBinding = new MultiBinding();
            //Bind to ModelItem
            Binding modelItemBinding = new Binding();
            modelItemBinding.Source = this;
            modelItemBinding.Path = new PropertyPath(WorkflowViewElement.ModelItemProperty);
            //Bind to IsRootDesigner
            Binding isRootDesignerBinding = new Binding();
            isRootDesignerBinding.Source = this;
            isRootDesignerBinding.Path = new PropertyPath(WorkflowViewElement.IsRootDesignerProperty);
            //Bind to DesignerView.ExpandAll
            Binding expandAllBinding = new Binding();
            DesignerView view = this.Context.Services.GetService<DesignerView>();
            expandAllBinding.Source = view;
            expandAllBinding.Path = new PropertyPath(DesignerView.ShouldExpandAllProperty);
            //Bind to DesignerView.CollapseAll
            Binding collapseAllBinding = new Binding();
            collapseAllBinding.Source = view;
            collapseAllBinding.Path = new PropertyPath(DesignerView.ShouldCollapseAllProperty);
            //Bind to ExpandState
            Binding expandStateBinding = new Binding();
            expandStateBinding.Source = this;
            expandStateBinding.Path = new PropertyPath(WorkflowViewElement.ExpandStateProperty);
            //Bind to PinState
            Binding pinStateBinding = new Binding();
            pinStateBinding.Source = this;
            pinStateBinding.Path = new PropertyPath(WorkflowViewElement.PinStateProperty);
            //Bind to self
            Binding selfBinding = new Binding();
            selfBinding.Source = this;
            //Bind to container (to recalculate on drag-drop.)
            Binding containerBinding = new Binding();
            containerBinding.Source = this;
            containerBinding.Path = new PropertyPath(DragDropHelper.DragSourceProperty);
            multiBinding.Bindings.Add(modelItemBinding);
            multiBinding.Bindings.Add(isRootDesignerBinding);
            multiBinding.Bindings.Add(expandAllBinding);
            multiBinding.Bindings.Add(collapseAllBinding);
            multiBinding.Bindings.Add(expandStateBinding);
            multiBinding.Bindings.Add(pinStateBinding);
            multiBinding.Bindings.Add(selfBinding);
            multiBinding.Bindings.Add(containerBinding);
 
            multiBinding.Mode = BindingMode.OneWay;
            multiBinding.Converter = new ShowExpandedMultiValueConverter();
            BindingOperations.SetBinding(this, WorkflowViewElement.ShowExpandedProperty, multiBinding );
        }
 
        [Fx.Tag.KnownXamlExternal]
        public EditingContext Context
        {
            get { return (EditingContext)GetValue(ContextProperty); }
            set { SetValue(ContextProperty, value); }
        }
 
        public bool ExpandState
        {
            get { return (bool)GetValue(ExpandStateProperty); }
            set { SetValue(ExpandStateProperty, value); }
        }
 
        public bool PinState
        {
            get { return (bool)GetValue(PinStateProperty); }
            set { SetValue(PinStateProperty, value); }
        }
 
        //This guides us whether to show the expand collapse button for this ViewElement or not.
        public bool Collapsible
        {
            get;
            set;
        }
 
        public bool IsRootDesigner
        {
            get { return (bool)GetValue(IsRootDesignerProperty); }
            internal set { SetValue(IsRootDesignerProperty, value); }
        }
 
        internal bool IsAncestorOfRootDesigner
        {
            get;
            set;
        }
        
        public bool ShowExpanded
        {
            get { return (bool)GetValue(ShowExpandedProperty); }
        }
 
        internal bool DoesParentAlwaysExpandChild()
        {
            return ViewUtilities.DoesParentAlwaysExpandChildren(this.ModelItem, this.Context);
        }
 
        internal bool DoesParentAlwaysCollapseChildren()
        {
            return ViewUtilities.DoesParentAlwaysCollapseChildren(this.ModelItem, this.Context);
        }
 
 
        [Fx.Tag.KnownXamlExternal]
        public ModelItem ModelItem
        {
            get { return (ModelItem)GetValue(ModelItemProperty); }
            set { SetValue(ModelItemProperty, value); }
        }
 
        public FrameworkElement DragHandle
        {
            get;
            set;
        }
 
        protected bool IsReadOnly
        {
            get { return (bool)GetValue(IsReadOnlyProperty); }
            private set { SetValue(IsReadOnlyProperty, value); }
        }
 
        internal ICompositeView ActiveCompositeView
        {
            get
            {
                if (!this.ShowExpanded)
                {
                    return null;
                }
                ICompositeView activeContainer = null;
                if (null != this.compositeViews && null != this.lastActivationElement)
                {
                    activeContainer = this.compositeViews.Find(p =>
                        {
                            Visual visual = p as Visual;
                            return (null != visual &&
                                visual == this.lastActivationElement.FindCommonVisualAncestor(visual));
                        });
                }
                activeContainer = activeContainer ?? this.DefaultCompositeView;
 
                System.Diagnostics.Debug.WriteLine(string.Format(
                    CultureInfo.InvariantCulture,
                    "Active ICompositeView in '{0}' is '{1}'",
                    this.GetType().Name, activeContainer == null ? "<null>" : activeContainer.GetHashCode().ToString(CultureInfo.InvariantCulture)));
 
                return activeContainer;
            }
        }
 
        // useful shortcuts for things we know we will be using a lot.
        // Shortcut to viewservice in editingcontext.services
        protected internal ViewService ViewService
        {
            get
            {
                Fx.Assert(this.Context != null, "Context should not be null ");
                ViewService viewService = this.Context.Services.GetService<ViewService>();
                Fx.Assert(viewService != null, "View service should never be null if we are in a valid view tree");
                return viewService;
            }
        }
 
        // Shortcut to ViewStateService in editingcontext.services
        protected internal ViewStateService ViewStateService
        {
            get
            {
                ViewStateService viewStateService = this.Context.Services.GetService<ViewStateService>();
                Fx.Assert(viewStateService != null, "ViewState service should never be null if we are in a valid view tree");
                return viewStateService;
            }
        }
 
        protected internal DesignerView Designer
        {
            get
            {
                DesignerView designer = this.Context.Services.GetService<DesignerView>();
                Fx.Assert(designer != null, "DesignerView service should never be null if we are in a valid state");
                return designer;
            }
        }
 
        protected IList<ICompositeView> CompositeViews
        {
            get { return this.compositeViews; }
        }
 
        protected ICompositeView DefaultCompositeView
        {
            get { return this.defaultCompositeView; }
        }
 
        internal bool DraggingMultipleItemsEnabled
        {
            get { return this.Context.Services.GetService<DesignerConfigurationService>().MultipleItemsDragDropEnabled; }
        }
 
        protected virtual string GetAutomationIdMemberName()
        {
            return null;
        }
 
        protected virtual string GetAutomationHelpText()
        {
            return string.Empty;
        }
 
        protected internal virtual string GetAutomationItemStatus()
        {
            if (this.CustomItemStatus == null)
            {
                return string.Empty;
            }
            else
            {
                if (!this.CustomItemStatus.EndsWith(" ", StringComparison.Ordinal))
                {
                    this.CustomItemStatus += " ";
                }
                return this.CustomItemStatus;
            }
        }
 
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new WorkflowViewElementAutomationPeer(this);
        }
 
        protected internal virtual void OnEditAnnotation()
        {
            return;
        }
 
        public void RegisterDefaultCompositeView(ICompositeView container)
        {
            if (null == container)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("container"));
            }
 
            this.defaultCompositeView = container;
 
            System.Diagnostics.Debug.WriteLine(string.Format(
                CultureInfo.InvariantCulture,
                "Default ICompositeView of type '{0}' for '{1}' loaded. hashcode = {2}",
                this.defaultCompositeView.GetType().Name, this.GetType().Name, this.defaultCompositeView.GetHashCode()));
        }
 
        public void UnregisterDefaultCompositeView(ICompositeView container)
        {
            if (null == container)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("container"));
            }
 
            if (object.Equals(this.defaultCompositeView, container))
            {
                this.defaultCompositeView = null;
            }
        }
 
        public void RegisterCompositeView(ICompositeView container)
        {
            if (null == container)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("container"));
            }
 
            if (null == this.CompositeViews)
            {
                this.compositeViews = new List<ICompositeView>();
            }
            if (!this.compositeViews.Contains(container))
            {
                System.Diagnostics.Debug.WriteLine(string.Format(
                    CultureInfo.InvariantCulture,
                    "ICompositeView of type '{0}' for '{1}' loaded. hashcode = {2}",
                    container.GetType().Name, this.GetType().Name, container.GetHashCode()));
 
                this.compositeViews.Add(container);
            }
        }
 
        public void UnregisterCompositeView(ICompositeView container)
        {
            if (null == container)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("container"));
            }
 
            if (null != this.compositeViews && this.compositeViews.Contains(container))
            {
                System.Diagnostics.Debug.WriteLine(string.Format(
                    CultureInfo.InvariantCulture,
                    "ICompositeView of type '{0}' for '{1}' unloaded",
                    container.GetType().Name, this.GetType().Name));
 
                this.compositeViews.Remove(container);
                if (0 == this.compositeViews.Count)
                {
                    this.compositeViews = null;
                }
            }
        }
 
        void OnGotFocusEvent(object sender, RoutedEventArgs e)
        {
            DesignerView designerView = this.Context.Services.GetService<DesignerView>();
            if (!e.Handled && this.ModelItem != null && !designerView.IsMultipleSelectionMode)
            {
                Selection selection = this.Context.Items.GetValue<Selection>();
                //update selection when following conditions apply:
                //1. We're not trying to open context menu using right click + ctrl key.
                //2. We're not clicking with left mouse - selection will be updated on left mouse button up
                //3. Current selection does not contain this.ModelItem if it's mouse right click
                bool becomesSelection = true;
                if (this.rightMouseClickWithCtrlDown || this.leftMouseButtonDown)
                {
                    becomesSelection = false;
                }
                else if (this.rightMouseClick)
                {
                    foreach (ModelItem item in selection.SelectedObjects)
                    {
                        if (item == this.ModelItem)
                        {
                            becomesSelection = false;
                            break;
                        }
                    }
                }
 
                //When there is only one selected model item, we want to change the selection when we tab into other items.
                if (becomesSelection)
                {
                    Selection.SelectOnly(this.Context, this.ModelItem);
                }
 
                System.Diagnostics.Debug.WriteLine(
                    string.Format(CultureInfo.InvariantCulture, "{0} ) WorkflowViewElement.OnGotFocusEvent ({1}, selection: {2}, becomesSelection {3}, raisedBy {4})",
                    DateTime.Now.ToLocalTime(), this.GetType().Name, selection.SelectionCount, becomesSelection, e.OriginalSource));
 
                //do not override last activation element if we get a reference to this (this will be passed as original source
                //whenever focus is set manualy - by direct call to Keyboard.SetFocus)
                if (!object.Equals(this, e.OriginalSource))
                {
                    this.lastActivationElement = e.OriginalSource as UIElement;
                }
                e.Handled = true;
            }
            else
            {
                this.lastActivationElement = null;
            }
        }
 
        protected virtual void OnModelItemChanged(object newItem)
        {
        }
 
        protected virtual void OnContextMenuLoaded(ContextMenu menu)
        {
        }
 
        void MakeRootDesigner()
        {
            DesignerView designerView = this.Context.Services.GetService<DesignerView>();
            if (!this.Equals(designerView.RootDesigner))
            {
                designerView.MakeRootDesigner(this.ModelItem);
                this.leftMouseButtonDown = false;
            }
        }
 
        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            this.lastMouseButtonDownTimeStamp = e.Timestamp;
            bool shouldSetFocus = false;
            bool shouldUpdateLastActivationPoint = false;
            this.leftMouseButtonDown = false;
            this.rightMouseClickWithCtrlDown = false;
            this.rightMouseClick = false;
            bool shouldToggle = false;
 
            if (this.ModelItem == null)
            {
                return;
            }
 
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                this.leftMouseButtonDown = true;
                this.lastMouseDownPoint = e.GetPosition(this);
                if (!Keyboard.IsKeyDown(Key.LeftCtrl) && !Keyboard.IsKeyDown(Key.RightCtrl))
                {
                    this.CaptureMouse();
                }
                else
                {
                    shouldToggle = true;
                }
 
                shouldSetFocus = Keyboard.FocusedElement != this;
                e.Handled = true;
                this.Designer.ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled = true;
                shouldUpdateLastActivationPoint = true;
            }
 
            if (e.LeftButton == MouseButtonState.Pressed && e.ClickCount == 2 && this.Designer.lastClickedDesigner == this)
            {
                this.MakeRootDesigner();
                Mouse.Capture(null);
                e.Handled = true;
                this.Designer.ShouldStillAllowRubberBandEvenIfMouseLeftButtonDownIsHandled = false;
            }
 
            if (e.RightButton == MouseButtonState.Pressed)
            {
                rightMouseClick = true;
                this.lastMouseDownPoint = e.GetPosition(this);
                if (!Keyboard.IsKeyDown(Key.LeftCtrl) && !Keyboard.IsKeyDown(Key.RightCtrl))
                {
                    shouldSetFocus = Keyboard.FocusedElement != this;
                }
                else
                {
                    rightMouseClickWithCtrlDown = true;
                }
 
                e.Handled = true;
                shouldUpdateLastActivationPoint = true;
            }
 
            System.Diagnostics.Debug.WriteLine(
                string.Format(CultureInfo.InvariantCulture, "{0} ) WorkflowViewElement.OnMouseDown ({1}, shouldSetFocus {2}, mouseCaptured {3}, raisedBy {4})",
                DateTime.Now.ToLocalTime(), this.GetType().Name, shouldSetFocus, this.IsMouseCaptured, e.OriginalSource));
 
            base.OnMouseDown(e);
 
            if (shouldSetFocus)
            {
                System.Diagnostics.Debug.WriteLine(
                    string.Format(CultureInfo.InvariantCulture, "{0} ) WorkflowViewElement.OnMouseDown.SetFocus ({1})",
                    DateTime.Now.ToLocalTime(), this.GetType().Name));
                //attempt to set focused and keyboard focused element to new designer
                Keyboard.Focus(this);
            }
 
            bool isSelected = this.Context.Items.GetValue<Selection>().SelectedObjects.Contains<ModelItem>(this.ModelItem);
 
            if (shouldToggle)
            {
                if (!isSelected)
                {
                    Selection.Toggle(this.Context, this.ModelItem);
                }
                else
                {
                    this.shouldChangeSelectionOnMouseUp = true;
                }
            }
            else
            {
                if (!rightMouseClickWithCtrlDown)
                {
                    if (rightMouseClick)
                    {
                        // if it's right mouse click without ctrl, change selection only if the current item is not selected.
                        bool alreadySelected = false;
                        Selection selection = this.Context.Items.GetValue<Selection>();
                        foreach (ModelItem item in selection.SelectedObjects)
                        {
                            if (item == this.ModelItem)
                            {
                                alreadySelected = true;
                                break;
                            }
                        }
                        if (!alreadySelected)
                        {
                            Selection.SelectOnly(this.Context, this.ModelItem);
                        }
                    }
                    else if (this.leftMouseButtonDown)
                    {
                        if (!isSelected)
                        {
                            Selection.SelectOnly(this.Context, this.ModelItem);
                        }
                        else
                        {
                            this.shouldChangeSelectionOnMouseUp = true;
                        }
                    }
                }
            }
 
            this.rightMouseClickWithCtrlDown = false;
            this.rightMouseClick = false;
 
            if (shouldUpdateLastActivationPoint)
            {
                this.lastActivationElement = e.OriginalSource as UIElement;
            }
 
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                this.Designer.lastClickedDesigner = this;
            }
        }
 
        bool AllowDragging()
        {
            Selection selection = this.Context.Items.GetValue<Selection>();
            
            if (!this.DraggingMultipleItemsEnabled)
            {
                if (selection.SelectionCount != 1)
                {
                    return false;
                }
            }
 
            return selection.SelectedObjects.All<ModelItem>((p) =>
                {
                    return p != null && p != p.Root && p.View != null && ((WorkflowViewElement)p.View).IsVisible &&
                        (DragDropHelper.GetCompositeView((WorkflowViewElement)p.View) as ICompositeView) != null;
                });
        }
 
        protected override void OnMouseMove(MouseEventArgs e)
        {
            // if model item is removed, uncapture mouse and return
            if (this.ModelItem != null && this.ModelItem != this.ModelItem.Root && this.ModelItem.Parent == null)
            {
                if (this.IsMouseCaptured)
                {
                    Mouse.Capture(null);
                }
                return;
            }
 
            if (e.LeftButton == MouseButtonState.Pressed &&
               this.leftMouseButtonDown &&
               this.ModelItem != null &&
               this.IsMouseOnDragHandle(this.lastMouseDownPoint) &&
               e.Timestamp - this.lastMouseButtonDownTimeStamp > 100)
            {
                //get new position
                Point newPosition = e.GetPosition(this);
                //calculate distance
                Vector difference = newPosition - this.lastMouseDownPoint;
                if (Math.Abs(difference.X) > SystemParameters.MinimumHorizontalDragDistance ||
                    Math.Abs(difference.Y) > SystemParameters.MinimumVerticalDragDistance)
                {
                    Selection selection = this.Context.Items.GetValue<Selection>();
                    // If the current model item is not selected, add it to the selection. 
                    if (!selection.SelectedObjects.Contains(this.ModelItem))
                    {
                        if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
                        {
                            Selection.Toggle(this.Context, this.ModelItem);
                        }
                        else
                        {
                            Selection.SelectOnly(this.Context, this.ModelItem);
                        }
                    }
 
                    if (this.AllowDragging())
                    {
                        //if mouse is caputured - release capture, drag&drop infrastructure will take care now for tracking mouse move
                        if (this.IsMouseCaptured)
                        {
                            Mouse.Capture(null);
                            StartDragging();
                        }
 
                        this.leftMouseButtonDown = false;
                        e.Handled = true;
                    }
                }
            }
            
            base.OnMouseMove(e);
        }
 
        private bool IsMouseOnDragHandle(Point mousePoint)
        {
            if (this.DragHandle != null)
            {
                GeneralTransform transform = this.DragHandle.TransformToAncestor(this);
                Point dragHandleLocation = transform.Transform(new Point(0, 0));
                Rect dragHandleRect = new Rect(dragHandleLocation, new Size(this.DragHandle.ActualWidth, this.DragHandle.ActualHeight));
                if (dragHandleRect.Contains(mousePoint))
                {
                    return true;
                }
            }
            return false;
        }
 
        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine(
                CultureInfo.InvariantCulture,
                string.Format(CultureInfo.CurrentUICulture, "{0} ) WorkflowViewElement.OnMouseUp ({1}, mouseCaptured {2})",
                DateTime.Now.ToLocalTime(), this.GetType().Name, this.IsMouseCaptured));
 
            if (this.leftMouseButtonDown)
            {
                if (this.IsMouseCaptured)
                {
                    Mouse.Capture(null);
                }
 
                if (!this.Designer.SuppressSelectionOnMouseUp && this.shouldChangeSelectionOnMouseUp)
                {
                    if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
                    {
                        Selection.Toggle(this.Context, this.ModelItem);
                    }
                    else
                    {
                        Selection.SelectOnly(this.Context, this.ModelItem);
                    }
                }
 
                this.leftMouseButtonDown = false;
                e.Handled = true;
            }
 
            this.shouldChangeSelectionOnMouseUp = false;
            base.OnMouseUp(e);
        }
 
        void OnBreadCrumbTimerElapsed(object sender, ElapsedEventArgs e)
        {
            this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
                {
                    this.breadCrumbTimer.Stop();
                    this.MakeRootDesigner();
                }));
        }
 
        void OnDrag(DragEventArgs e)
        {
            if (!e.Handled)
            {
                e.Effects = DragDropEffects.None;
                e.Handled = true;
            }
        }
 
        // This is to set the cursor to the forbidden icon when dragging to the designer.
        // It doesn't affect the drag-drop behavior of components that have AllowDrop == ture within the designer.
        protected override void OnDragEnter(DragEventArgs e)
        {
            this.OnDrag(e);
            base.OnDragEnter(e);
        }
 
        // This is to set the cursor to the forbidden icon when dragging within the designer.
        // It doesn't affect the drag-drop behavior of components that have AllowDrop == ture within the designer.
        protected override void OnDragOver(DragEventArgs e)
        {
            this.OnDrag(e);
            base.OnDragOver(e);
        }
 
        protected override void OnPreviewDragEnter(DragEventArgs e)
        {
            if (this.ShowExpanded == false)
            {
                this.breadCrumbTimer.Start();
            }
            base.OnPreviewDragEnter(e);
        }
 
        protected override void OnPreviewDragLeave(DragEventArgs e)
        {
            this.breadCrumbTimer.Stop();
            base.OnPreviewDragLeave(e);
        }
 
        protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
        {
            this.breadCrumbTimer.Stop();
            base.OnPreviewMouseUp(e);
        }
 
        static void OnModelItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            WorkflowViewElement viewElement = (WorkflowViewElement)dependencyObject;
            viewElement.OnModelItemChanged(e.NewValue);
        }
 
        void BeginDropAnimation(WorkflowViewElement target)
        {
            DropAnimation opacityAnimation = new DropAnimation();
            target.BeginAnimation(FrameworkElement.OpacityProperty, opacityAnimation);
        }
 
        [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DoNotCatchGeneralExceptionTypes,
            Justification = "Catching all exceptions to avoid VS Crash.")]
        [SuppressMessage("Reliability", "Reliability108",
            Justification = "Catching all exceptions to avoid VS crash.")]
 
        void StartDragging()
        {
            try
            {
                using (ModelEditingScope editingScope = this.ModelItem.BeginEdit(SR.MoveEditingScopeDescription, true))
                {
                    HashSet<WorkflowViewElement> draggedViews = new HashSet<WorkflowViewElement>();
                    Dictionary<ModelItem, ICompositeView> sourceContainers = new Dictionary<ModelItem, ICompositeView>();
                    HashSet<ICompositeView> compViewSet = new HashSet<ICompositeView>();
                    Selection selection = this.Context.Items.GetValue<Selection>();
                    IEnumerable<ModelItem> selectedObjects = selection.SelectedObjects;
                    IEnumerable<ModelItem> modelItemsToDrag = DragDropHelper.GetModelItemsToDrag(selectedObjects);
                    
                    // Save the source containers for the dragged items
                    foreach (ModelItem modelItem in modelItemsToDrag)
                    {
                        WorkflowViewElement view = (WorkflowViewElement)modelItem.View;
                        draggedViews.Add(view);
                        ICompositeView container = DragDropHelper.GetCompositeView(view) as ICompositeView;
                        sourceContainers.Add(modelItem, container);
                        // If Add returns true => the container is added the first time, which is always ok
                        // If Add returns false => the container is added more than once
                        //    it must be a IMultipleDragEnabledCompositeView, otherwise, return, because 
                        //    we don't support dragging from ICompositeView.
                        if (!compViewSet.Add(container) && !(container is IMultipleDragEnabledCompositeView))
                        {
                            return;
                        }
                    }
                    
                    // Calculate the anchor point for the dragged items
                    Point relativeLocation = GetRelativeLocation(draggedViews);
                    Point referencePoint = this.lastMouseDownPoint;
                    referencePoint.Offset(relativeLocation.X, relativeLocation.Y);
 
 
                    DataObject dataObject = DragDropHelper.DoDragMoveImpl(draggedViews, referencePoint);
                    IEnumerable<WorkflowViewElement> movedViewElements = DragDropHelper.GetDragDropMovedViewElements(dataObject);
 
                    // once drag drop is done make sure the CompositeView is notified of the change in data
                    if (movedViewElements != null)
                    {
                        Dictionary<ICompositeView, List<ModelItem>> containerMovedModelItemList = new Dictionary<ICompositeView, List<ModelItem>>();
                        
                        // Create containerMovedModelItemList
                        foreach (WorkflowViewElement view in movedViewElements)
                        {
                            ICompositeView compView = DragDropHelper.GetCompositeView(view) as ICompositeView;
                            Fx.Assert(compView != null, "not an ICompositeView");
                            if (!containerMovedModelItemList.ContainsKey(compView))
                            {
                                containerMovedModelItemList.Add(compView, new List<ModelItem>());
                            }
                            containerMovedModelItemList[compView].Add(view.ModelItem);
                        }
 
                        // Call OnItemsMoved to notify the source container.
                        foreach (KeyValuePair<ICompositeView, List<ModelItem>> pair in containerMovedModelItemList)
                        {
                            if (pair.Key is IMultipleDragEnabledCompositeView)
                            {
                                ((IMultipleDragEnabledCompositeView)pair.Key).OnItemsMoved(pair.Value);
                            }
                            else
                            {
                                if (pair.Value.Count >= 2)
                                {
                                    throw FxTrace.Exception.AsError(
                                        new InvalidOperationException(SR.Error_MovingMoreThanOneItemsFromICompositeView));
                                }
                                pair.Key.OnItemMoved(pair.Value[0]);
                            }
                        }
 
                        // animation
                        foreach (WorkflowViewElement view in movedViewElements)
                        {
                            BeginDropAnimation(view);
                        }
                    }
                    // the drop target is using old DragDropHelper API and did not set the moved view elements
                    else
                    {
                        DragDropEffects executedDragDropEffect = DragDropHelper.GetDragDropCompletedEffects(dataObject);
                        if (executedDragDropEffect == DragDropEffects.Move)
                        {
                            if (modelItemsToDrag.Count() == 1)
                            {
                                ModelItem movedItem = modelItemsToDrag.First<ModelItem>();
                                sourceContainers[movedItem].OnItemMoved(movedItem);
                                BeginDropAnimation((WorkflowViewElement)movedItem.View);
                            }
                            else
                            {
                                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.DraggingMulitpleItemsError));
                            }
                        }
                    }
                    editingScope.Complete();
 
                    bool dropHappened = movedViewElements != null
                        || DragDropHelper.GetDragDropCompletedEffects(dataObject) == DragDropEffects.Move;
                    if (dropHappened)
                    {
                        // add the selected objects back into selection.
                        this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)(() =>
                        {
 
                            foreach (ModelItem item in selectedObjects)
                            {
                                // We need only the first one
                                IInputElement viewToFocus = item == null ? null : item.View as IInputElement;
                                if (viewToFocus != null)
                                {
                                    Keyboard.Focus(viewToFocus);
                                    break;
                                }
                            }
                            this.Context.Items.SetValue(new Selection(selectedObjects));
                        }));
                    }
                }
            }
            catch (Exception e)
            {
                ErrorReporting.ShowErrorMessage(e.Message);
            }
        }
 
        private Point GetRelativeLocation(IEnumerable<WorkflowViewElement> draggedViews)
        {
            HashSet<WorkflowViewElement> viewElements = new HashSet<WorkflowViewElement>(draggedViews);
            if (!viewElements.Contains(this))
            {
                viewElements.Add(this);
            }
            Dictionary<WorkflowViewElement, Point> locations = DragDropHelper.GetViewElementRelativeLocations(viewElements);
            return locations[this];
        }
 
        internal void NotifyContextMenuLoaded(ContextMenu menu)
        {
            if (null != menu)
            {
                OnContextMenuLoaded(menu);
            }
        }
 
        class WorkflowViewElementAutomationPeer : UIElementAutomationPeer
        {
            WorkflowViewElement owner;
 
            public WorkflowViewElementAutomationPeer(WorkflowViewElement owner)
                : base(owner)
            {
                this.owner = owner;
            }
 
            protected override AutomationControlType GetAutomationControlTypeCore()
            {
                return AutomationControlType.Custom;
            }
 
 
            protected override string GetAutomationIdCore()
            {
                string automationId = this.GetClassNameCore();
                string automationIdVariablePartMemberName = owner.GetAutomationIdMemberName();
                if (!string.IsNullOrEmpty(automationIdVariablePartMemberName))
                {
                    ModelItem modelItem = this.owner.ModelItem;
                    string variablePartOfAutomationId = string.Empty;
                    if (modelItem != null)
                    {
                        ModelProperty property = modelItem.Properties[automationIdVariablePartMemberName];
                        Fx.Assert(property != null, "property to use for Automation ID variable part missing ? are you using the right property Name?");
                        if (property.Value != null)
                        {
                            variablePartOfAutomationId = property.Value.GetCurrentValue().ToString();
                        }
                    }
                    automationId = variablePartOfAutomationId + "(" + this.GetClassNameCore() + ")";
                }
                return automationId;
            }
 
            protected override string GetNameCore()
            {
                Type itemType = null;
                if (this.owner.ModelItem != null)
                {
                    itemType = this.owner.ModelItem.ItemType;
                }
                else
                {
                    itemType = this.owner.GetType();
                }
 
                if (itemType.IsGenericType)
                {
                    //append the argument types for generic types
                    //we expect the single level of generic is sufficient for the screen reader, so we're no going into
                    //nesting of generic types
                    Type[] argumentTypes = itemType.GetGenericArguments();
                    StringBuilder name = new StringBuilder(itemType.Name);
                    name.Append('[');
                    foreach (Type argument in argumentTypes)
                    {
                        name.Append(argument.Name);
                        name.Append(',');
                    }
                    name.Replace(',', ']', name.Length - 1, 1);
                    return name.ToString();
                }
                else
                {
                    return itemType.Name;
                }
            }
 
            protected override string GetHelpTextCore()
            {
                return this.owner.GetAutomationHelpText();
            }
 
            protected override string GetItemStatusCore()
            {
                return this.owner.GetAutomationItemStatus();
            }
 
            protected override string GetClassNameCore()
            {
                return this.owner.GetType().Name;
            }
        }
    }
 
}