CutCopyPasteHelper.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / NetFx40 / Tools / System.Activities.Presentation / System / Activities / Presentation / CutCopyPasteHelper.cs / 1477467 / CutCopyPasteHelper.cs

                            //---------------------------------------------------------------- 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//---------------------------------------------------------------

namespace System.Activities.Presentation 
{
    using System; 
    using System.Activities.Debugger; 
    using System.Activities.Presentation.Model;
    using System.Activities.Presentation.View; 
    using System.Activities.Statements;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis; 
    using System.Globalization;
    using System.IO; 
    using System.Linq; 
    using System.Runtime;
    using System.Runtime.InteropServices; 
    using System.ServiceModel;
    using System.ServiceModel.Activities;
    using System.Windows;
    using System.Windows.Documents; 
    using System.Windows.Input;
    using System.Windows.Media; 
    using System.Xaml; 

    public static class CutCopyPasteHelper 
    {
        internal static readonly DependencyProperty ChildContainersProperty =
            DependencyProperty.RegisterAttached("ChildContainers", typeof(HashSet), typeof(CutCopyPasteHelper), new UIPropertyMetadata(null));
 
        static object workflowCallbackContext = null;
 
        const string WorkflowClipboardFormat = "WorkflowXamlFormat"; 

        //define a workflow callback clipboard format - make it unique across all processes 
        static readonly string WorkflowCallbackClipboardFormat = string.Format(CultureInfo.InvariantCulture, "WorkflowCallbackFormat{0}", Guid.NewGuid());

        const string versionInfo = "1.0";
 
        static IList disallowedTypesForCopy;
 
        static IEnumerable DisallowedTypesForCopy 
        {
            get 
            {
                if (null == disallowedTypesForCopy)
                {
                    disallowedTypesForCopy = new List(); 
                    disallowedTypesForCopy.Add(typeof(ActivityBuilder));
                    disallowedTypesForCopy.Add(typeof(ModelItemKeyValuePair<,>)); 
                    disallowedTypesForCopy.Add(typeof(WorkflowService)); 
                    disallowedTypesForCopy.Add(typeof(Catch));
                } 
                return disallowedTypesForCopy;
            }
        }
 
        internal static void AddDisallowedTypeForCopy(Type type)
        { 
            if (!DisallowedTypesForCopy.Any(p => type == p)) 
            {
                disallowedTypesForCopy.Add(type); 
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] 
        static void AddChildContainer(WorkflowViewElement viewElement, ICompositeView sourceContainer)
        { 
            if (viewElement == null) 
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("viewElement")); 
            }
            if (sourceContainer == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("sourceContainer")); 
            }
 
