[corlib] Update ValueTuple implementation
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / DragDropHelper.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 namespace System.Activities.Presentation
6 {
7     using System.Diagnostics;
8     using System.Windows;
9     using System.Windows.Documents;
10     using System.Windows.Media;
11     using System.Windows.Shapes;
12     using System.Activities.Presentation.Hosting;
13     using System.Activities.Presentation.Model;
14     using System.Activities.Presentation.View;
15     using System.Windows.Threading;
16     using System.Diagnostics.CodeAnalysis;
17     using System.Globalization;
18     using System.Activities.Presentation.Internal.PropertyEditing;
19     using System.Runtime;
20     using System.Reflection;
21     using System.Activities.Presentation.Sqm;
22     using System.Collections.Generic;
23     using System.Windows.Controls;
24     using System.Linq;
25     using Microsoft.Activities.Presentation;
26
27
28     // This is a helper class for making dragdrop inside workflow designer easy. This abstracts out te encoding formats used in the
29     // DataObject that is passed on from Drag source to target.
30     public static class DragDropHelper
31     {
32         // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
33         public static readonly DependencyProperty DragSourceProperty =
34             DependencyProperty.RegisterAttached("DragSource", typeof(UIElement), typeof(DragDropHelper), new UIPropertyMetadata(null));
35         public static readonly string ModelItemDataFormat;
36         public static readonly string CompositeViewFormat;
37         public static readonly string CompletedEffectsFormat = "DragCompletedEffectsFormat";
38         public static readonly string WorkflowItemTypeNameFormat = "WorkflowItemTypeNameFormat";
39         public static readonly string DragAnchorPointFormat;
40         internal static readonly string ModelItemsDataFormat;
41         internal static readonly string MovedViewElementsFormat;
42
43         [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
44         static DragDropHelper()
45         {
46             string postfix = Guid.NewGuid().ToString();
47             //set per process unique data format names - this will disable possibility of trying to drag & drop operation 
48             //between designers in two different VS instances (use Cut-Copy-Paste for that)
49             ModelItemDataFormat = string.Format(CultureInfo.InvariantCulture, "ModelItemFormat_{0}", postfix);
50             CompositeViewFormat = string.Format(CultureInfo.InvariantCulture, "CompositeViewFormat_{0}", postfix);
51             DragAnchorPointFormat = string.Format(CultureInfo.InvariantCulture, "DragAnchorFormat_{0}", postfix);
52             ModelItemsDataFormat = string.Format(CultureInfo.InvariantCulture, "ModelItemsFormat_{0}", postfix);
53             MovedViewElementsFormat = string.Format(CultureInfo.InvariantCulture, "MovedViewElementsFormat_{0}", postfix);
54         }
55
56         [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
57         public static void SetCompositeView(WorkflowViewElement workflowViewElement, UIElement dragSource)
58         {
59             if (workflowViewElement == null)
60             {
61                 throw FxTrace.Exception.ArgumentNull("workflowViewElement");
62             }
63             if (dragSource == null)
64             {
65                 throw FxTrace.Exception.ArgumentNull("dragSource");
66             }
67             workflowViewElement.SetValue(DragDropHelper.DragSourceProperty, dragSource);
68         }
69
70         [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
71         public static UIElement GetCompositeView(WorkflowViewElement workflowViewElement)
72         {
73             if (workflowViewElement == null)
74             {
75                 throw FxTrace.Exception.ArgumentNull("workflowViewElement");
76             }
77             return (UIElement)workflowViewElement.GetValue(DragDropHelper.DragSourceProperty);
78         }
79
80         internal static DataObject DoDragMoveImpl(IEnumerable<WorkflowViewElement> draggedViewElements, Point referencePoint)
81         {
82             List<ModelItem> draggedModelItems = new List<ModelItem>();
83             bool first = true;
84             WorkflowViewElement viewElement = null;
85             foreach (WorkflowViewElement view in draggedViewElements)
86             {
87                 if (view != null)
88                 {
89                     if (first)
90                     {
91                         viewElement = view;
92                         first = false;
93                     }
94                     draggedModelItems.Add(view.ModelItem);
95                     view.IsHitTestVisible = false;
96                 }
97             }
98             DataObject dataObject = new DataObject(ModelItemsDataFormat, draggedModelItems);
99
100             // For compatiblity
101             if (viewElement != null)
102             {
103                 dataObject.SetData(ModelItemDataFormat, viewElement.ModelItem);
104                 dataObject.SetData(CompositeViewFormat, GetCompositeView(viewElement));
105             }
106
107             dataObject.SetData(DragAnchorPointFormat, referencePoint);
108
109             if (viewElement != null)
110             {
111                 DesignerView designerView = viewElement.Context.Services.GetService<DesignerView>();
112                 ViewElementDragShadow dragShadow = new ViewElementDragShadow(designerView.scrollableContent, draggedViewElements, referencePoint, designerView.ZoomFactor);
113                 designerView.BeginDragShadowTracking(dragShadow);
114                 //whenever drag drop fails - ensure getting rid of drag shadow
115                 try
116                 {
117                     DragDrop.DoDragDrop(designerView, dataObject, DragDropEffects.Move | DragDropEffects.Copy | DragDropEffects.Scroll | DragDropEffects.Link);
118                 }
119                 catch
120                 {
121                     //let the caller handle exception
122                     throw;
123                 }
124                 finally
125                 {
126                     designerView.EndDragShadowTracking(dragShadow);
127                     foreach (WorkflowViewElement view in draggedViewElements)
128                     {
129                         if (view != null)
130                         {
131                             view.IsHitTestVisible = true;
132                         }
133                     }
134                 }
135             }
136             return dataObject;
137         }
138
139         [Obsolete("This method does not support dragging multiple items. Use \"public static IEnumerable<WorkflowViewElement> DoDragMove(IEnumerable<WorkflowViewElement> draggedViewElements, Point referencePoint)\" instead.")]
140         public static DragDropEffects DoDragMove(WorkflowViewElement draggedViewElement, Point referencePoint)
141         {
142             if (draggedViewElement == null)
143             {
144                 throw FxTrace.Exception.ArgumentNull("draggedViewElement");
145             }
146             if (referencePoint == null)
147             {
148                 throw FxTrace.Exception.ArgumentNull("referencePoint");
149             }
150             ModelItem draggedActivityModelItem = draggedViewElement.ModelItem;
151             DataObject dataObject = new DataObject(ModelItemDataFormat, draggedActivityModelItem);
152             dataObject.SetData(CompositeViewFormat, GetCompositeView(draggedViewElement));
153             dataObject.SetData(DragAnchorPointFormat, referencePoint);
154             List<ModelItem> draggedModelItems = new List<ModelItem>();
155             draggedModelItems.Add(draggedActivityModelItem);
156             dataObject.SetData(ModelItemsDataFormat, draggedModelItems);
157
158             DesignerView view = draggedViewElement.Context.Services.GetService<DesignerView>();
159             ViewElementDragShadow dragShadow = new ViewElementDragShadow(view.scrollableContent, draggedViewElement, referencePoint, view.ZoomFactor);
160
161             draggedViewElement.IsHitTestVisible = false;
162             view.BeginDragShadowTracking(dragShadow);
163
164             //whenever drag drop fails - ensure getting rid of drag shadow
165             try
166             {
167                 DragDrop.DoDragDrop(GetCompositeView(draggedViewElement), dataObject, DragDropEffects.Move | DragDropEffects.Copy | DragDropEffects.Scroll | DragDropEffects.Link);
168             }
169             catch
170             {
171                 //let the caller handle exception
172                 throw;
173             }
174             finally
175             {
176                 view.EndDragShadowTracking(dragShadow);
177                 draggedViewElement.IsHitTestVisible = true;
178             }
179
180             return GetDragDropCompletedEffects(dataObject);
181         }
182
183         public static bool AllowDrop(IDataObject draggedDataObject, EditingContext context, params Type[] allowedItemTypes)
184         {
185
186             if (draggedDataObject == null)
187             {
188                 throw FxTrace.Exception.ArgumentNull("draggedDataObject");
189             }
190             if (context == null)
191             {
192                 throw FxTrace.Exception.ArgumentNull("context");
193             }
194             if (allowedItemTypes == null)
195             {
196                 throw FxTrace.Exception.ArgumentNull("allowedItemTypes");
197             }
198             ReadOnlyState readOnlyState = context.Items.GetValue<ReadOnlyState>();
199             if (readOnlyState != null && readOnlyState.IsReadOnly)
200             {
201                 return false;
202             }
203             if (!AllowDrop(draggedDataObject, context))
204             {
205                 return false;
206             }
207             List<Type> draggedTypes = GetDraggedTypes(draggedDataObject);
208             return draggedTypes != null
209                 && draggedTypes.Count != 0
210                 && draggedTypes.All<Type>((p) =>
211                 {
212                     for (int i = 0; i < allowedItemTypes.Length; ++i)
213                     {
214                         if (allowedItemTypes[i] == null)
215                         {
216                             throw FxTrace.Exception.ArgumentNull(string.Format(CultureInfo.InvariantCulture, "allowedItemTypes[{0}]", i));
217                         }
218                         if (AllowDrop(p, allowedItemTypes[i]))
219                         {
220                             return true;
221                         }
222                     }
223                     return false;
224                 });
225         }
226
227         static bool AllowDrop(IDataObject draggedDataObject, EditingContext context)
228         {
229             ModelItem droppedModelItem = draggedDataObject.GetData(ModelItemDataFormat) as ModelItem;
230             if (droppedModelItem == null)
231             {
232                 return true;
233             }
234             return ((IModelTreeItem)droppedModelItem).ModelTreeManager.Context.Equals(context);
235         }
236
237         internal static bool AllowDrop(Type draggedType, Type allowedItemType)
238         {
239             if (draggedType == null)
240             {
241                 // This is the case where some external stuff (e.g. Recycle bin) get dragged over.
242                 return false;
243             }
244             // This is a special case in GetDroppedObject() and replicated here.
245             // Check whether dragged type is IActivityTemplateFactory, if true, use Factory's implement type instead.
246             Type factoryType;
247             if (draggedType.TryGetActivityTemplateFactory(out factoryType))
248             {
249                 draggedType = factoryType;
250             }
251
252             if (allowedItemType.IsAssignableFrom(draggedType))
253             {
254                 return true;
255             }
256             else if (allowedItemType.IsGenericTypeDefinition && draggedType.IsGenericType)
257             {
258                 // We don't have inheritance relationship for GenericTypeDefinition, therefore the right check is equality
259                 return allowedItemType.Equals(draggedType.GetGenericTypeDefinition());
260             }
261             else if (allowedItemType.IsGenericType && draggedType.IsGenericTypeDefinition)
262             {
263                 // Allow GenericTypeDefinition to be dropped with GenericType constraint, if user select a correct argument type, drop should work.
264                 return draggedType.Equals(allowedItemType.GetGenericTypeDefinition());
265             }
266             else if (allowedItemType.IsGenericType && draggedType.IsGenericType && draggedType.ContainsGenericParameters)
267             {
268                 // If the draggedType is generic type but it contains generic parameters, which may happen to match the constraint.
269                 return allowedItemType.GetGenericTypeDefinition() == draggedType.GetGenericTypeDefinition();
270             }
271             else
272             {
273                 return false;
274             }
275         }
276
277         internal static List<Type> GetDraggedTypes(IDataObject draggedDataObject)
278         {
279             List<Type> types = new List<Type>();
280             if (draggedDataObject != null)
281             {
282                 if (draggedDataObject.GetDataPresent(ModelItemsDataFormat))
283                 {
284                     IEnumerable<ModelItem> modelItems = draggedDataObject.GetData(ModelItemsDataFormat) as IEnumerable<ModelItem>;
285                     foreach (ModelItem modelItem in modelItems)
286                     {
287                         if (modelItem != null)
288                         {
289                             types.Add(modelItem.ItemType);
290                         }
291                     }
292                 }
293                 else if (draggedDataObject.GetDataPresent(ModelItemDataFormat))
294                 {
295                     ModelItem modelItem = draggedDataObject.GetData(ModelItemDataFormat) as ModelItem;
296                     if (modelItem != null)
297                     {
298                         types.Add(modelItem.ItemType);
299                     }
300                 }
301
302                 // This is an object dragged from somewhere else other than from within the designer surface
303                 if (draggedDataObject.GetDataPresent(WorkflowItemTypeNameFormat))
304                 {
305                     // This is the case where the object is dropped from the toolbox
306                     string text = draggedDataObject.GetData(WorkflowItemTypeNameFormat) as string;
307                     if (!string.IsNullOrEmpty(text))
308                     {
309                         types.Add(Type.GetType(text));
310                     }
311                 }
312             }
313
314             return types;
315         }
316
317         internal static bool IsDraggingFromToolbox(DragEventArgs e)
318         {
319             return e.Data.GetDataPresent(WorkflowItemTypeNameFormat);
320         }
321
322         public static IEnumerable<object> GetDroppedObjects(DependencyObject dropTarget, DragEventArgs e, EditingContext context)
323         {
324             List<object> droppedObjects = new List<object>();
325             if (e.Data.GetDataPresent(ModelItemsDataFormat))
326             {
327                 IEnumerable<ModelItem> droppedModelItems = e.Data.GetData(ModelItemsDataFormat) as IEnumerable<ModelItem>;
328                 foreach (ModelItem modelItem in droppedModelItems)
329                 {
330                     droppedObjects.Add(modelItem);
331                 }
332             }
333             else
334             {
335                 object droppedObject = e.Data.GetData(ModelItemDataFormat) as ModelItem;
336                 // could have been dropped from toolbox.
337                 if (droppedObject == null)
338                 {
339                     Type type = null;
340                     if (e.Data.GetDataPresent(WorkflowItemTypeNameFormat))
341                     {
342                         string text = e.Data.GetData(WorkflowItemTypeNameFormat) as string;
343                         if (!string.IsNullOrEmpty(text))
344                         {
345                             //try to use the text format to see if it holds a type name and  try to create an object out of it.
346                             type = Type.GetType(text);
347                         }
348                     }
349                     droppedObject = GetDroppedObjectInstance(dropTarget, context, type, e.Data);
350                 }
351                 if (droppedObject != null)
352                 {
353                     droppedObjects.Add(droppedObject);
354                 }
355             }
356             e.Handled = true;
357             context.Services.GetService<DesignerPerfEventProvider>().WorkflowDesignerDrop();
358
359             Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
360                     new Action(() =>
361                     {
362                         context.Services.GetService<DesignerPerfEventProvider>().WorkflowDesignerIdleAfterDrop();
363
364                     }));
365             return droppedObjects;
366         }
367
368         internal static void ValidateItemsAreOnView(IList<ModelItem> items, ICollection<ModelItem> modelItemsOnView)
369         {
370             Fx.Assert(items != null, "items");
371             Fx.Assert(modelItemsOnView != null, "modelItemsOnView");
372
373             for (int index = 0; index < items.Count; ++index)
374             {
375                 if (!modelItemsOnView.Contains(items[index]))
376                 {
377                     throw FxTrace.Exception.AsError(
378                         new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.Error_ItemNotOnView, index)));
379                 }
380             }
381         }
382
383         [Obsolete("This method does not support dropping multiple items. Use \"public static IEnumerable<object> GetDroppedObjects(DependencyObject dropTarget, DragEventArgs e, EditingContext context)\" instead.")]
384         public static object GetDroppedObject(DependencyObject dropTarget, DragEventArgs e, EditingContext context)
385         {
386             IEnumerable<object> droppedObjects = GetDroppedObjects(dropTarget, e, context);
387             if (droppedObjects.Count() > 0)
388             {
389                 return droppedObjects.First();
390             }
391             return null;
392         }
393
394         [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DoNotCatchGeneralExceptionTypes,
395             Justification = "Any exception can be thrown from custom code. We don't want to crash VS.")]
396         [SuppressMessage("Reliability", "Reliability108",
397             Justification = "Any exception can be thrown from custom code. We don't want to crash VS.")]
398         internal static object GetDroppedObjectInstance(DependencyObject dropTarget, EditingContext context, Type type, IDataObject dataObject)
399         {
400             if (type != null)
401             {
402                 //check if type is generic
403                 if (type.IsGenericTypeDefinition)
404                 {
405                     type = ResolveGenericParameters(dropTarget, context, type);
406                 }
407             }
408
409             object droppedObject = null;
410             if (null != type)
411             {
412                 try
413                 {
414                     droppedObject = Activator.CreateInstance(type);
415
416                     if (type.IsActivityTemplateFactory() && type.IsClass)
417                     {
418                         //find parent WorkflowViewElement - in case of mouse drop, current drop target most likely is ISourceContainer
419                         if (!(dropTarget is WorkflowViewElement))
420                         {
421                             dropTarget = VisualTreeUtils.FindVisualAncestor<WorkflowViewElement>(dropTarget);
422                         }
423
424                         Type templateFactoryInterface2 = type.GetInterface(typeof(IActivityTemplateFactory<>).FullName);
425                         if (templateFactoryInterface2 != null)
426                         {
427                             droppedObject = templateFactoryInterface2.InvokeMember("Create", BindingFlags.InvokeMethod, null, droppedObject, new object[] { dropTarget, dataObject }, CultureInfo.InvariantCulture);
428                         }
429                         else if (droppedObject is IActivityTemplateFactory)
430                         {
431                             droppedObject = ((IActivityTemplateFactory)droppedObject).Create(dropTarget);
432                         }
433
434                     }
435
436                     // SQM: Log activity usage count
437                     ActivityUsageCounter.ReportUsage(context.Services.GetService<IVSSqmService>(), type);
438                 }
439                 catch (Exception ex)
440                 {
441                     if (Fx.IsFatal(ex))
442                     {
443                         throw;
444                     }
445
446                     string details = ex.Message;
447                     if (ex is TargetInvocationException && ex.InnerException != null)
448                     {
449                         details = ex.InnerException.Message;
450                     }
451
452
453                     ErrorReporting.ShowErrorMessage(string.Format(CultureInfo.CurrentUICulture, SR.CannotCreateInstance, TypeNameHelper.GetDisplayName(type, false)), details);
454                 }
455             }
456
457             return droppedObject;
458         }
459
460         static Type ResolveGenericParameters(DependencyObject dropTarget, EditingContext context, Type type)
461         {
462             // look to see if there is a DefaultTypeArgumentAttribute on it 
463             DefaultTypeArgumentAttribute typeArgumentAttribute = ExtensibilityAccessor.GetAttribute<DefaultTypeArgumentAttribute>(type);
464             if (typeArgumentAttribute != null && typeArgumentAttribute.Type != null)
465             {
466                 type = type.MakeGenericType(typeArgumentAttribute.Type);
467             }
468             else //require user to resolve generic arguments
469             {
470                 ActivityTypeResolver wnd = new ActivityTypeResolver();
471                 if (null != context)
472                 {
473                     WindowHelperService service = context.Services.GetService<WindowHelperService>();
474                     if (null != service)
475                     {
476                         service.TrySetWindowOwner(dropTarget, wnd);
477                     }
478                 }
479
480                 TypeResolvingOptions dropTargetOptions = null;
481                 TypeResolvingOptions activityTypeOptions = null;
482
483                 //try to see if the container has any customization for type resolver
484                 ICompositeView container = dropTarget as ICompositeView;
485                 if (container != null)
486                 {
487                     dropTargetOptions = container.DroppingTypeResolvingOptions;
488                 }
489
490                 //try to see if the activity type in discourse has any customization for type resolver
491                 TypeResolvingOptionsAttribute attr = WorkflowViewService.GetAttribute<TypeResolvingOptionsAttribute>(type);
492                 if (attr != null)
493                 {
494                     activityTypeOptions = attr.TypeResolvingOptions;
495                 }
496                 //if both have type resolver, try to merge them
497                 TypeResolvingOptions options = TypeResolvingOptions.Merge(dropTargetOptions, activityTypeOptions);
498                 if (options != null)
499                 {
500                     wnd.Options = options;
501                 }
502
503                 wnd.Context = context;
504                 wnd.EditedType = type;
505                 wnd.Width = 340;
506                 wnd.Height = 200;
507                 type = (true == wnd.ShowDialog() ? wnd.ConcreteType : null);
508             }
509             return type;
510         }
511
512         [Obsolete("This method does not support dragging multiple items. Use \"public static IEnumerable<ModelItem> GetDraggedModelItems(DragEventArgs e)\" instead.")]
513         public static ModelItem GetDraggedModelItem(DragEventArgs e)
514         {
515             return GetDraggedModelItemInternal(e);
516         }
517
518         internal static ModelItem GetDraggedModelItemInternal(DragEventArgs e)
519         {
520             IEnumerable<ModelItem> draggedModelItems = GetDraggedModelItems(e);
521             if (draggedModelItems.Count() > 0)
522             {
523                 return draggedModelItems.First();
524             }
525             return null;
526         }
527
528         public static IEnumerable<ModelItem> GetDraggedModelItems(DragEventArgs e)
529         {
530             IEnumerable<ModelItem> draggedModelItems = e.Data.GetData(ModelItemsDataFormat) as IEnumerable<ModelItem>;
531             if (draggedModelItems != null)
532             {
533                 return draggedModelItems;
534             }
535             else
536             {
537                 ModelItem draggedItem = e.Data.GetData(ModelItemDataFormat) as ModelItem;
538                 if (draggedItem != null)
539                 {
540                     return new ModelItem[] { draggedItem };
541                 }
542             }
543             return new ModelItem[] { };
544         }
545
546         internal static bool AreListsIdenticalExceptOrder<T>(IList<T> sourceList, IList<T> destinationList)
547         {
548             // User does not 
549             // 1) introduce unseen object into the collection.
550             // 2) remove object from the collection.
551             // 3) introduce null in the collection.
552             // 4) return null
553             if (sourceList == null)
554             {
555                 return destinationList == null;
556             }
557
558             if (destinationList == null)
559             {
560                 return false;
561             }
562
563             if (sourceList.Count != destinationList.Count)
564             {
565                 return false;
566             }
567             HashSet<T> checkingMap = new HashSet<T>();
568
569             // create set
570             foreach (T item in sourceList)
571             {
572                 bool ret = checkingMap.Add(item);
573                 // an internal error, the item in src should be identical.
574                 Fx.Assert(ret, "item in source list is not identical?");
575             }
576
577             foreach (T item in destinationList)
578             {
579                 if (!checkingMap.Remove(item))
580                 {
581                     return false;
582                 }
583             }
584             return checkingMap.Count == 0;
585         }
586
587         // 1) obj with CompositeView2: sort by IMultipleDragEnabledCompositeView SortSelectedItems.
588         // 2) obj with CompoisteView: no sort.
589         // 3) obj without CompositeView: just put them at the end of the list as the order in selectedObjects.
590         internal static List<object> SortSelectedObjects(IEnumerable<object> selectedObjects)
591         {
592             //1) Separate objects
593             Dictionary<ICompositeView, List<ModelItem>> viewItemListDictionary = new Dictionary<ICompositeView, List<ModelItem>>();
594             List<object> nonCompositeView = new List<object>();
595             List<object> retList = new List<object>();
596             foreach (object obj in selectedObjects)
597             {
598                 ModelItem modelItem = obj as ModelItem;
599                 if (modelItem == null || modelItem.View == null)
600                 {
601                     nonCompositeView.Add(obj);
602                     continue;
603                 }
604                 ICompositeView container = DragDropHelper
605                     .GetCompositeView(modelItem.View as WorkflowViewElement) as ICompositeView;
606                 if (container == null)
607                 {
608                     nonCompositeView.Add(obj);
609                     continue;
610                 }
611
612                 // add to dictionary.
613                 if (!viewItemListDictionary.ContainsKey(container))
614                 {
615                     viewItemListDictionary.Add(container, new List<ModelItem>());
616                 }
617
618                 viewItemListDictionary[container].Add(modelItem);
619             }
620
621             // 2) sort when possible
622             foreach (KeyValuePair<ICompositeView, List<ModelItem>> pair in viewItemListDictionary)
623             {
624                 IMultipleDragEnabledCompositeView view2 = pair.Key as IMultipleDragEnabledCompositeView;
625                 List<ModelItem> sortedList = view2 == null ?
626                     pair.Value : view2.SortSelectedItems(new List<ModelItem>(pair.Value));
627                 if (!AreListsIdenticalExceptOrder(pair.Value, sortedList))
628                 {
629                     // check consistens.
630                     throw FxTrace.Exception.AsError(
631                         new InvalidOperationException(SR.Error_BadOutputFromSortSelectedItems));
632                 }
633                 retList.AddRange(sortedList);
634             }
635
636             retList.AddRange(nonCompositeView);
637             return retList;
638         }
639
640         [Obsolete("This method does not support dragging multiple items. Use \"public static UIElement GetCompositeView(WorkflowViewElement workflowViewElement)\" instead.")]
641         public static ICompositeView GetCompositeView(DragEventArgs e)
642         {
643             return (ICompositeView)e.Data.GetData(CompositeViewFormat);
644         }
645
646         public static Point GetDragDropAnchorPoint(DragEventArgs e)
647         {
648             Point referencePoint;
649             if (e.Data.GetDataPresent(DragAnchorPointFormat))
650             {
651                 referencePoint = (Point)e.Data.GetData(DragAnchorPointFormat);
652             }
653             else
654             {
655                 referencePoint = new Point(-1, -1);
656             }
657             return referencePoint;
658         }
659
660         [Obsolete("This method does not support dragging multiple items. Consider using \"public static void SetDragDropMovedViewElements(DragEventArgs e, IEnumerable<WorkflowViewElement> movedViewElements)\" instead.")]
661         public static void SetDragDropCompletedEffects(DragEventArgs e, DragDropEffects completedEffects)
662         {
663             try
664             {
665                 e.Data.SetData(CompletedEffectsFormat, completedEffects);
666             }
667             catch (InvalidOperationException exception)
668             {
669                 Trace.WriteLine(exception.ToString());
670             }
671         }
672
673         [Obsolete("This method does not support dragging multiple items. Consider using \"public static IEnumerable<WorkflowViewElement> GetDragDropMovedViewElements(DataObject data)\" instead.")]
674         public static DragDropEffects GetDragDropCompletedEffects(DataObject data)
675         {
676             if (data == null)
677             {
678                 throw FxTrace.Exception.ArgumentNull("data");
679             }
680             DragDropEffects completedEffects = DragDropEffects.None;
681             if (data.GetDataPresent(CompletedEffectsFormat))
682             {
683                 completedEffects = (DragDropEffects)data.GetData(CompletedEffectsFormat);
684             }
685             return completedEffects;
686         }
687
688         internal static void SetDragDropMovedViewElements(DragEventArgs e, IEnumerable<WorkflowViewElement> movedViewElements)
689         {
690             if (e == null)
691             {
692                 throw FxTrace.Exception.ArgumentNull("e");
693             }
694
695             if (movedViewElements == null)
696             {
697                 throw FxTrace.Exception.ArgumentNull("movedViewElements");
698             }
699
700             try
701             {
702                 e.Data.SetData(MovedViewElementsFormat, movedViewElements);
703             }
704             catch (InvalidOperationException exception)
705             {
706                 Trace.WriteLine(exception.ToString());
707             }
708         }
709
710         internal static IEnumerable<WorkflowViewElement> GetDragDropMovedViewElements(DataObject data)
711         {
712             if (data == null)
713             {
714                 throw FxTrace.Exception.ArgumentNull("data");
715             }
716
717             if (data.GetDataPresent(MovedViewElementsFormat))
718             {
719                 return (IEnumerable<WorkflowViewElement>)data.GetData(MovedViewElementsFormat);
720             }
721             return null;
722         }
723
724         internal static int GetDraggedObjectCount(DragEventArgs e)
725         {
726             return GetDraggedTypes(e.Data).Count;
727         }
728
729         internal static Dictionary<WorkflowViewElement, Point> GetViewElementRelativeLocations(IEnumerable<WorkflowViewElement> viewElements)
730         {
731             DesignerView designerView = null;
732             Dictionary<WorkflowViewElement, Point> locations = new Dictionary<WorkflowViewElement, Point>();
733             Point topLeftPoint = new Point(double.PositiveInfinity, double.PositiveInfinity);
734             foreach (WorkflowViewElement viewElement in viewElements)
735             {
736                 if (designerView == null)
737                 {
738                     designerView = viewElement.Context.Services.GetService<DesignerView>();
739                 }
740
741                 Point location = new Point(0, 0);
742                 if (designerView.scrollableContent.IsAncestorOf(viewElement))
743                 {
744                     GeneralTransform transform = viewElement.TransformToAncestor(designerView.scrollableContent);
745                     location = transform.Transform(new Point(0, 0));
746                 }
747
748                 if (location.X < topLeftPoint.X)
749                 {
750                     topLeftPoint.X = location.X;
751                 }
752
753                 if (location.Y < topLeftPoint.Y)
754                 {
755                     topLeftPoint.Y = location.Y;
756                 }
757
758                 locations.Add(viewElement, location);
759             }
760
761             foreach (WorkflowViewElement viewElement in viewElements)
762             {
763                 locations[viewElement] = Vector.Add(new Vector(-topLeftPoint.X, -topLeftPoint.Y), locations[viewElement]);
764             }
765             return locations;
766         }
767
768         internal static Dictionary<WorkflowViewElement, Point> GetDraggedViewElementRelativeLocations(DragEventArgs e)
769         {
770             List<WorkflowViewElement> draggedViewElements = new List<WorkflowViewElement>();
771             if (e.Data.GetDataPresent(ModelItemsDataFormat))
772             {
773                 IEnumerable<ModelItem> draggedModelItems = e.Data.GetData(ModelItemsDataFormat) as IEnumerable<ModelItem>;
774                 if (draggedModelItems != null)
775                 {
776                     foreach (ModelItem draggedModelItem in draggedModelItems)
777                     {
778                         if (draggedModelItem != null && draggedModelItem.View != null)
779                         {
780                             draggedViewElements.Add((WorkflowViewElement)draggedModelItem.View);
781                         }
782                     }
783                 }
784             }
785             else if (e.Data.GetDataPresent(ModelItemDataFormat))
786             {
787                 ModelItem draggedModelItem = e.Data.GetData(ModelItemDataFormat) as ModelItem;
788                 if (draggedModelItem != null && draggedModelItem.View != null)
789                 {
790                     draggedViewElements.Add((WorkflowViewElement)draggedModelItem.View);
791                 }
792             }
793             return GetViewElementRelativeLocations(draggedViewElements);
794         }
795
796         // Get rid of descendant model items when both ancestor and descendant and ancestor model items are selected
797         internal static IEnumerable<ModelItem> GetModelItemsToDrag(IEnumerable<ModelItem> modelItems)
798         {
799             HashSet<ModelItem> modelItemsToDrag = new HashSet<ModelItem>();
800             foreach (ModelItem modelItem in modelItems)
801             {
802                 HashSet<ModelItem> parentModelItems = CutCopyPasteHelper.GetSelectableParentModelItems(modelItem);
803                 parentModelItems.IntersectWith(modelItems);
804                 if (parentModelItems.Count == 0)
805                 {
806                     modelItemsToDrag.Add(modelItem);
807                 }
808             }
809             return modelItemsToDrag;
810         }
811
812
813         internal sealed class ViewElementDragShadow : Adorner
814         {
815             Rectangle content;
816             double x;
817             double y;
818             double offsetX;
819             double offsetY;
820             double scaleFactor;
821             double width;
822             double height;
823             AdornerLayer layer;
824
825             public ViewElementDragShadow(UIElement owner, WorkflowViewElement viewElement, Point offset, double scaleFactor)
826                 : base(owner)
827             {
828                 Rect bounds = VisualTreeHelper.GetDescendantBounds(viewElement);
829                 this.width = bounds.Width;
830                 this.height = bounds.Height;
831
832                 this.content = new Rectangle()
833                 {
834                     Width = this.width,
835                     Height = this.height,
836                     Fill = new VisualBrush(viewElement)
837                     {
838                         Opacity = 0.6
839                     }
840                 };
841                 this.InitializeCommon(offset, scaleFactor);
842             }
843
844             public ViewElementDragShadow(UIElement owner, IEnumerable<WorkflowViewElement> viewElements, Point offset, double scaleFactor)
845                 : base(owner)
846             {
847                 Dictionary<WorkflowViewElement, Point> locations = DragDropHelper.GetViewElementRelativeLocations(viewElements);
848
849                 Grid grid = new Grid();
850                 foreach (WorkflowViewElement viewElement in viewElements)
851                 {
852                     Rect bounds = VisualTreeHelper.GetDescendantBounds(viewElement);
853                     Rectangle rectangle = new Rectangle()
854                     {
855                         Width = bounds.Width,
856                         Height = bounds.Height,
857                         Fill = new VisualBrush(viewElement),
858                         Margin = new Thickness(locations[viewElement].X, locations[viewElement].Y, 0, 0),
859                         VerticalAlignment = VerticalAlignment.Top,
860                         HorizontalAlignment = HorizontalAlignment.Left
861                     };
862                     grid.Children.Add(rectangle);
863                 }
864                 grid.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
865                 this.width = grid.DesiredSize.Width;
866                 this.height = grid.DesiredSize.Height;
867
868                 this.content = new Rectangle()
869                 {
870                     Width = this.width,
871                     Height = this.height,
872                     Fill = new VisualBrush(grid)
873                     {
874                         Opacity = 0.6
875                     }
876                 };
877                 this.InitializeCommon(offset, scaleFactor);
878             }
879
880             internal void UpdatePosition(double x, double y)
881             {
882                 if (this.Visibility == Visibility.Hidden)
883                 {
884                     this.Visibility = Visibility.Visible;
885                 }
886                 double oldX = this.x;
887                 double oldY = this.y;
888                 this.x = x - this.offsetX;
889                 this.y = y - this.offsetY;
890                 if (oldX != this.x || oldY != this.y)
891                 {
892                     this.layer = this.Parent as AdornerLayer;
893                     this.layer.Update(this.AdornedElement);
894                 }
895             }
896
897             public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
898             {
899                 GeneralTransformGroup result = new GeneralTransformGroup();
900                 result.Children.Add(new TranslateTransform(this.x, this.y));
901                 result.Children.Add(new ScaleTransform(this.scaleFactor, this.scaleFactor, this.x, this.y));
902                 return result;
903             }
904
905             protected override Visual GetVisualChild(int index)
906             {
907                 return this.content;
908             }
909
910             protected override int VisualChildrenCount
911             {
912                 get { return 1; }
913             }
914
915             protected override Size ArrangeOverride(Size finalSize)
916             {
917                 this.content.Arrange(new Rect(this.content.DesiredSize));
918                 System.Diagnostics.Debug.WriteLine("DragShadow.ArrangeOverride " + this.content.DesiredSize);
919                 return this.content.DesiredSize;
920             }
921
922             protected override Size MeasureOverride(Size constraint)
923             {
924                 this.content.Measure(constraint);
925                 System.Diagnostics.Debug.WriteLine("DragShadow.MeasureOverride " + this.content.DesiredSize);
926                 return this.content.DesiredSize;
927             }
928
929             private void InitializeCommon(Point offset, double scaleFactor)
930             {
931                 this.offsetX = offset.X * scaleFactor;
932                 this.offsetY = offset.Y * scaleFactor;
933                 this.Visibility = Visibility.Hidden;
934                 this.scaleFactor = scaleFactor;
935             }
936         }
937     }
938 }