File: cdf\src\NetFx40\Tools\System.Activities.Presentation\System\Activities\Presentation\WorkflowItemPresenter.cs
Project: ndp\System.Data.csproj (System.Data)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
 
namespace System.Activities.Presentation
{
    using System.Activities.Presentation.Hosting;
    using System.Activities.Presentation.Internal.PropertyEditing;
    using System.Activities.Presentation.Model;
    using System.Activities.Presentation.Services;
    using System.Activities.Presentation.View;
    using System.Activities.Statements;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime;
    using System.Windows;
    using System.Windows.Automation.Peers;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Shapes;
    using System.Windows.Threading;
    using System.Timers;
 
    enum InsertionPosition
    {
        Before,
        After,
        None,
    }
 
    class AutoWrapEventArgs : EventArgs
    {
        public Activity ExistingActivity { get; set; }
        public InsertionPosition InsertionPosition { get; set; }
        public List<Activity> ActivitiesToBeInserted { get; set; }
    }
 
    // This class provides a visual edit box to edit ModelItems. Textbox offers to edit strings, ints as a TextBlock and a cursor visually,
    // The workflowitempresenter edits modelitems by picking their view using the view service. It presents s the visual for the modelitem
    // pointe by Item property if it is set, it shows the hint text if the property is not set. It allows the associated item to be deleted 
    // visually , and removes the reference to Item when deleted. It also allows droping ModelItems, to set the Item property to the dropped 
    // item.
    public sealed class WorkflowItemPresenter : ContentControl, ICompositeView
    {
        public static readonly DependencyProperty HintTextProperty =
            DependencyProperty.Register("HintText", typeof(string), typeof(WorkflowItemPresenter), new UIPropertyMetadata(String.Empty));
 
        public static readonly DependencyProperty ItemProperty =
            DependencyProperty.Register("Item", typeof(ModelItem), typeof(WorkflowItemPresenter), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(WorkflowItemPresenter.OnItemChanged)));
 
        public static readonly DependencyProperty AllowedItemTypeProperty =
            DependencyProperty.Register("AllowedItemType", typeof(Type), typeof(WorkflowItemPresenter), new UIPropertyMetadata(typeof(object)));
 
        public static readonly DependencyProperty IsDefaultContainerProperty =
            DependencyProperty.Register("IsDefaultContainer", typeof(bool), typeof(WorkflowItemPresenter), new UIPropertyMetadata(false));
 
        public static readonly DependencyProperty DroppingTypeResolvingOptionsProperty =
            DependencyProperty.Register("DroppingTypeResolvingOptions", typeof(TypeResolvingOptions), typeof(WorkflowItemPresenter));
 
        SpacerHelper spacerHelper;
        Grid contentGrid;
        StackPanel stackPanel;
        Grid containerGrid;
        TextBlock text;
        EditingContext context = null;
        bool shouldSetFocus = false;
        bool isItemPastedOrDropped = false;
 
        public WorkflowItemPresenter()
        {
            this.text = new TextBlock();
            this.text.SetBinding(TextBlock.TextProperty, "HintText");
            this.text.DataContext = this;
            this.text.HorizontalAlignment = HorizontalAlignment.Center;
            this.text.VerticalAlignment = VerticalAlignment.Center;
            this.text.Foreground = new SolidColorBrush(SystemColors.GrayTextColor);
            this.text.FontStyle = FontStyles.Italic;
 
            this.contentGrid = new Grid();
            this.contentGrid.Background = Brushes.Transparent;
            this.contentGrid.VerticalAlignment = VerticalAlignment.Center;
            this.contentGrid.Children.Add(text);
 
            this.stackPanel = new StackPanel();
            this.stackPanel.HorizontalAlignment = HorizontalAlignment.Center;
            this.stackPanel.VerticalAlignment = VerticalAlignment.Center;
            this.stackPanel.Children.Add(contentGrid);
 
            this.containerGrid = new Grid();
            this.containerGrid.Children.Add(stackPanel);
            this.containerGrid.Background = Brushes.Transparent;
        }
        
        internal bool AutoWrapInSequenceEnabled
        {
            get
            {
                // Don't allow auto wrap in sequence if allowed item isn't of type of Activity
                return this.Context != null
                    && this.Context.Services.GetService<DesignerConfigurationService>().AutoSurroundWithSequenceEnabled
                    && typeof(Activity).IsAssignableFrom(this.AllowedItemType); 
            }
        }
 
        Activity MyActivity
        {
            get
            {
                return this.Item == null ? null :
                    this.Item.GetCurrentValue() as Activity;
            }
        }
 
        List<Activity> ObjectList2ActivityList(IEnumerable<object> droppedObjects)
        {
            List<Activity> activityList = new List<Activity>();
            foreach (object droppedObject in droppedObjects)
            {
                object modelObject = droppedObject;
                if (modelObject is ModelItem)
                {
                    modelObject = ((ModelItem)droppedObject).GetCurrentValue();
                }
                if (modelObject is Activity)
                {
                    activityList.Add(modelObject as Activity);
                }
                else
                {
                    Fx.Assert("A non-activity is found in the list, there must be something seriously wrong!");
                }
            }
            return activityList;
        }
 