            HashSet containers = (HashSet)viewElement.GetValue(CutCopyPasteHelper.ChildContainersProperty); 
            if (containers == null)
            { 
                containers = new HashSet();
                viewElement.SetValue(CutCopyPasteHelper.ChildContainersProperty, containers);
            }
            containers.Add(sourceContainer); 
        }
 
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] 
        static HashSet GetChildContainers(WorkflowViewElement workflowViewElement)
        { 
            HashSet childContainers = null;
            if (workflowViewElement != null && workflowViewElement.ShowExpanded)
            {
                childContainers = (HashSet)workflowViewElement.GetValue(CutCopyPasteHelper.ChildContainersProperty); 
            }
            return childContainers; 
        } 

        //This enables us to get children ICompositeViews from WorkflowViewElements. 
        //Eg. get the WorkflowItemsPresenter from SequenceDesigner.
        //This is useful for Cut-Copy-Paste, Delete handling, etc.
        internal static void RegisterWithParentViewElement(ICompositeView container)
        { 
            WorkflowViewElement parent = GetParentViewElement(container);
            if (parent != null) 
            { 
                CutCopyPasteHelper.AddChildContainer(parent, container);
            } 
        }

        //Returns the first WorkflowViewElement in the parent chain.
        //If ICompositeView is a WorkflowViewElement this method returns the same object. 
        static WorkflowViewElement GetParentViewElement(ICompositeView container)
        { 
            DependencyObject parent = container as DependencyObject; 
            return GetParentViewElement(parent);
        } 

        //Returns the first WorkflowViewElement in the parent chain.
        //Move this to a helper class.
        internal static WorkflowViewElement GetParentViewElement(DependencyObject obj) 
        {
            while (obj != null && !(obj is WorkflowViewElement)) 
            { 
                obj = VisualTreeHelper.GetParent(obj);
            } 
            return obj as WorkflowViewElement;
        }

        public static void DoCut(EditingContext context) 
        {
            if (context == null) 
            { 
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            } 
            Selection currentSelection = context.Items.GetValue();
            List modelItemsToCut = new List(currentSelection.SelectedObjects);
            CutCopyPasteHelper.DoCut(modelItemsToCut, context);
        } 

        internal static void DoCut(List modelItemsToCut, EditingContext context) 
        { 
            if (modelItemsToCut == null)
            { 
                throw FxTrace.Exception.AsError(new ArgumentNullException("modelItemsToCut"));
            }
            if (context == null)
            { 
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            } 
            modelItemsToCut.RemoveAll((modelItem) => { return modelItem == null; }); 
            if (modelItemsToCut.Count > 0)
            { 
                using (EditingScope es = (EditingScope)modelItemsToCut[0].BeginEdit(SR.CutOperationEditingScopeDescription))
                {
                    try
                    { 
                        CutCopyOperation(modelItemsToCut, context, true);
                    } 
                    catch (ExternalException e) 
                    {
                        es.Revert(); 
                        ErrorReporting.ShowErrorMessage(e.Message);
                        return;
                    }
                    DesignerView view = context.Services.GetService(); 
                    //Setting the selection to Breadcrumb root.
                    Fx.Assert(view != null, "DesignerView Cannot be null during cut"); 
                    WorkflowViewElement rootView = view.RootDesigner as WorkflowViewElement; 
                    if (rootView != null)
                    { 
                        Selection.SelectOnly(context, rootView.ModelItem);
                    }
                    es.Complete();
                } 
            }
        } 
 
        public static void DoCopy(EditingContext context)
        { 
            if (context == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            } 
            Selection currentSelection = context.Items.GetValue();
            List modelItemsToCopy = new List(currentSelection.SelectedObjects); 
            CutCopyPasteHelper.DoCopy(modelItemsToCopy, context); 
        }
 
        internal static void DoCopy(List modelItemsToCopy, EditingContext context)
        {
            if (modelItemsToCopy == null)
            { 
                throw FxTrace.Exception.AsError(new ArgumentNullException("modelItemsToCopy"));
            } 
            if (context == null) 
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context")); 
            }

            // copy only works if we have DesignerView up and running so check and throw here
            if (context.Services.GetService() == null) 
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CutCopyRequiresDesignerView)); 
            } 
            modelItemsToCopy.RemoveAll((modelItem) => { return modelItem == null; });
            try 
            {
                CutCopyOperation(modelItemsToCopy, context, false);
            }
            catch (ExternalException e) 
            {
                ErrorReporting.ShowErrorMessage(e.Message); 
            } 

        } 


        static void CutCopyOperation(List modelItemsToCutCopy, EditingContext context, bool isCutOperation)
        { 
            List objectsOnClipboard = null;
            List metaData = null; 
            if (modelItemsToCutCopy.Count > 0) 
            {
                objectsOnClipboard = new List(modelItemsToCutCopy.Count); 
                metaData = new List();
                Dictionary> notifyDictionary = new Dictionary>();
                UIElement breadCrumbRootView = ((DesignerView)context.Services.GetService()).RootDesigner;
                foreach (ModelItem modelItem in modelItemsToCutCopy) 
                {
                    object currentElement = modelItem.GetCurrentValue(); 
 
                    if (typeof(Activity).IsAssignableFrom(currentElement.GetType()))
                    { 
                        string fileName;
                        if (AttachablePropertyServices.TryGetProperty(currentElement, XamlDebuggerXmlReader.FileNameName, out fileName))
                        {
                            AttachablePropertyServices.RemoveProperty(currentElement, XamlDebuggerXmlReader.FileNameName); 
                        }
                    } 
 
                    if (modelItem.View != null)
                    { 
                        //The case where the breadcrumbroot designer is Cut/Copied. We do not delete the root designer, we only copy it.
                        if (breadCrumbRootView.Equals(modelItem.View))
                        {
                            notifyDictionary.Clear(); 
                            objectsOnClipboard.Add(modelItem.GetCurrentValue());
                            break; 
                        } 
                        else
                        { 
                            ICompositeView container = (ICompositeView)DragDropHelper.GetCompositeView((WorkflowViewElement)modelItem.View);
                            if (container != null)
                            {
                                //If the parent and some of its children are selected and cut/copied, we ignore the children. 
                                //The entire parent will be cut/copied.
                                //HashSet parentModelItems contains all the model items in the parent chain of current modelItem. 
                                //We use HashSet.IntersectWith operation to determine if one of the parents is set to be cut. 
                                HashSet parentModelItems = CutCopyPasteHelper.GetSelectableParentModelItems(modelItem);
                                parentModelItems.IntersectWith(modelItemsToCutCopy); 
                                if (parentModelItems.Count == 0)
                                {
                                    if (!notifyDictionary.ContainsKey(container))
                                    { 
                                        notifyDictionary[container] = new List();
                                    } 
                                    notifyDictionary[container].Add(modelItem); 
                                }
                            } 
                        }

                    }
                } 

                foreach (ICompositeView container in notifyDictionary.Keys) 
                { 
                    object containerMetaData = false;
                    if (isCutOperation) 
                    {
                        containerMetaData = container.OnItemsCut(notifyDictionary[container]);
                    }
                    else 
                    {
                        containerMetaData = container.OnItemsCopied(notifyDictionary[container]); 
                    } 
                    if (containerMetaData != null)
                    { 
                        metaData.Add(containerMetaData);
                    }
                    //Put the actual activities and not the modelItems in the clipboard.
                    foreach (ModelItem modelItem in notifyDictionary[container]) 
                    {
                        objectsOnClipboard.Add(modelItem.GetCurrentValue()); 
                    } 
                }
                if (metaData.Count == 0) 
                {
                    metaData = null;
                }
            } 
            try
            { 
                PutOnClipBoard(objectsOnClipboard, metaData); 
            }
            catch (XamlObjectReaderException exception) 
            {
                if (modelItemsToCutCopy.Count > 0 && ErrorActivity.GetHasErrorActivities(modelItemsToCutCopy[0].Root.GetCurrentValue()))
                {
                    ErrorReporting.ShowErrorMessage(SR.CutCopyErrorActivityMessage); 
                }
                else 
                { 
                    ErrorReporting.ShowErrorMessage(exception.Message);
                } 
            }
        }

 
        //This method collects all the ModelItems in the parent chain by calling the GetSelectableParentViewElements method
        //which walks the WPF Visual tree. We want to avoid walking ModelItem tree. 
        static HashSet GetSelectableParentModelItems(ModelItem modelItem) 
        {
            if (null == modelItem) 
            {
                throw FxTrace.Exception.ArgumentNull("modelItem");
            }
            List parentViewElements = GetSelectableParentViewElements(modelItem.View as WorkflowViewElement); 
            HashSet parentModelItems = new HashSet();
            foreach (WorkflowViewElement view in parentViewElements) 
            { 
                parentModelItems.Add(view.ModelItem);
            } 
            return parentModelItems;
        }

        //This is more efficient than walking up the VisualTree looking for WorkflowViewElements. 
        //Assuming that Cut-Copy will always be against selected elements.
        //This implies that only elements under the BreadCrumbRoot can be cut/copied. 
        static List GetSelectableParentViewElements(WorkflowViewElement childElement) 
        {
            List parentViewElements = new List(); 
            if (childElement != null)
            {
                UIElement breadcrumbRoot = childElement.Context.Services.GetService().RootDesigner;
                ICompositeView container = (ICompositeView)DragDropHelper.GetCompositeView(childElement); 
                while (!childElement.Equals(breadcrumbRoot) && container != null)
                { 
                    childElement = CutCopyPasteHelper.GetParentViewElement(container); 
                    Fx.Assert(childElement != null, "container should be present in a WorkflowViewElement");
                    parentViewElements.Add(childElement); 
                    container = (ICompositeView)DragDropHelper.GetCompositeView(childElement);
                }
            }
            return parentViewElements; 
        }
 
 
        public static void DoPaste(EditingContext context)
        { 
            if (context == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            } 
            DoPaste(context, new Point(-1, -1), null);
        } 
 
        internal static void DoPaste(EditingContext context, Point pastePoint, WorkflowViewElement pastePointReference)
        { 
            if (context == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            } 
            ModelItem primarySelection = context.Items.GetValue().PrimarySelection;
            if (primarySelection != null) 
            { 
                DoPaste(primarySelection, pastePoint, pastePointReference);
            } 
        }
        internal static void DoPaste(ModelItem modelItem, Point pastePoint, WorkflowViewElement pastePointReference)
        {
            if (modelItem == null) 
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("modelItem")); 
            } 
            //Get data from clipboard.
            List metaData = null; 
            List clipboardObjects = GetFromClipboard(out metaData);
            if (clipboardObjects != null)
            {
                using (EditingScope es = (EditingScope)modelItem.BeginEdit(SR.PasteUndoDescription)) 
                {
                    if (clipboardObjects.Count == 3 && clipboardObjects[1] is Func) 
                    { 
                        var factoryMethod = (Func)clipboardObjects[1];
                        object result = factoryMethod(modelItem, clipboardObjects[2]); 
                        clipboardObjects = new List();
                        clipboardObjects.Add(result);
                    }
                    ICompositeView container = GetContainerForPaste(modelItem, pastePoint); 
                    if (container != null)
                    { 
                        container.OnItemsPasted(clipboardObjects, metaData, pastePoint, pastePointReference); 
                    }
                    es.Complete(); 
                }
            }
        }
 

 
        static ICompositeView GetClickedContainer(ModelItem clickedModelItem, Point clickPoint) 
        {
            Visual parentVisual = clickedModelItem.View as Visual; 
            if (parentVisual == null)
            {
                return null;
            } 

            DependencyObject visualHit = null; 
            HitTestResult hitTest = VisualTreeHelper.HitTest(parentVisual, clickPoint); 
            if (hitTest != null)
            { 
                visualHit = hitTest.VisualHit;
                while (visualHit != null && !visualHit.Equals(parentVisual) &&
                    !typeof(ICompositeView).IsAssignableFrom(visualHit.GetType()))
                { 
                    visualHit = VisualTreeHelper.GetParent(visualHit);
                } 
            } 
            return visualHit as ICompositeView;
 
        }


        static ICompositeView GetContainerForPaste(ModelItem pasteModelItem, Point clickPoint) 
        {
            ICompositeView pasteContainer = null; 
 
            if (null != pasteModelItem && null != pasteModelItem.View && pasteModelItem.View is WorkflowViewElement)
            { 
                pasteContainer = ((WorkflowViewElement)pasteModelItem.View).ActiveCompositeView;
            }
            if (null == pasteContainer)
            { 
                //Get clicked container.
                if (clickPoint.X > 0 && clickPoint.Y > 0) 
                { 
                    pasteContainer = GetClickedContainer(pasteModelItem, clickPoint);
                } 

                //If the container itself is a WVE, there's posibility that it's collapsed.
                //Thus, we need to check this as well.
                if (pasteContainer != null && pasteContainer is WorkflowViewElement) 
                {
                    WorkflowViewElement view = pasteContainer as WorkflowViewElement; 
                    if (!view.ShowExpanded) 
                    {
                        pasteContainer = null; 
                    }
                }
                else if (pasteContainer == null) //If the modelitem.View itself is a container.
                { 
                    WorkflowViewElement view = pasteModelItem.View as WorkflowViewElement;
                    if (view != null && view.ShowExpanded) 
                    { 
                        pasteContainer = pasteModelItem.View as ICompositeView;
                    } 
                }

                //Get the container registered with modelItem.View if unambigous
                //If nothing works take the container with keyboard focus if one exists. 
                if (pasteContainer == null)
                { 
                    HashSet childrenContainers = CutCopyPasteHelper.GetChildContainers(pasteModelItem.View as WorkflowViewElement); 
                    if ((childrenContainers == null || childrenContainers.Count == 0) && null != pasteModelItem.View)
                    { 
                        pasteContainer = (ICompositeView)DragDropHelper.GetCompositeView((WorkflowViewElement)pasteModelItem.View);
                    }
                    else if (null != childrenContainers && childrenContainers.Count == 1)
                    { 
                        pasteContainer = new List(childrenContainers)[0];
                    } 
                    else 
                    {
                        pasteContainer = Keyboard.FocusedElement as ICompositeView; 
                    }

                }
            } 
            return pasteContainer;
        } 
 

        internal static void PutOnClipBoard(List selectedData, List metaData) 
        {
            CutCopyPasteHelper.workflowCallbackContext = null;
            if (selectedData != null)
            { 
                ClipboardData clipboardData = new ClipboardData();
                clipboardData.Data = selectedData; 
                clipboardData.Metadata = metaData; 
                clipboardData.Version = versionInfo;
                string clipBoardString = XamlServices.Save(clipboardData); 

                DataObject dataObject = new DataObject(WorkflowClipboardFormat, clipBoardString);
                dataObject.SetData(DataFormats.Text, clipBoardString);
                RetriableClipboard.SetDataObject(dataObject, true); 
            }
        } 
 
        //PutCallbackOnClipBoard - tries to put into private (this application only) clipboard a callback
        //to a method. The method will be invoked when user retrieves clipboard content - i.e. by 
        //calling a paste command.
        //the callback has to be:
        //- static method
        //- have return value (not void) 
        //- takes 2 input parameters:
        //   * 1 parameter is modelitem - this is a target modelitem upon which callback is to be executed 
        //   * 2 parameter is user provided context - any object. Since this callback will be executed within 
        //    this application only, there is no need for context to be serializable.
        internal static void PutCallbackOnClipBoard(Func callbackMethod, Type callbackResultType, object context) 
        {
            if (null == callbackMethod || null == context)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException(null == callbackMethod ? "callbackMethod" : "context")); 
            }
            ClipboardData clipboardData = new ClipboardData(); 
            List data = new List(); 
            data.Add(callbackResultType);
            data.Add(callbackMethod); 
            clipboardData.Data = data;
            clipboardData.Version = versionInfo;
            CutCopyPasteHelper.workflowCallbackContext = context;
            try 
            {
                RetriableClipboard.SetDataObject(new DataObject(WorkflowCallbackClipboardFormat, clipboardData, false), false); 
            } 
            catch (ExternalException e)
            { 
                ErrorReporting.ShowErrorMessage(e.Message);
            }
        }
 

        //This method returns the list of objects put on clipboard by cut/copy. 
        //Out parameter is the metaData information. 
        [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DoNotCatchGeneralExceptionTypes,
            Justification = "Deserialization of cliboard data might fail. Propagating exceptions might lead to VS crash.")] 
        [SuppressMessage("Reliability", "Reliability108",
            Justification = "Deserialization of cliboard data might fail. Propagating exceptions might lead to VS crash.")]
        internal static List GetFromClipboard(out List metaData)
        { 
            DataObject dataObject = RetriableClipboard.GetDataObject() as DataObject;
            List workflowData = null; 
            metaData = null; 
            if (dataObject != null)
            { 
                if (dataObject.GetDataPresent(WorkflowClipboardFormat))
                {
                    string clipBoardString = (string)dataObject.GetData(WorkflowClipboardFormat);
                    using (StringReader reader = new StringReader(clipBoardString)) 
                    {
                        try 
                        { 
                            ClipboardData clipboardData = (ClipboardData)XamlServices.Load(reader);
                            metaData = clipboardData.Metadata; 
                            workflowData = clipboardData.Data;
                        }
                        catch (Exception e)
                        { 
                            Trace.WriteLine(e.Message);
                        } 
                    } 
                }
                else if (dataObject.GetDataPresent(WorkflowCallbackClipboardFormat)) 
                {
                    ClipboardData localData = (ClipboardData)dataObject.GetData(WorkflowCallbackClipboardFormat);
                    metaData = null;
                    workflowData = localData.Data; 
                    workflowData.Add(CutCopyPasteHelper.workflowCallbackContext);
                } 
            } 
            return workflowData;
        } 

        static bool CanCopy(Type type)
        {
            foreach (Type disallowedType in CutCopyPasteHelper.DisallowedTypesForCopy) 
            {
                if(disallowedType.IsAssignableFrom(type)) 
                { 
                    return false;
                } 
                if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(disallowedType))
                {
                    return false;
                } 
            }
            return true; 
        } 

        static bool CanCopy(ModelItem item) 
        {
            return null != item.View && item.View is WorkflowViewElement &&
                null != ((WorkflowViewElement)item.View).ModelItem &&
                CanCopy(((WorkflowViewElement)item.View).ModelItem.ItemType); 
        }
 
        public static bool CanCopy(EditingContext context) 
        {
            if (context == null) 
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            }
            Selection selection = context.Items.GetValue(); 
            return selection.SelectionCount > 0 && selection.SelectedObjects.All(p => CanCopy(p));
        } 
 
        public static bool CanCut(EditingContext context)
        { 
            if (context == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            } 
            bool result = false;
            Selection selection = context.Items.GetValue(); 
            if (null != selection && selection.SelectionCount > 0) 
            {
                DesignerView designerView = context.Services.GetService(); 
                result = selection.SelectedObjects.All(p =>
                    CanCopy(p) && !p.View.Equals(designerView.RootDesigner));
            }
            return result; 
        }
 
        public static bool CanPaste(EditingContext context) 
        {
            if (context == null) 
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            }
            bool result = false; 
            ModelItem primarySelection = context.Items.GetValue().PrimarySelection;
            if (null != primarySelection) 
            { 
                ICompositeView container = GetContainerForPaste(primarySelection, new Point(-1, -1));
                if (null != container) 
                {
                    DataObject dataObject = RetriableClipboard.GetDataObject() as DataObject;
                    if (null != dataObject)
                    { 
                        List metaData = null;
                        List itemsToPaste = null; 
                        try 
                        {
                            if (dataObject.GetDataPresent(WorkflowClipboardFormat)) 
                            {
                                itemsToPaste = GetFromClipboard(out metaData);
                                result = container.CanPasteItems(itemsToPaste);
                            } 
                            else if (dataObject.GetDataPresent(WorkflowCallbackClipboardFormat))
                            { 
                                itemsToPaste = GetFromClipboard(out metaData); 
                                result = container.CanPasteItems(itemsToPaste.GetRange(0, 1));
                            } 
                        }
                        //This is being defensive for the case where user code for CanPasteITems throws a non-fatal exception.
                        catch (Exception exp)
                        { 
                            if (Fx.IsFatal(exp))
                            { 
                                throw; 
                            }
                        } 
                    }
                }
            }
            return result; 
        }
 
    } 

 

}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------- 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//---------------------------------------------------------------

namespace System.Activities.Presentation 
{
    using System; 
    using System.Activities.Debugger; 
    using System.Activities.Presentation.Model;
    using System.Activities.Presentation.View; 
    using System.Activities.Statements;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis; 
    using System.Globalization;
    using System.IO; 
    using System.Linq; 
    using System.Runtime;
    using System.Runtime.InteropServices; 
    using System.ServiceModel;
    using System.ServiceModel.Activities;
    using System.Windows;
    using System.Windows.Documents; 
    using System.Windows.Input;
    using System.Windows.Media; 
    using System.Xaml; 

    public static class CutCopyPasteHelper 
    {
        internal static readonly DependencyProperty ChildContainersProperty =
            DependencyProperty.RegisterAttached("ChildContainers", typeof(HashSet), typeof(CutCopyPasteHelper), new UIPropertyMetadata(null));
 
        static object workflowCallbackContext = null;
 
        const string WorkflowClipboardFormat = "WorkflowXamlFormat"; 

        //define a workflow callback clipboard format - make it unique across all processes 
        static readonly string WorkflowCallbackClipboardFormat = string.Format(CultureInfo.InvariantCulture, "WorkflowCallbackFormat{0}", Guid.NewGuid());

        const string versionInfo = "1.0";
 
        static IList disallowedTypesForCopy;
 