        private List<WorkflowViewElement> ObjectList2WorkflowViewElementList(IEnumerable<object> droppedObjects)
        {
            List<WorkflowViewElement> movedViewElements = new List<WorkflowViewElement>();
            foreach (object droppedObject in droppedObjects)
            {
                if (droppedObject is ModelItem && ((ModelItem)droppedObject).View != null)
                {
                    WorkflowViewElement view = (WorkflowViewElement)((ModelItem)droppedObject).View;
                    WorkflowItemPresenter container = DragDropHelper.GetCompositeView(view) as WorkflowItemPresenter;
                    if (container != this)
                    {
                        movedViewElements.Add(view);
                    }
                }
            }
            return movedViewElements;
        }
 
        // return true if really something is dropped, otherwise, false.
        bool DoAutoWrapDrop(InsertionPosition insertionPos, IEnumerable<object> droppedObjects)
        {
            List<Activity> activityList = ObjectList2ActivityList(droppedObjects);
            if (activityList.Count == 0)
            {
                return false;
            }
 
            AutoWrapEventArgs args = new AutoWrapEventArgs()
            {
                InsertionPosition = insertionPos,
                ExistingActivity = this.MyActivity,
                ActivitiesToBeInserted = activityList
            };
 
            using (ModelEditingScope scope = this.Context.Services.GetService<ModelService>().Root.BeginEdit(SR.WrapInSequenceDescription))
            {
                ModelItem sequenceActivity = WorkflowItemPresenter.AutoWrapInSequenceHandler(this.Context, args);
                if (this.UpdateItem(sequenceActivity, true))
                {
                    scope.Complete();
                    return true;
                }
                else
                {
                    scope.Revert();
                    return false;
                }
            }
        }
 
        bool DoAutoWrapDrop(InsertionPosition insertionPos, DragEventArgs e, IList<object> droppedObjects = null)
        {
            if (droppedObjects == null)
            {
                ModelTreeManager manager = this.Context.Services.GetRequiredService<ModelTreeManager>();
                EditingScope editingScope = null;
 
                try
                {
                    editingScope = ModelItemHelper.TryCreateImmediateEditingScope(manager, SR.WrapInSequenceDescription);
 
                    droppedObjects = this.GetSortedObjectList(e);
 
                    if (!this.DoAutoWrapDrop(insertionPos, droppedObjects))
                    {
                        return false;
                    }
 
                    if (editingScope != null)
                    {
                        editingScope.Complete();
                    }
                }
                finally
                {
                    if (editingScope != null)
                    {
                        editingScope.Dispose();
                        editingScope = null;
                    }
                }
            }
            else
            {
                if (!this.DoAutoWrapDrop(insertionPos, droppedObjects))
                {
                    return false;
                }
            }
 
            if (!DragDropHelper.IsDraggingFromToolbox(e))
            {
                List<WorkflowViewElement> movedViewElements = ObjectList2WorkflowViewElementList(droppedObjects);
                DragDropHelper.SetDragDropMovedViewElements(e, movedViewElements);
 
                //Backward compatibility for 4.0
                if (droppedObjects.Count == 1 && movedViewElements.Count == 1)
                {
                    #pragma warning disable 618
                    DragDropHelper.SetDragDropCompletedEffects(e, DragDropEffects.Move);
                    #pragma warning restore 618
                }
            }
            else
            {
                Fx.Assert(droppedObjects.Count == 1, "Dropping from Toolbox with count != 1");
 
                // Set focus if it is dropping from ToolBox.
                // In common drag/drop, the selection setting is done at the end of
                // StartDragging().
                if (this.Item == null)
                {
                    return true;
                }
                
                Fx.Assert(typeof(Sequence).IsAssignableFrom(this.Item.ItemType), 
                    "Auto Wrap didn't add a sequence. Is Item.Properties[\"Activities\"] still correct?");
                foreach (ModelItem item in this.Item.Properties["Activities"].Collection)
                {
                    // Find the ModelItem whose value is an activity from Toolbox.
                    if (item.GetCurrentValue() == droppedObjects[0])
                    {
                        this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)(() =>
                        {
                            item.Focus();
                        }));
                        break;
                    }
                }
            }
 
            return true;
        }
 
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            this.AllowDrop = true;
            this.Content = containerGrid;
            ICompositeViewEvents containerEvents = null;
            bool isDefault = false;
 
            this.Loaded += (s, eventArgs) =>
            {
                isDefault = this.IsDefaultContainer;
                DependencyObject parent = VisualTreeHelper.GetParent(this);
                while (null != parent && !typeof(ICompositeViewEvents).IsAssignableFrom(parent.GetType()))
                {
                    parent = VisualTreeHelper.GetParent(parent);
                }
                containerEvents = parent as ICompositeViewEvents;
                if (null != containerEvents)
                {
                    if (isDefault)
                    {
                        containerEvents.RegisterDefaultCompositeView(this);
                    }
                    else
                    {
                        containerEvents.RegisterCompositeView(this);
                    }
                }
                this.shouldSetFocus = true;
 
                if (this.AutoWrapInSequenceEnabled)
                {
                    // spacer and placer holder
                    this.spacerHelper = new SpacerHelper(this);
                }
            };
 
            this.Unloaded += (s, eventArgs) =>
            {
                if (null != containerEvents)
                {
                    if (isDefault)
                    {
                        containerEvents.UnregisterDefaultCompositeView(this);
                    }
                    else
                    {
                        containerEvents.UnregisterCompositeView(this);
                    }
                }
                this.shouldSetFocus = false;
 
                if (this.AutoWrapInSequenceEnabled)
                {
                    if (this.spacerHelper != null)
                    {
                        this.spacerHelper.Unload();
                        this.spacerHelper = null;
                    }
                }
            };
        }
 
        public string HintText
        {
            get { return (string)GetValue(HintTextProperty); }
            set { SetValue(HintTextProperty, value); }
        }
 
        [Fx.Tag.KnownXamlExternal]
        public ModelItem Item
        {
            get { return (ModelItem)GetValue(ItemProperty); }
            set { SetValue(ItemProperty, value); }
        }
 
        public Type AllowedItemType
        {
            get { return (Type)GetValue(AllowedItemTypeProperty); }
            set { SetValue(AllowedItemTypeProperty, value); }
        }
 
        [Fx.Tag.KnownXamlExternal]
        public TypeResolvingOptions DroppingTypeResolvingOptions
        {
            get { return (TypeResolvingOptions)GetValue(DroppingTypeResolvingOptionsProperty); }
            set { SetValue(DroppingTypeResolvingOptionsProperty, value); }
        }
 
        EditingContext Context
        {
            get
            {
                if (context == null)
                {
                    IModelTreeItem modelTreeItem = this.Item as IModelTreeItem;
                    if (modelTreeItem != null)
                    {
                        this.context = modelTreeItem.ModelTreeManager.Context;
                    }
                    else // There is no ModelItem yet, try to walk up the tree to find a WorkflowViewElement.
                    {
                        WorkflowViewElement parentViewElement = GetParentWorkflowViewElement();
                        if (parentViewElement != null)
                        {
                            this.context = parentViewElement.Context;
                        }
                    }
                }
                return context;
            }
        }
 
 
        public bool IsDefaultContainer
        {
            get { return (bool)GetValue(IsDefaultContainerProperty); }
            set { SetValue(IsDefaultContainerProperty, value); }
        }
 
        protected override void OnRender(DrawingContext drawingContext)
        {
            CutCopyPasteHelper.RegisterWithParentViewElement(this);
            base.OnRender(drawingContext);
        }
 
        private WorkflowViewElement GetParentWorkflowViewElement()
        {
            // Walk the logic tree first.
            FrameworkElement parent = (FrameworkElement)this.Parent;
            while (parent != null && !(parent is WorkflowViewElement))
            {
                parent = parent.Parent as FrameworkElement;
            }
            WorkflowViewElement result = parent as WorkflowViewElement;
            // If not found, walk the visual tree.
            if (null == result)
            {
                parent = VisualTreeHelper.GetParent(this) as FrameworkElement;
                while (parent != null && !(parent is WorkflowViewElement))
                {
                    parent = VisualTreeHelper.GetParent(parent) as FrameworkElement;
                }
                result = parent as WorkflowViewElement;
            }
            return result;
        }
 
 
        static void OnItemChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            WorkflowItemPresenter control = (WorkflowItemPresenter)dependencyObject;
            control.OnItemChanged(e);
        }
 
        void OnItemChanged(DependencyPropertyChangedEventArgs e)
        {
            object newItem = e.NewValue;
            PopulateContent();
 
            if (newItem != null)
            {
                //We want to set the selection only if the item is dropped or pasted. 
                //We cannot set the selection in UpdateItem, since while pasting that would still be in EditingScope and this.Item will be null.
                if (this.isItemPastedOrDropped)
                {
                    Fx.Assert(this.Item != null, "Item cannot be null");
                    // If we are currently moving from somewhere else to a WorkflowItemPresenter, the currently 
                    // focusing view element will be removed, we need to set the keyboard focus explicitly to 
                    // avoid WPF FocusManager to focus on an element, leading to flashing effect.
                    Keyboard.Focus((UIElement)this.Item.View);
                    this.isItemPastedOrDropped = false;
                }
                if (this.shouldSetFocus)
                {
                    this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)(() =>
                    {
                        // check for Item == null, we found an interesting bug, where the user 
                        // could drop something in here, and undo the change before the code below
                        // could execute
                        if (this.Item != null)
                        {
                            UIElement view = (UIElement)(this.Item.View);
                            if (view != null)
                            {
                                Keyboard.Focus(view);
                                Selection.SelectOnly(this.Context, this.Item);
                            }
                        }
                        //this.shouldSetFocus = false;
                    }));
                }
            }
            else
            {
                // remove the selection if the previous value was selected.
                if (this.Context != null)
                {
                    if (this.Context.Items.GetValue<Selection>().SelectedObjects.Contains(e.OldValue))
                    {
                        this.Context.Items.SetValue(new Selection(new ModelItem[] { }));
                    }
                }
            }
        }
 
 
        void PopulateContent()
        {
            if (this.Item != null)
            {
                VirtualizedContainerService containerService = this.Context.Services.GetService<VirtualizedContainerService>();
                UIElement itemView = containerService.GetContainer(this.Item, this);
                this.contentGrid.Children.Clear();
                this.contentGrid.Children.Add(itemView);
            }
            else
            {
                contentGrid.Children.Clear();
                contentGrid.Children.Add(text);
            }
        }
 
        bool UpdateItem(object newItem)
        {
            return UpdateItem(newItem, false);
        }
 
        bool UpdateItem(object newItem, bool allowReplaceExistingActivity)
        {
            bool updateSucceeded = false;
            ModelItem newModelItem = newItem as ModelItem;
            if (this.Item == null || allowReplaceExistingActivity)
            {
                if (newModelItem == null && newItem != null)
                {
                    // try to wrap the droppedObject in  a ModelItem.
                    ModelServiceImpl modelService = (ModelServiceImpl)this.Context.Services.GetService<ModelService>();
                    newModelItem = modelService.WrapAsModelItem(newItem);
                }
                if (this.CanUpdateItem(newModelItem))
                {
                    // In order to allow for model updates that happens during the model item is drop, this is all done in an atomic unit.
                    using (ModelEditingScope editingScope = this.Context.Services.GetService<ModelService>().Root.BeginEdit(SR.PropertyChangeEditingScopeDescription))
                    {
                        this.Item = newModelItem;
                        editingScope.Complete();
                    }
                    updateSucceeded = true;
                    this.isItemPastedOrDropped = true;
                }
            }
            return updateSucceeded;
 
        }
 
        bool CanUpdateItem(ModelItem newModelItem)
        {
            return null != newModelItem
                && TypeUtilities.IsTypeCompatible(newModelItem.ItemType, this.AllowedItemType)
                && !this.IsInParentChain(newModelItem);
        }
 
        List<object> GetSortedObjectList(DragEventArgs args)
        {
            IEnumerable<object> droppedObjects = DragDropHelper.GetDroppedObjects(this, args, this.context);
            return DragDropHelper.SortSelectedObjects(droppedObjects);
        }
 
        bool DoSingleDrop(object droppedObject, DragEventArgs args)
        {
            if (UpdateItem(droppedObject))
            {
                args.Handled = true;
                #pragma warning disable 618
                DragDropHelper.SetDragDropCompletedEffects(args, DragDropEffects.Move);
                #pragma warning restore 618
                if (droppedObject is ModelItem && ((ModelItem)droppedObject).View != null)
                {
                    DragDropHelper.SetDragDropMovedViewElements(args, new WorkflowViewElement[] { ((ModelItem)droppedObject).View as WorkflowViewElement });
                }
 
                return true;
            }
 
            return false;
        }
 
        protected override void OnDrop(DragEventArgs e)
        {
            ModelTreeManager manager = this.Context.Services.GetService<ModelTreeManager>();
 
            // When dragging from toolbox:
            //     editingScope should not be null
            //     there should only be one item
            // When dragging from canvas:
            //     editingScope should be null
            // Call editingScope.Complete() to commit changes, otherwise the editing scope will be aborted
            using (EditingScope editingScope = ModelItemHelper.TryCreateImmediateEditingScope(manager, SR.PropertyChangeEditingScopeDescription))
            {
                List<object> droppedObjects = this.GetSortedObjectList(e);
#pragma warning disable 618
                DragDropHelper.SetDragDropCompletedEffects(e, DragDropEffects.None);
#pragma warning restore 618
                if (droppedObjects == null || droppedObjects.Count == 0)
                {
                    return;
                }
                if (droppedObjects.Count == 1)
                {
                    if (this.DoSingleDrop(droppedObjects[0], e))
                    {
                        if (editingScope != null)
                        {
                            editingScope.Complete();
                        }
                    }
                    return;
                }
                else
                {
                    // multi drop
                    Fx.Assert(editingScope == null, "editingScope should be null for dragging from canvas.");
                    this.DoAutoWrapDrop(InsertionPosition.None, e, droppedObjects);
                }
                base.OnDrop(e);
            }
        }
 
        void OnDrag(DragEventArgs e)
        {
            if (!e.Handled)
            {
                this.UpdateEffects(e);
                e.Handled = true;
            }
        }
 
        void UpdateEffects(DragEventArgs args)
        {
            if (!DragDropHelper.AllowDrop(args.Data, this.Context, this.AllowedItemType))
            {
                args.Effects = DragDropEffects.None;
            }
        }
 
        protected override void OnDragEnter(DragEventArgs e)
        {
            this.OnDrag(e);
            base.OnDragEnter(e);
        }
 
        protected override void OnDragOver(DragEventArgs e)
        {
            this.OnDrag(e);
            base.OnDragOver(e);
        }
 
        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            // do not move focus if it's a ctrl right click.
            if (e.RightButton == MouseButtonState.Pressed)
            {
                if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
                {
                    e.Handled = true;
                    base.OnMouseDown(e);
                    return;
                }
            }
 
            // Schedule the Keyboard.Focus command to let it execute later than WorkflowViewElement.OnMouseDown, 
            // where WorkflowViewElement will move the keyboard focus on itself
            this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
            {
                Keyboard.Focus((FrameworkElement)this);
            }));
            base.OnMouseDown(e);
        }
 
        private bool IsInParentChain(ModelItem droppedModelItem)
        {
            bool isInParentChain = false;
            // start with immediate workflowviewElement outside this.
            WorkflowViewElement parentViewElement = GetParentWorkflowViewElement();
            if (parentViewElement != null)
            {
                ModelItem parentModelItem = parentViewElement.ModelItem;
                while (parentModelItem != null)
                {
                    if (parentModelItem == droppedModelItem)
                    {
                        isInParentChain = true;
                        break;
                    }
                    parentModelItem = parentModelItem.Parent;
                }
            }
            return isInParentChain;
        }
 
        void DeleteItem()
        {
            this.Item = null;
            this.PopulateContent();
        }
 
        void ICompositeView.OnItemMoved(ModelItem modelItem)
        {
            if (this.Item == modelItem)
            {
                this.Item = null;
            }
        }
 
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new WorkflowItemPresenterAutomationPeer(this);
        }
 
 
        object ICompositeView.OnItemsCut(List<ModelItem> itemsToCut)
        {
            Fx.Assert(itemsToCut.Count == 1, "Only one item can be cut");
            Fx.Assert(itemsToCut[0].Equals(this.Item), "Only one item can be cut.");
            this.DeleteItem();
            return null;
        }
 
        object ICompositeView.OnItemsCopied(List<ModelItem> itemsToCopy)
        {
            return null;
        }
 
        void ICompositeView.OnItemsPasted(List<object> itemsToPaste, List<object> metaData, Point pastePoint, WorkflowViewElement pastePointReference)
        {
            if (itemsToPaste.Count == 1)
            {
                // Single Paste
                UpdateItem(itemsToPaste[0]);
            }
            else
            {
                // Mutiple Paste.
                IList<object> sortedList = CutCopyPasteHelper.SortFromMetaData(itemsToPaste, metaData);
                Fx.Assert(this.Item == null, "multi-paste on item != null is not supported now");
                this.DoAutoWrapDrop(InsertionPosition.None, sortedList); 
            }
        }
 
        void ICompositeView.OnItemsDelete(List<ModelItem> itemsToDelete)
        {
            if (null != itemsToDelete && itemsToDelete.Contains(this.Item))
            {
                this.DeleteItem();
            }
        }
 
 
        bool ICompositeView.CanPasteItems(List<object> itemsToPaste)
        {
            return null != itemsToPaste &&
                itemsToPaste.Count > 0 &&
                null != itemsToPaste[0] &&
                null == this.Item &&
                ((itemsToPaste[0] is ModelItem && this.CanUpdateItem((ModelItem)itemsToPaste[0])) ||
                (itemsToPaste[0] is Type && this.AllowedItemType.IsAssignableFrom((Type)itemsToPaste[0])) ||
                this.AllowedItemType.IsAssignableFrom(itemsToPaste[0].GetType()));
        }
 
 
        class WorkflowItemPresenterAutomationPeer : UIElementAutomationPeer
        {
            WorkflowItemPresenter owner;
 
            public WorkflowItemPresenterAutomationPeer(WorkflowItemPresenter owner)
                : base(owner)
            {
                this.owner = owner;
            }
 
            protected override AutomationControlType GetAutomationControlTypeCore()
            {
                return AutomationControlType.Custom;
            }
 
            protected override string GetAutomationIdCore()
            {
                string baseAutomationID = base.GetAutomationIdCore();
                if (!string.IsNullOrEmpty(baseAutomationID))
                {
                    return baseAutomationID;
                }
                return this.owner.GetType().Name;
            }
 
            protected override string GetNameCore()
            {
                // Return the item type name if an activity is dropped on the presenter
                if (owner.Item != null &&
                    owner.Item.ItemType != null &&
                    !string.IsNullOrEmpty(owner.Item.ItemType.Name))
                {
                    return owner.Item.ItemType.Name;
                }
                string name = base.GetNameCore();
                if (string.IsNullOrEmpty(name))
                {
                    name = this.owner.HintText;
                }
                return name;
            }
 
            protected override string GetClassNameCore()
            {
                return this.owner.GetType().Name;
            }
        }
 
        private static ModelItem AutoWrapInSequenceHandler(EditingContext editingContext, AutoWrapEventArgs e)
        {
            Fx.Assert(e.ExistingActivity != null || e.InsertionPosition == InsertionPosition.None, 
                "Existing activity must not be null");
 
            ModelItem sequence = editingContext.Services.GetService<ModelTreeManager>().CreateModelItem(null, new Sequence());
            foreach (Activity activity in e.ActivitiesToBeInserted)
            {
                sequence.Properties["Activities"].Collection.Add(activity);
            }
 
            switch (e.InsertionPosition)
            {
                case InsertionPosition.Before:
                    sequence.Properties["Activities"].Collection.Add(e.ExistingActivity);
                    break;
                case InsertionPosition.After:
                    sequence.Properties["Activities"].Collection.Insert(0, e.ExistingActivity);
                    break;
                case InsertionPosition.None:
                    break;
                default:
                    Fx.Assert("Invalid insert position");
                    break;
            }
 
            return sequence;
        }
 
        // NOTE: This wrapper method is exclusively called by TransitionDesigner, because
        // WIP of Transition.Action would handle the event if the dragged source comes from
        // WIP of Transition.Trigger (see Bug 201342).  However, Auto-Surround spacer is usually
        // handled in DragEnter handler of WIP, and other ActivityDesigner should not need to 
        // access this method directly.
        internal void ShowSpacerHelperOnDraggedItems(DragEventArgs arg)
        {
            this.spacerHelper.OnWfItemPresenterPreviewDragEnter(this, arg);
        }
 
        // classes and helpers for Spacer
        private sealed class SpacerWrapper
        {
            public FrameworkElement Spacer { get; set; }
            public SpacerPlaceholder Placeholder { get; set; }
 
            public void ShowSpacer()
            {
                if (Spacer != null)
                {
                    Spacer.Visibility = Visibility.Visible;
                }
                if (Placeholder != null)
                {
                    Placeholder.Visibility = Visibility.Collapsed;
                }
            }
 
            public void HideSpacer()
            {
                if (Spacer != null)
                {
                    Spacer.Visibility = Visibility.Collapsed;
                }
                if (Placeholder != null)
                {
                    Placeholder.Visibility = Visibility.Visible;
                }
            }
 
            public bool HighlightPlaceholder
            {
                set
                {
                    this.Placeholder.TargetVisiable = value;
                }
            }
        }
 
        // All the e.Handle = true in OnDragXXXEnter/Leave/Over:
        // Prevent the events to be further handled by OnDrag, which will set DragDropEffects to None
        private sealed class SpacerHelper
        {
            public SpacerWrapper TopSpacerWrapper { get; set; }
            public SpacerWrapper BottomSpacerWrapper { get; set; }
            public Timer SpacerTimer { get; set; }
 
            private WorkflowItemPresenter wfItemPresenter;
            private SpacerWrapper SpacerToShow { get; set; }
 
            static private SpacerHelper uniqueSpacerHelper = null;
 
            static private SpacerHelper UniqueSpacerHelper
            {
                set
                {
                    if (uniqueSpacerHelper == value)
                    {
                        return;
                    }
                    if (uniqueSpacerHelper != null)
                    {
                        uniqueSpacerHelper.HighLighted = false;
                    }
                    uniqueSpacerHelper = value;
                    if (uniqueSpacerHelper != null)
                    {
                        uniqueSpacerHelper.HighLighted = true;
                    }
                }
            }
 
            public SpacerHelper(WorkflowItemPresenter wfItemPresenter)
            {
                Fx.Assert(wfItemPresenter != null, "null WorkflowItemPresenter");
                this.TopSpacerWrapper = new SpacerWrapper();
                this.BottomSpacerWrapper = new SpacerWrapper();
                this.wfItemPresenter = wfItemPresenter;
                Loaded();
            }
 
            public bool HighLighted 
            {
                set
                {
                    this.TopSpacerWrapper.HighlightPlaceholder = value;
                    this.BottomSpacerWrapper.HighlightPlaceholder = value;
                }
            }
 
            public void Unload()
            {
                // event
                this.wfItemPresenter.containerGrid.PreviewDrop  -= new DragEventHandler(OnContainerGridPreviewDrop);
                this.wfItemPresenter.PreviewDragEnter           -= new DragEventHandler(OnWfItemPresenterPreviewDragEnter);
                this.wfItemPresenter.PreviewDragLeave           -= new DragEventHandler(OnWfItemPresenterPreviewDragLeave);
 
                this.TopSpacerWrapper.Placeholder.DragEnter     -= new DragEventHandler(OnTopPlaceholderDragEnter);
                this.TopSpacerWrapper.Placeholder.DragLeave     -= new DragEventHandler(OnPlaceHoderDragLeave);
                this.TopSpacerWrapper.Spacer.DragEnter          -= new DragEventHandler(OnTopSpacerDragEnter);
                this.TopSpacerWrapper.Spacer.DragLeave          -= new DragEventHandler(OnTopSpacerDragLeave);
 
                this.BottomSpacerWrapper.Placeholder.DragEnter  -= new DragEventHandler(OnBottomPlaceholderDragEnter);
                this.BottomSpacerWrapper.Placeholder.DragLeave  -= new DragEventHandler(OnPlaceHoderDragLeave);
                this.BottomSpacerWrapper.Spacer.DragEnter       -= new DragEventHandler(OnBottomSpacerDragEnter);
                this.BottomSpacerWrapper.Spacer.DragLeave       -= new DragEventHandler(OnBottomSpacerDragLeave);
 
                this.SpacerTimer.Elapsed -= new ElapsedEventHandler(OnSpacerTimerElapsed);
            }
 
            void UpdateEffects(DragEventArgs args)
            {
                if (this.wfItemPresenter.Item == null)
                {
                    // Item is null, then use WIP's UpdateEffects.
                    this.wfItemPresenter.UpdateEffects(args);
                    return;
                }
 
                if (!this.AllowDropOnSpacer(args))
                {
                    args.Effects = DragDropEffects.None;
                }
            }
 
            void OnTopPlaceholderDragEnter(object sender, DragEventArgs e)
            {
                this.UpdateEffects(e);
                this.OnPlaceholderEnter(this.TopSpacerWrapper, e);
                e.Handled = true;
            }
 
            void OnBottomPlaceholderDragEnter(object sender, DragEventArgs e)
            {
                this.UpdateEffects(e);
                this.OnPlaceholderEnter(this.BottomSpacerWrapper, e);
                e.Handled = true;
            }
 
            void OnPlaceHoderDragLeave(object sender, DragEventArgs e)
            {
                this.UpdateEffects(e);
                this.SpacerTimer.Stop();
                e.Handled = true;
            }
 
            void OnTopSpacerDragEnter(object sender, DragEventArgs e)
            {
                this.UpdateEffects(e);
                this.TopSpacerWrapper.ShowSpacer();
                e.Handled = true;
            }
 
            void OnTopSpacerDragLeave(object sender, DragEventArgs e)
            {
                this.TopSpacerWrapper.HideSpacer();
                e.Handled = true;
            }
 
            void OnBottomSpacerDragEnter(object sender, DragEventArgs e)
            {
                this.BottomSpacerWrapper.ShowSpacer();
                e.Handled = true;
            }
 
            void OnBottomSpacerDragLeave(object sender, DragEventArgs e)
            {
                this.UpdateEffects(e);
                this.BottomSpacerWrapper.HideSpacer();
                e.Handled = true;
            }
 
            void OnSpacerOrPlaceholderDragOver(object sender, DragEventArgs e)
            {
                this.UpdateEffects(e);
                e.Handled = true;
            }
 
            void OnSpacerDrop(object sender, DragEventArgs e)
            {
                if (!this.AllowDropOnSpacer(e))
                {
                    return;
                }
                InsertionPosition insertionPos = (sender == this.BottomSpacerWrapper.Spacer)
                    ? InsertionPosition.After : InsertionPosition.Before;
 
                ModelItemHelper.TryCreateImmediateEditingScopeAndExecute(this.wfItemPresenter.Context, SR.WrapInSequenceDescription, (es) =>
                {
                    if (this.wfItemPresenter.DoAutoWrapDrop(insertionPos, e))
                    {
                        // auto wrap is successful
                        if (es != null)
                        {
                            // if we created an immedate editing scope, try to complete it.
                            es.Complete();
                        }
                    }
                });
            }
 
            void OnPlaceholderDrop(object sender, DragEventArgs e)
            {
                if (!this.AllowDropOnSpacer(e))
                {
                    return;
                }
                InsertionPosition insertionPos = (sender == this.BottomSpacerWrapper.Placeholder)
                    ? InsertionPosition.After : InsertionPosition.Before;
                ModelItemHelper.TryCreateImmediateEditingScopeAndExecute(this.wfItemPresenter.Context, SR.WrapInSequenceDescription, (es) =>
                    {
                        if (this.wfItemPresenter.DoAutoWrapDrop(insertionPos, e))
                        {
                            // auto wrap is successful
                            if (es != null)
                            {
                                // if we created an immediate editing scope, try to complete it.
                                es.Complete();
                            }
                        }
                    });
            }
 
            void OnSpacerTimerElapsed(object sender, ElapsedEventArgs e)
            {
                this.wfItemPresenter.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
                {
                    if (this.SpacerToShow != null)
                    {
                        SpacerToShow.ShowSpacer();
                        SpacerToShow = null;
                    }
                }));
            }
 
            void OnContainerGridPreviewDrop(object sender, DragEventArgs e)
            {
                this.SpacerTimer.Stop();
                this.TopSpacerWrapper.HideSpacer();
                this.BottomSpacerWrapper.HideSpacer();
                UniqueSpacerHelper = null;
            }
 
            private FrameworkElement CreateAndInitializeSpacer(VerticalAlignment alignment)
            {
                FrameworkElement spacer = (SpacerTemplate != null)
                    ? (FrameworkElement)SpacerTemplate.LoadContent()
                    : new Rectangle();
                spacer.AllowDrop = true;
                spacer.DragOver += new DragEventHandler(OnSpacerOrPlaceholderDragOver);
                spacer.Drop += new DragEventHandler(OnSpacerDrop);
                spacer.VerticalAlignment = alignment;
                spacer.IsHitTestVisible = true;
                spacer.Visibility = Visibility.Collapsed;
                return spacer;
            }
 
            void OnWfItemPresenterPreviewDragLeave(object sender, DragEventArgs e)
            {
                UniqueSpacerHelper = null;
                this.TopSpacerWrapper.HideSpacer();
                this.BottomSpacerWrapper.HideSpacer();
            }
 
            // this method is made internal because WorkflowItemPresenter.ShowSpacerHelperOnDraggedItems
            // needs to access this method to show the spacer UI gesture for Auto-surround.
            internal void OnWfItemPresenterPreviewDragEnter(object sender, DragEventArgs arg)
            {
                if (!this.AllowDropOnSpacer(arg))
                {
                    return;
                }
                UniqueSpacerHelper = this;
            }
 
            private void Loaded()
            {
                this.TopSpacerWrapper.Spacer            = CreateAndInitializeSpacer(VerticalAlignment.Bottom);
                this.TopSpacerWrapper.Placeholder       = CreateSpacerPlaceHolder();
                this.BottomSpacerWrapper.Spacer         = CreateAndInitializeSpacer(VerticalAlignment.Top);
                this.BottomSpacerWrapper.Placeholder    = CreateSpacerPlaceHolder();
 
                // timer
                this.SpacerTimer = new Timer(500);
                this.SpacerTimer.Elapsed += new ElapsedEventHandler(OnSpacerTimerElapsed);
                this.SpacerTimer.AutoReset = false;
 
                // view
                this.wfItemPresenter.stackPanel.Children.Insert(0, TopSpacerWrapper.Spacer);
                this.wfItemPresenter.stackPanel.Children.Insert(0, TopSpacerWrapper.Placeholder);
                this.wfItemPresenter.stackPanel.Children.Insert(this.wfItemPresenter.stackPanel.Children.Count, BottomSpacerWrapper.Spacer);
                this.wfItemPresenter.stackPanel.Children.Insert(this.wfItemPresenter.stackPanel.Children.Count, BottomSpacerWrapper.Placeholder);
                this.wfItemPresenter.containerGrid.Background = Brushes.Transparent;
 
                // event
                this.wfItemPresenter.containerGrid.PreviewDrop  += new DragEventHandler(OnContainerGridPreviewDrop);
                this.wfItemPresenter.PreviewDragEnter           += new DragEventHandler(OnWfItemPresenterPreviewDragEnter);
                this.wfItemPresenter.PreviewDragLeave           += new DragEventHandler(OnWfItemPresenterPreviewDragLeave);
                this.TopSpacerWrapper.Placeholder.DragEnter     += new DragEventHandler(OnTopPlaceholderDragEnter);
                this.TopSpacerWrapper.Placeholder.DragLeave     += new DragEventHandler(OnPlaceHoderDragLeave);
                this.TopSpacerWrapper.Spacer.DragEnter          += new DragEventHandler(OnTopSpacerDragEnter);
                this.TopSpacerWrapper.Spacer.DragLeave          += new DragEventHandler(OnTopSpacerDragLeave);
 
                this.BottomSpacerWrapper.Placeholder.DragEnter  += new DragEventHandler(OnBottomPlaceholderDragEnter);
                this.BottomSpacerWrapper.Placeholder.DragLeave  += new DragEventHandler(OnPlaceHoderDragLeave);
                this.BottomSpacerWrapper.Spacer.DragEnter       += new DragEventHandler(OnBottomSpacerDragEnter);
                this.BottomSpacerWrapper.Spacer.DragLeave       += new DragEventHandler(OnBottomSpacerDragLeave);
            }
 
            private static DataTemplate defaultSpacerTemplate = CreateDefaultSpacerTemplate();
 
            private static DataTemplate SpacerTemplate
            {
                get { return defaultSpacerTemplate; }
            }
 
            private static DataTemplate CreateDefaultSpacerTemplate()
            {
                FrameworkElementFactory feFactory = new FrameworkElementFactory(typeof(VerticalConnector));
                DataTemplate dt = new DataTemplate() { VisualTree = feFactory };
                dt.Seal();
                return dt;
            }
 
            private SpacerPlaceholder CreateSpacerPlaceHolder()
            {
                // The place holder should be something that can triger DragEnter
                SpacerPlaceholder spacerPlaceholder = new SpacerPlaceholder { MinHeight = 20, Visibility = Visibility.Visible, AllowDrop = true };
                spacerPlaceholder.DragOver += new DragEventHandler(OnSpacerOrPlaceholderDragOver);
                spacerPlaceholder.Drop += new DragEventHandler(OnPlaceholderDrop);
                return spacerPlaceholder; 
            }
 
            private void OnPlaceholderEnter(SpacerWrapper wrapper, DragEventArgs e)
            {
                if (!this.AllowDropOnSpacer(e))
                {
                    return;
                }
                this.SpacerToShow = wrapper;
                this.SpacerTimer.Start();
                e.Handled = true;
            }
 
            private bool AllowDropOnSpacer(DragEventArgs e)
            {
                return (this.wfItemPresenter.Item != null
                    && !this.IsOwnerActivityBeingDragged(e)
                    && DragDropHelper.AllowDrop(typeof(Sequence), this.wfItemPresenter.AllowedItemType)     // Is Sequence allowed to be dropped inside the WIP? Beause it will trigger AutoWrap.
                    && DragDropHelper.AllowDrop(e.Data, this.wfItemPresenter.Context, typeof(Activity)));   // Is the item being dragged allowed to be dropped onto Sequence?
            }
 
            private bool IsOwnerActivityBeingDragged(DragEventArgs e)
            {
                if (this.wfItemPresenter.Item == null)
                {
                    return false;
                }
                else
                {
                    // In case of a toolbox drop, DragDropHelper.GetObjectsToBeDropped 
                    //  will create an instance, which will possibliy pop up a type picker
                    //  dialog for generic activities. So check for it first and avoid
                    //  pop up dialogs.
                    if (DragDropHelper.IsDraggingFromToolbox(e))
                    {
                        return false;
                    }
                    IEnumerable<ModelItem> draggedObjects = DragDropHelper.GetDraggedModelItems(e);
                    return draggedObjects.Contains(this.wfItemPresenter.Item);
                }
            }
        }
 
    }
}