        static IEnumerable DisallowedTypesForCopy 
        {
            get 
            {
                if (null == disallowedTypesForCopy)
                {
                    disallowedTypesForCopy = new List(); 
                    disallowedTypesForCopy.Add(typeof(ActivityBuilder));
                    disallowedTypesForCopy.Add(typeof(ModelItemKeyValuePair<,>)); 
                    disallowedTypesForCopy.Add(typeof(WorkflowService)); 
                    disallowedTypesForCopy.Add(typeof(Catch));
                } 
                return disallowedTypesForCopy;
            }
        }
 
        internal static void AddDisallowedTypeForCopy(Type type)
        { 
            if (!DisallowedTypesForCopy.Any(p => type == p)) 
            {
                disallowedTypesForCopy.Add(type); 
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] 
        static void AddChildContainer(WorkflowViewElement viewElement, ICompositeView sourceContainer)
        { 
            if (viewElement == null) 
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("viewElement")); 
            }
            if (sourceContainer == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("sourceContainer")); 
            }
 
            HashSet containers = (HashSet)viewElement.GetValue(CutCopyPasteHelper.ChildContainersProperty); 
            if (containers == null)
            { 
                containers = new HashSet();
                viewElement.SetValue(CutCopyPasteHelper.ChildContainersProperty, containers);
            }
            containers.Add(sourceContainer); 
        }
 
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] 
        static HashSet GetChildContainers(WorkflowViewElement workflowViewElement)
        { 
            HashSet childContainers = null;
            if (workflowViewElement != null && workflowViewElement.ShowExpanded)
            {
                childContainers = (HashSet)workflowViewElement.GetValue(CutCopyPasteHelper.ChildContainersProperty); 
            }
            return childContainers; 
        } 

        //This enables us to get children ICompositeViews from WorkflowViewElements. 
        //Eg. get the WorkflowItemsPresenter from SequenceDesigner.
        //This is useful for Cut-Copy-Paste, Delete handling, etc.
        internal static void RegisterWithParentViewElement(ICompositeView container)
        { 
            WorkflowViewElement parent = GetParentViewElement(container);
            if (parent != null) 
            { 
                CutCopyPasteHelper.AddChildContainer(parent, container);
            } 
        }

        //Returns the first WorkflowViewElement in the parent chain.
        //If ICompositeView is a WorkflowViewElement this method returns the same object. 
        static WorkflowViewElement GetParentViewElement(ICompositeView container)
        { 
            DependencyObject parent = container as DependencyObject; 
            return GetParentViewElement(parent);
        } 

        //Returns the first WorkflowViewElement in the parent chain.
        //Move this to a helper class.
        internal static WorkflowViewElement GetParentViewElement(DependencyObject obj) 
        {
            while (obj != null && !(obj is WorkflowViewElement)) 
            { 
                obj = VisualTreeHelper.GetParent(obj);
            } 
            return obj as WorkflowViewElement;
        }

        public static void DoCut(EditingContext context) 
        {
            if (context == null) 
            { 
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            } 
            Selection currentSelection = context.Items.GetValue();
            List modelItemsToCut = new List(currentSelection.SelectedObjects);
            CutCopyPasteHelper.DoCut(modelItemsToCut, context);
        } 

        internal static void DoCut(List modelItemsToCut, EditingContext context) 
        { 
            if (modelItemsToCut == null)
            { 
                throw FxTrace.Exception.AsError(new ArgumentNullException("modelItemsToCut"));
            }
            if (context == null)
            { 
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            } 
            modelItemsToCut.RemoveAll((modelItem) => { return modelItem == null; }); 
            if (modelItemsToCut.Count > 0)
            { 
                using (EditingScope es = (EditingScope)modelItemsToCut[0].BeginEdit(SR.CutOperationEditingScopeDescription))
                {
                    try
                    { 
                        CutCopyOperation(modelItemsToCut, context, true);
                    } 
                    catch (ExternalException e) 
                    {
                        es.Revert(); 
                        ErrorReporting.ShowErrorMessage(e.Message);
                        return;
                    }
                    DesignerView view = context.Services.GetService(); 
                    //Setting the selection to Breadcrumb root.
                    Fx.Assert(view != null, "DesignerView Cannot be null during cut"); 
                    WorkflowViewElement rootView = view.RootDesigner as WorkflowViewElement; 
                    if (rootView != null)
                    { 
                        Selection.SelectOnly(context, rootView.ModelItem);
                    }
                    es.Complete();
                } 
            }
        } 
 
        public static void DoCopy(EditingContext context)
        { 
            if (context == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            } 
            Selection currentSelection = context.Items.GetValue();
            List modelItemsToCopy = new List(currentSelection.SelectedObjects); 
            CutCopyPasteHelper.DoCopy(modelItemsToCopy, context); 
        }
 
        internal static void DoCopy(List modelItemsToCopy, EditingContext context)
        {
            if (modelItemsToCopy == null)
            { 
                throw FxTrace.Exception.AsError(new ArgumentNullException("modelItemsToCopy"));
            } 
            if (context == null) 
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context")); 
            }

            // copy only works if we have DesignerView up and running so check and throw here
            if (context.Services.GetService() == null) 
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CutCopyRequiresDesignerView)); 
            } 
            modelItemsToCopy.RemoveAll((modelItem) => { return modelItem == null; });
            try 
            {
                CutCopyOperation(modelItemsToCopy, context, false);
            }
            catch (ExternalException e) 
            {
                ErrorReporting.ShowErrorMessage(e.Message); 
            } 

        } 


        static void CutCopyOperation(List modelItemsToCutCopy, EditingContext context, bool isCutOperation)
        { 
            List objectsOnClipboard = null;
            List metaData = null; 
            if (modelItemsToCutCopy.Count > 0) 
            {
                objectsOnClipboard = new List(modelItemsToCutCopy.Count); 
                metaData = new List();
                Dictionary> notifyDictionary = new Dictionary>();
                UIElement breadCrumbRootView = ((DesignerView)context.Services.GetService()).RootDesigner;
                foreach (ModelItem modelItem in modelItemsToCutCopy) 
                {
                    object currentElement = modelItem.GetCurrentValue(); 
 
                    if (typeof(Activity).IsAssignableFrom(currentElement.GetType()))
                    { 
                        string fileName;
                        if (AttachablePropertyServices.TryGetProperty(currentElement, XamlDebuggerXmlReader.FileNameName, out fileName))
                        {
                            AttachablePropertyServices.RemoveProperty(currentElement, XamlDebuggerXmlReader.FileNameName); 
                        }
                    } 
 
                    if (modelItem.View != null)
                    { 
                        //The case where the breadcrumbroot designer is Cut/Copied. We do not delete the root designer, we only copy it.
                        if (breadCrumbRootView.Equals(modelItem.View))
                        {
                            notifyDictionary.Clear(); 
                            objectsOnClipboard.Add(modelItem.GetCurrentValue());
                            break; 
                        } 
                        else
                        { 
                            ICompositeView container = (ICompositeView)DragDropHelper.GetCompositeView((WorkflowViewElement)modelItem.View);
                            if (container != null)
                            {
                                //If the parent and some of its children are selected and cut/copied, we ignore the children. 
                                //The entire parent will be cut/copied.
                                //HashSet parentModelItems contains all the model items in the parent chain of current modelItem. 
                                //We use HashSet.IntersectWith operation to determine if one of the parents is set to be cut. 
                                HashSet parentModelItems = CutCopyPasteHelper.GetSelectableParentModelItems(modelItem);
                                parentModelItems.IntersectWith(modelItemsToCutCopy); 
                                if (parentModelItems.Count == 0)
                                {
                                    if (!notifyDictionary.ContainsKey(container))
                                    { 
                                        notifyDictionary[container] = new List();
                                    } 
                                    notifyDictionary[container].Add(modelItem); 
                                }
                            } 
                        }

                    }
                } 

                foreach (ICompositeView container in notifyDictionary.Keys) 
                { 
                    object containerMetaData = false;
                    if (isCutOperation) 
                    {
                        containerMetaData = container.OnItemsCut(notifyDictionary[container]);
                    }
                    else 
                    {
                        containerMetaData = container.OnItemsCopied(notifyDictionary[container]); 
                    } 
                    if (containerMetaData != null)
                    { 
                        metaData.Add(containerMetaData);
                    }
                    //Put the actual activities and not the modelItems in the clipboard.
                    foreach (ModelItem modelItem in notifyDictionary[container]) 
                    {
                        objectsOnClipboard.Add(modelItem.GetCurrentValue()); 
                    } 
                }
                if (metaData.Count == 0) 
                {
                    metaData = null;
                }
            } 
            try
            { 
                PutOnClipBoard(objectsOnClipboard, metaData); 
            }
            catch (XamlObjectReaderException exception) 
            {
                if (modelItemsToCutCopy.Count > 0 && ErrorActivity.GetHasErrorActivities(modelItemsToCutCopy[0].Root.GetCurrentValue()))
                {
                    ErrorReporting.ShowErrorMessage(SR.CutCopyErrorActivityMessage); 
                }
                else 
                { 
                    ErrorReporting.ShowErrorMessage(exception.Message);
                } 
            }
        }

 
        //This method collects all the ModelItems in the parent chain by calling the GetSelectableParentViewElements method
        //which walks the WPF Visual tree. We want to avoid walking ModelItem tree. 
        static HashSet GetSelectableParentModelItems(ModelItem modelItem) 
        {
            if (null == modelItem) 
            {
                throw FxTrace.Exception.ArgumentNull("modelItem");
            }
            List parentViewElements = GetSelectableParentViewElements(modelItem.View as WorkflowViewElement); 
            HashSet parentModelItems = new HashSet();
            foreach (WorkflowViewElement view in parentViewElements) 
            { 
                parentModelItems.Add(view.ModelItem);
            } 
            return parentModelItems;
        }

        //This is more efficient than walking up the VisualTree looking for WorkflowViewElements. 
        //Assuming that Cut-Copy will always be against selected elements.
        //This implies that only elements under the BreadCrumbRoot can be cut/copied. 
        static List GetSelectableParentViewElements(WorkflowViewElement childElement) 
        {
            List parentViewElements = new List(); 
            if (childElement != null)
            {
                UIElement breadcrumbRoot = childElement.Context.Services.GetService().RootDesigner;
                ICompositeView container = (ICompositeView)DragDropHelper.GetCompositeView(childElement); 
                while (!childElement.Equals(breadcrumbRoot) && container != null)
                { 
                    childElement = CutCopyPasteHelper.GetParentViewElement(container); 
                    Fx.Assert(childElement != null, "container should be present in a WorkflowViewElement");
                    parentViewElements.Add(childElement); 
                    container = (ICompositeView)DragDropHelper.GetCompositeView(childElement);
                }
            }
            return parentViewElements; 
        }
 
 
        public static void DoPaste(EditingContext context)
        { 
            if (context == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            } 
            DoPaste(context, new Point(-1, -1), null);
        } 
 
        internal static void DoPaste(EditingContext context, Point pastePoint, WorkflowViewElement pastePointReference)
        { 
            if (context == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            } 
            ModelItem primarySelection = context.Items.GetValue().PrimarySelection;
            if (primarySelection != null) 
            { 
                DoPaste(primarySelection, pastePoint, pastePointReference);
            } 
        }
        internal static void DoPaste(ModelItem modelItem, Point pastePoint, WorkflowViewElement pastePointReference)
        {
            if (modelItem == null) 
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("modelItem")); 
            } 
            //Get data from clipboard.
            List metaData = null; 
            List clipboardObjects = GetFromClipboard(out metaData);
            if (clipboardObjects != null)
            {
                using (EditingScope es = (EditingScope)modelItem.BeginEdit(SR.PasteUndoDescription)) 
                {
                    if (clipboardObjects.Count == 3 && clipboardObjects[1] is Func) 
                    { 
                        var factoryMethod = (Func)clipboardObjects[1];
                        object result = factoryMethod(modelItem, clipboardObjects[2]); 
                        clipboardObjects = new List();
                        clipboardObjects.Add(result);
                    }
                    ICompositeView container = GetContainerForPaste(modelItem, pastePoint); 
                    if (container != null)
                    { 
                        container.OnItemsPasted(clipboardObjects, metaData, pastePoint, pastePointReference); 
                    }
                    es.Complete(); 
                }
            }
        }
 

 
        static ICompositeView GetClickedContainer(ModelItem clickedModelItem, Point clickPoint) 
        {
            Visual parentVisual = clickedModelItem.View as Visual; 
            if (parentVisual == null)
            {
                return null;
            } 

            DependencyObject visualHit = null; 
            HitTestResult hitTest = VisualTreeHelper.HitTest(parentVisual, clickPoint); 
            if (hitTest != null)
            { 
                visualHit = hitTest.VisualHit;
                while (visualHit != null && !visualHit.Equals(parentVisual) &&
                    !typeof(ICompositeView).IsAssignableFrom(visualHit.GetType()))
                { 
                    visualHit = VisualTreeHelper.GetParent(visualHit);
                } 
            } 
            return visualHit as ICompositeView;
 
        }


        static ICompositeView GetContainerForPaste(ModelItem pasteModelItem, Point clickPoint) 
        {
            ICompositeView pasteContainer = null; 
 
            if (null != pasteModelItem && null != pasteModelItem.View && pasteModelItem.View is WorkflowViewElement)
            { 
                pasteContainer = ((WorkflowViewElement)pasteModelItem.View).ActiveCompositeView;
            }
            if (null == pasteContainer)
            { 
                //Get clicked container.
                if (clickPoint.X > 0 && clickPoint.Y > 0) 
                { 
                    pasteContainer = GetClickedContainer(pasteModelItem, clickPoint);
                } 

                //If the container itself is a WVE, there's posibility that it's collapsed.
                //Thus, we need to check this as well.
                if (pasteContainer != null && pasteContainer is WorkflowViewElement) 
                {
                    WorkflowViewElement view = pasteContainer as WorkflowViewElement; 
                    if (!view.ShowExpanded) 
                    {
                        pasteContainer = null; 
                    }
                }
                else if (pasteContainer == null) //If the modelitem.View itself is a container.
                { 
                    WorkflowViewElement view = pasteModelItem.View as WorkflowViewElement;
                    if (view != null && view.ShowExpanded) 
                    { 
                        pasteContainer = pasteModelItem.View as ICompositeView;
                    } 
                }

                //Get the container registered with modelItem.View if unambigous
                //If nothing works take the container with keyboard focus if one exists. 
                if (pasteContainer == null)
                { 
                    HashSet childrenContainers = CutCopyPasteHelper.GetChildContainers(pasteModelItem.View as WorkflowViewElement); 
                    if ((childrenContainers == null || childrenContainers.Count == 0) && null != pasteModelItem.View)
                    { 
                        pasteContainer = (ICompositeView)DragDropHelper.GetCompositeView((WorkflowViewElement)pasteModelItem.View);
                    }
                    else if (null != childrenContainers && childrenContainers.Count == 1)
                    { 
                        pasteContainer = new List(childrenContainers)[0];
                    } 
                    else 
                    {
                        pasteContainer = Keyboard.FocusedElement as ICompositeView; 
                    }

                }
            } 
            return pasteContainer;
        } 
 

        internal static void PutOnClipBoard(List selectedData, List metaData) 
        {
            CutCopyPasteHelper.workflowCallbackContext = null;
            if (selectedData != null)
            { 
                ClipboardData clipboardData = new ClipboardData();
                clipboardData.Data = selectedData; 
                clipboardData.Metadata = metaData; 
                clipboardData.Version = versionInfo;
                string clipBoardString = XamlServices.Save(clipboardData); 

                DataObject dataObject = new DataObject(WorkflowClipboardFormat, clipBoardString);
                dataObject.SetData(DataFormats.Text, clipBoardString);
                RetriableClipboard.SetDataObject(dataObject, true); 
            }
        } 
 
        //PutCallbackOnClipBoard - tries to put into private (this application only) clipboard a callback
        //to a method. The method will be invoked when user retrieves clipboard content - i.e. by 
        //calling a paste command.
        //the callback has to be:
        //- static method
        //- have return value (not void) 
        //- takes 2 input parameters:
        //   * 1 parameter is modelitem - this is a target modelitem upon which callback is to be executed 
        //   * 2 parameter is user provided context - any object. Since this callback will be executed within 
        //    this application only, there is no need for context to be serializable.
        internal static void PutCallbackOnClipBoard(Func callbackMethod, Type callbackResultType, object context) 
        {
            if (null == callbackMethod || null == context)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException(null == callbackMethod ? "callbackMethod" : "context")); 
            }
            ClipboardData clipboardData = new ClipboardData(); 
            List data = new List(); 
            data.Add(callbackResultType);
            data.Add(callbackMethod); 
            clipboardData.Data = data;
            clipboardData.Version = versionInfo;
            CutCopyPasteHelper.workflowCallbackContext = context;
            try 
            {
                RetriableClipboard.SetDataObject(new DataObject(WorkflowCallbackClipboardFormat, clipboardData, false), false); 
            } 
            catch (ExternalException e)
            { 
                ErrorReporting.ShowErrorMessage(e.Message);
            }
        }
 

        //This method returns the list of objects put on clipboard by cut/copy. 
        //Out parameter is the metaData information. 
        [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DoNotCatchGeneralExceptionTypes,
            Justification = "Deserialization of cliboard data might fail. Propagating exceptions might lead to VS crash.")] 
        [SuppressMessage("Reliability", "Reliability108",
            Justification = "Deserialization of cliboard data might fail. Propagating exceptions might lead to VS crash.")]
        internal static List GetFromClipboard(out List metaData)
        { 
            DataObject dataObject = RetriableClipboard.GetDataObject() as DataObject;
            List workflowData = null; 
            metaData = null; 
            if (dataObject != null)
            { 
                if (dataObject.GetDataPresent(WorkflowClipboardFormat))
                {
                    string clipBoardString = (string)dataObject.GetData(WorkflowClipboardFormat);
                    using (StringReader reader = new StringReader(clipBoardString)) 
                    {
                        try 
                        { 
                            ClipboardData clipboardData = (ClipboardData)XamlServices.Load(reader);
                            metaData = clipboardData.Metadata; 
                            workflowData = clipboardData.Data;
                        }
                        catch (Exception e)
                        { 
                            Trace.WriteLine(e.Message);
                        } 
                    } 
                }
                else if (dataObject.GetDataPresent(WorkflowCallbackClipboardFormat)) 
                {
                    ClipboardData localData = (ClipboardData)dataObject.GetData(WorkflowCallbackClipboardFormat);
                    metaData = null;
                    workflowData = localData.Data; 
                    workflowData.Add(CutCopyPasteHelper.workflowCallbackContext);
                } 
            } 
            return workflowData;
        } 

        static bool CanCopy(Type type)
        {
            foreach (Type disallowedType in CutCopyPasteHelper.DisallowedTypesForCopy) 
            {
                if(disallowedType.IsAssignableFrom(type)) 
                { 
                    return false;
                } 
                if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(disallowedType))
                {
                    return false;
                } 
            }
            return true; 
        } 

        static bool CanCopy(ModelItem item) 
        {
            return null != item.View && item.View is WorkflowViewElement &&
                null != ((WorkflowViewElement)item.View).ModelItem &&
                CanCopy(((WorkflowViewElement)item.View).ModelItem.ItemType); 
        }
 
        public static bool CanCopy(EditingContext context) 
        {
            if (context == null) 
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            }
            Selection selection = context.Items.GetValue(); 
            return selection.SelectionCount > 0 && selection.SelectedObjects.All(p => CanCopy(p));
        } 
 
        public static bool CanCut(EditingContext context)
        { 
            if (context == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            } 
            bool result = false;
            Selection selection = context.Items.GetValue(); 
            if (null != selection && selection.SelectionCount > 0) 
            {
                DesignerView designerView = context.Services.GetService(); 
                result = selection.SelectedObjects.All(p =>
                    CanCopy(p) && !p.View.Equals(designerView.RootDesigner));
            }
            return result; 
        }
 
        public static bool CanPaste(EditingContext context) 
        {
            if (context == null) 
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("context"));
            }
            bool result = false; 
            ModelItem primarySelection = context.Items.GetValue().PrimarySelection;
            if (null != primarySelection) 
            { 
                ICompositeView container = GetContainerForPaste(primarySelection, new Point(-1, -1));
                if (null != container) 
                {
                    DataObject dataObject = RetriableClipboard.GetDataObject() as DataObject;
                    if (null != dataObject)
                    { 
                        List metaData = null;
                        List itemsToPaste = null; 
                        try 
                        {
                            if (dataObject.GetDataPresent(WorkflowClipboardFormat)) 
                            {
                                itemsToPaste = GetFromClipboard(out metaData);
                                result = container.CanPasteItems(itemsToPaste);
                            } 
                            else if (dataObject.GetDataPresent(WorkflowCallbackClipboardFormat))
                            { 
                                itemsToPaste = GetFromClipboard(out metaData); 
                                result = container.CanPasteItems(itemsToPaste.GetRange(0, 1));
                            } 
                        }
                        //This is being defensive for the case where user code for CanPasteITems throws a non-fatal exception.
                        catch (Exception exp)
                        { 
                            if (Fx.IsFatal(exp))
                            { 
                                throw; 
                            }
                        } 
                    }
                }
            }
            return result; 
        }
 
    } 

 

}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK