38e814df3b54b21ded896b04dc2c0cb5a723a819
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / WorkflowItemsPresenter.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 #pragma warning disable 618
6
7 namespace System.Activities.Presentation
8 {
9     using System.Activities.Presentation.Internal.PropertyEditing;
10     using System.Activities.Presentation.Model;
11     using System.Activities.Presentation.Services;
12     using System.Collections;
13     using System.Collections.Generic;
14     using System.Collections.Specialized;
15     using System.Diagnostics.CodeAnalysis;
16     using System.Globalization;
17     using System.Linq;
18     using System.Runtime;
19     using System.Windows;
20     using System.Windows.Automation.Peers;
21     using System.Windows.Controls;
22     using System.Windows.Input;
23     using System.Windows.Markup;
24     using System.Windows.Media;
25     using System.Windows.Threading;
26     using System.Activities.Presentation.View;
27     using System.Windows.Shapes;
28
29     // This is similar to the WorkflowItemPresenter , but its an edit box for collections. It supports drag drop, and delete.
30     // it auto refreshes the collection on collection changed events.
31     public class WorkflowItemsPresenter : ContentControl, IMultipleDragEnabledCompositeView
32     {
33
34         public static readonly DependencyProperty HintTextProperty =
35             DependencyProperty.Register("HintText", typeof(string), typeof(WorkflowItemsPresenter), new UIPropertyMetadata(String.Empty, new PropertyChangedCallback(WorkflowItemsPresenter.OnHintTextChanged)));
36
37         public static readonly DependencyProperty ItemsProperty =
38             DependencyProperty.Register("Items", typeof(ModelItemCollection), typeof(WorkflowItemsPresenter), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(WorkflowItemsPresenter.OnItemsChanged)));
39
40         public static readonly DependencyProperty SpacerTemplateProperty =
41             DependencyProperty.Register("SpacerTemplate", typeof(DataTemplate), typeof(WorkflowItemsPresenter), new UIPropertyMetadata(null));
42
43         public static readonly DependencyProperty HeaderTemplateProperty =
44             DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(WorkflowItemsPresenter), new UIPropertyMetadata(null));
45
46         public static readonly DependencyProperty FooterTemplateProperty =
47             DependencyProperty.Register("FooterTemplate", typeof(DataTemplate), typeof(WorkflowItemsPresenter), new UIPropertyMetadata(null));
48
49         public static readonly DependencyProperty ItemsPanelProperty =
50             DependencyProperty.Register("ItemsPanel", typeof(ItemsPanelTemplate), typeof(WorkflowItemsPresenter), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(WorkflowItemsPresenter.OnItemsPanelChanged)));
51
52         public static readonly DependencyProperty IndexProperty =
53             DependencyProperty.RegisterAttached("Index", typeof(int), typeof(WorkflowItemsPresenter), new UIPropertyMetadata(addAtEndMarker));
54
55         public static readonly DependencyProperty AllowedItemTypeProperty =
56             DependencyProperty.Register("AllowedItemType", typeof(Type), typeof(WorkflowItemsPresenter), new UIPropertyMetadata(typeof(object)));
57
58         public static readonly DependencyProperty IsDefaultContainerProperty =
59             DependencyProperty.Register("IsDefaultContainer", typeof(bool), typeof(WorkflowItemsPresenter), new UIPropertyMetadata(false));
60
61         public static readonly DependencyProperty DroppingTypeResolvingOptionsProperty =
62             DependencyProperty.Register("DroppingTypeResolvingOptions", typeof(TypeResolvingOptions), typeof(WorkflowItemsPresenter));
63
64
65         const int addAtEndMarker = -2;
66
67         int selectedSpacerIndex;
68
69
70         ItemsControl panel;
71         Grid hintTextGrid;
72         EditingContext context = null;
73         bool isRegisteredWithParent = false;
74         bool populateOnLoad = false;
75         bool handleSpacerGotKeyboardFocus = false;
76         Grid outerGrid;
77
78         public WorkflowItemsPresenter()
79         {
80             panel = new ItemsControl();
81             panel.Focusable = false;
82             hintTextGrid = new Grid();
83             hintTextGrid.Focusable = false;
84             hintTextGrid.Background = Brushes.Transparent;
85             hintTextGrid.DataContext = this;
86             hintTextGrid.SetBinding(Grid.MinHeightProperty, "MinHeight");
87             hintTextGrid.SetBinding(Grid.MinWidthProperty, "MinWidth");
88             TextBlock text = new TextBlock();
89             text.Focusable = false;
90             text.SetBinding(TextBlock.TextProperty, "HintText");
91             text.HorizontalAlignment = HorizontalAlignment.Center;
92             text.VerticalAlignment = VerticalAlignment.Center;
93             text.DataContext = this;
94             text.Foreground = new SolidColorBrush(SystemColors.GrayTextColor);
95             text.FontStyle = FontStyles.Italic;
96             ((IAddChild)hintTextGrid).AddChild(text);
97
98             this.outerGrid = new Grid()
99             {
100                 RowDefinitions = { new RowDefinition(), new RowDefinition() },
101                 ColumnDefinitions = { new ColumnDefinition() }
102             };
103             Grid.SetRow(this.panel, 0);
104             Grid.SetColumn(this.panel, 0);
105             Grid.SetRow(this.hintTextGrid, 1);
106             Grid.SetColumn(this.hintTextGrid, 0);
107             this.outerGrid.Children.Add(panel);
108             this.outerGrid.Children.Add(hintTextGrid);
109         }
110
111
112         public Type AllowedItemType
113         {
114             get { return (Type)GetValue(AllowedItemTypeProperty); }
115             set { SetValue(AllowedItemTypeProperty, value); }
116         }
117
118         public string HintText
119         {
120             get { return (string)GetValue(HintTextProperty); }
121             set { SetValue(HintTextProperty, value); }
122         }
123
124         [Fx.Tag.KnownXamlExternal]
125         public DataTemplate SpacerTemplate
126         {
127             get { return (DataTemplate)GetValue(SpacerTemplateProperty); }
128             set { SetValue(SpacerTemplateProperty, value); }
129         }
130
131         [Fx.Tag.KnownXamlExternal]
132         public DataTemplate HeaderTemplate
133         {
134             get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
135             set { SetValue(HeaderTemplateProperty, value); }
136         }
137
138         [Fx.Tag.KnownXamlExternal]
139         public DataTemplate FooterTemplate
140         {
141             get { return (DataTemplate)GetValue(FooterTemplateProperty); }
142             set { SetValue(FooterTemplateProperty, value); }
143         }
144
145         [Fx.Tag.KnownXamlExternal]
146         public ItemsPanelTemplate ItemsPanel
147         {
148             get { return (ItemsPanelTemplate)GetValue(ItemsPanelProperty); }
149             set { SetValue(ItemsPanelProperty, value); }
150         }
151
152         [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.CollectionPropertiesShouldBeReadOnly,
153             Justification = "Setter is provided to enable setting this property in code.")]
154         [Fx.Tag.KnownXamlExternal]
155         public ModelItemCollection Items
156         {
157             get { return (ModelItemCollection)GetValue(ItemsProperty); }
158             set { SetValue(ItemsProperty, value); }
159         }
160
161         EditingContext Context
162         {
163             get
164             {
165                 if (context == null)
166                 {
167                     IModelTreeItem modelTreeItem = this.Items as IModelTreeItem;
168                     if (modelTreeItem != null)
169                     {
170                         this.context = modelTreeItem.ModelTreeManager.Context;
171                     }
172                 }
173                 return context;
174             }
175         }
176
177         public bool IsDefaultContainer
178         {
179             get { return (bool)GetValue(IsDefaultContainerProperty); }
180             set { SetValue(IsDefaultContainerProperty, value); }
181         }
182
183         [Fx.Tag.KnownXamlExternal]
184         public TypeResolvingOptions DroppingTypeResolvingOptions
185         {
186             get { return (TypeResolvingOptions)GetValue(DroppingTypeResolvingOptionsProperty); }
187             set { SetValue(DroppingTypeResolvingOptionsProperty, value); }
188         }
189
190         static void OnHintTextChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
191         {
192             WorkflowItemsPresenter itemsPresenter = (WorkflowItemsPresenter)dependencyObject;
193             itemsPresenter.UpdateHintTextVisibility(e.NewValue as string);
194         }
195
196         static void OnItemsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
197         {
198             WorkflowItemsPresenter itemsPresenter = (WorkflowItemsPresenter)dependencyObject;
199             itemsPresenter.OnItemsChanged((ModelItemCollection)e.OldValue, (ModelItemCollection)e.NewValue);
200         }
201
202         static void OnItemsPanelChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
203         {
204             WorkflowItemsPresenter itemsPresenter = (WorkflowItemsPresenter)dependencyObject;
205             itemsPresenter.panel.ItemsPanel = (ItemsPanelTemplate)e.NewValue;
206         }
207
208         void OnItemsChanged(ModelItemCollection oldItemsCollection, ModelItemCollection newItemsCollection)
209         {
210             if (oldItemsCollection != null)
211             {
212                 oldItemsCollection.CollectionChanged -= this.OnCollectionChanged;
213             }
214
215             if (newItemsCollection != null)
216             {
217                 newItemsCollection.CollectionChanged += this.OnCollectionChanged;
218             }
219
220             if (!isRegisteredWithParent)
221             {
222                 CutCopyPasteHelper.RegisterWithParentViewElement(this);
223                 isRegisteredWithParent = true;
224             }
225             populateOnLoad = false;
226             PopulateContent();
227         }
228
229         void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
230         {
231             // if this.Items is null, and we are getting a collection changed that 
232             // means this event some how happened before this can get the unloaded event
233             // and unsubscribe from this event.
234             if (this.Items == null)
235             {
236                 return;
237             }
238             bool fullRepopulateNeeded = true;
239
240             // when one item is dropped into this items presenter focus on the new view element for it.
241             if (e.Action == NotifyCollectionChangedAction.Add
242                 && e.NewItems != null
243                 && e.NewItems.Count == 1)
244             {
245                 // insert itemview and spacer
246                 fullRepopulateNeeded = false;
247                 int itemViewIndex = GetViewIndexForItem(e.NewStartingIndex);
248                 VirtualizedContainerService containerService = this.Context.Services.GetService<VirtualizedContainerService>();
249                 UIElement itemView = containerService.GetContainer((ModelItem)e.NewItems[0], this);
250                 this.panel.Items.Insert(itemViewIndex, itemView as UIElement);
251                 // index 2 + i*2 + 1 is spacer i+1
252                 FrameworkElement spacer = CreateSpacer();
253                 this.panel.Items.Insert(itemViewIndex + 1, spacer);
254
255
256                 ModelItem insertedItem = (ModelItem)e.NewItems[0];
257                 this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)(() =>
258                 {
259                     UIElement view = (UIElement)insertedItem.View;
260                     if (view != null)
261                     {
262                         Keyboard.Focus(view);
263                     }
264                 }));
265             }
266
267             else if (e.Action == NotifyCollectionChangedAction.Remove)
268             {
269                 if (e.OldItems != null && e.OldItems.Count == 1)
270                 {
271                     fullRepopulateNeeded = false;
272                     int itemViewIndex = GetViewIndexForItem(e.OldStartingIndex);
273                     this.panel.Items.RemoveAt(itemViewIndex);
274                     //remove spacer also
275                     this.panel.Items.RemoveAt(itemViewIndex);
276                 }
277
278                 if (this.Items.Count == 0)
279                 {
280                     fullRepopulateNeeded = true;
281                 }
282
283                 // deselect removed items
284                 if (this.Context != null)
285                 {
286                     IList<ModelItem> selectedItems = this.Context.Items.GetValue<Selection>().SelectedObjects.ToList();
287                     foreach (ModelItem selectedAndRemovedItem in selectedItems.Intersect(e.OldItems.Cast<ModelItem>()))
288                     {
289                         Selection.Toggle(this.Context, selectedAndRemovedItem);
290                     }
291                 }
292             }
293             if (this.Items.Count > 0)
294             {
295                 this.hintTextGrid.Visibility = Visibility.Collapsed;
296             }
297             else
298             {
299                 this.hintTextGrid.Visibility = Visibility.Visible;
300             }
301
302             if (fullRepopulateNeeded)
303             {
304                 PopulateContent();
305             }
306         }
307
308         protected override void OnInitialized(EventArgs e)
309         {
310             base.OnInitialized(e);
311             this.AllowDrop = true;
312             this.Content = outerGrid;
313             if (this.ItemsPanel != null)
314             {
315                 this.panel.ItemsPanel = this.ItemsPanel;
316             }
317
318             ICompositeViewEvents containerEvents = null;
319             bool isDefault = false;
320
321             this.Loaded += (s, eventArgs) =>
322             {
323                 isDefault = this.IsDefaultContainer;
324                 selectedSpacerIndex = addAtEndMarker;
325                 DependencyObject parent = VisualTreeHelper.GetParent(this);
326                 while (null != parent && !typeof(ICompositeViewEvents).IsAssignableFrom(parent.GetType()))
327                 {
328                     parent = VisualTreeHelper.GetParent(parent);
329                 }
330                 containerEvents = parent as ICompositeViewEvents;
331                 if (null != containerEvents)
332                 {
333                     if (isDefault)
334                     {
335                         containerEvents.RegisterDefaultCompositeView(this);
336                     }
337                     else
338                     {
339                         containerEvents.RegisterCompositeView(this);
340                     }
341                 }
342                 if (this.Items != null)
343                 {
344                     //UnRegistering because of 137896: Inside tab control multiple Loaded events happen without an Unloaded event.
345                     this.Items.CollectionChanged -= new NotifyCollectionChangedEventHandler(OnCollectionChanged);
346                     this.Items.CollectionChanged += new NotifyCollectionChangedEventHandler(OnCollectionChanged);
347                 }
348                 if (populateOnLoad)
349                 {
350                     this.PopulateContent();
351                 }
352             };
353
354             this.Unloaded += (s, eventArgs) =>
355             {
356                 if (null != containerEvents)
357                 {
358                     if (isDefault)
359                     {
360                         containerEvents.UnregisterDefaultCompositeView(this);
361                     }
362                     else
363                     {
364                         containerEvents.UnregisterCompositeView(this);
365
366                     }
367                 }
368                 if (this.Items != null)
369                 {
370                     this.Items.CollectionChanged -= new NotifyCollectionChangedEventHandler(OnCollectionChanged);
371                 }
372                 populateOnLoad = true;
373             };
374         }
375
376         void PopulateContent()
377         {
378             this.panel.Items.Clear();
379
380             if (this.Items != null)
381             {
382
383                 // index 0 is header.
384                 ContentControl header = new ContentControl();
385                 header.Focusable = false;
386                 header.ContentTemplate = this.HeaderTemplate;
387                 header.SetValue(IndexProperty, 0);
388                 header.Drop += new DragEventHandler(OnSpacerDrop);
389                 this.panel.Items.Add(header);
390
391                 // index 1 is first spacer
392                 FrameworkElement startSpacer = CreateSpacer();
393                 this.panel.Items.Add(startSpacer);
394
395                 foreach (ModelItem item in this.Items)
396                 {
397                     // index 2 + i*2 is itemView i
398                     VirtualizedContainerService containerService = this.Context.Services.GetService<VirtualizedContainerService>();
399                     UIElement itemView = containerService.GetContainer(item, this);
400                     this.panel.Items.Add(itemView as UIElement);
401                     // index 2 + i*2 + 1 is spacer i+1
402                     FrameworkElement spacer = CreateSpacer();
403                     this.panel.Items.Add(spacer);
404                 }
405                 // index 2 + count*2 is footer 
406                 ContentControl footer = new ContentControl();
407                 footer.ContentTemplate = this.FooterTemplate;
408                 footer.Focusable = true;
409                 footer.IsHitTestVisible = true;
410                 footer.IsTabStop = true;
411                 footer.SetValue(IndexProperty, addAtEndMarker);
412                 footer.Drop += new DragEventHandler(OnSpacerDrop);
413                 footer.LostFocus += new RoutedEventHandler(OnSpacerLostFocus);
414                 footer.GotFocus += new RoutedEventHandler(OnSpacerGotFocus);
415                 this.panel.Items.Add(footer);
416                 footer.Focusable = false;
417             }
418             UpdateHintTextVisibility(HintText);
419         }
420
421         int GetViewIndexForItem(int itemIndex)
422         {
423             return 2 + itemIndex * 2;
424         }
425
426         int GetViewIndexForSpacer(int spacerIndex)
427         {
428             return 2 + spacerIndex * 2 + 1;
429         }
430
431         int GetSpacerIndex(int viewIndex)
432         {
433             if (viewIndex == 1)
434             {
435                 return 0;
436             }
437             else
438             {
439                 return (viewIndex - 3) / 2 + 1;
440             }
441         }
442
443         void UpdateHintTextVisibility(string hintText)
444         {
445             if (this.hintTextGrid != null && this.Items != null)
446             {
447                 this.hintTextGrid.Visibility = (this.Items.Count == 0 && !string.IsNullOrEmpty(hintText)) ? Visibility.Visible : Visibility.Collapsed;
448             }
449         }
450
451         private IList<object> GetOrderMetaData(List<ModelItem> items)
452         {
453             List<ModelItem> sortedList = this.SortSelectedItems(new List<ModelItem>(items));
454             this.CheckListConsistentAndThrow(items, sortedList);
455             return sortedList.Select((m) => m.GetCurrentValue()).ToList();
456         }
457
458         private FrameworkElement CreateSpacer()
459         {
460             FrameworkElement spacer = (this.SpacerTemplate != null) ? (FrameworkElement)this.SpacerTemplate.LoadContent() : new Rectangle();
461             spacer.IsHitTestVisible = true;
462             Control spacerControl = spacer as Control;
463             if (spacerControl != null)
464             {
465                 spacerControl.IsTabStop = true;
466             }
467             spacer.Drop += new DragEventHandler(OnSpacerDrop);
468             spacer.LostFocus += new RoutedEventHandler(OnSpacerLostFocus);
469             spacer.GotFocus += new RoutedEventHandler(OnSpacerGotFocus);
470             spacer.GotKeyboardFocus += new KeyboardFocusChangedEventHandler(OnSpacerGotKeyboardFocus);
471             spacer.MouseDown += new MouseButtonEventHandler(OnSpacerMouseDown);
472             return spacer;
473         }
474
475         void OnSpacerDrop(object sender, DragEventArgs e)
476         {
477             int index = GetSpacerIndexFromView(sender);
478             OnItemsDropped(e, index);
479         }
480
481         private int GetSpacerIndexFromView(object sender)
482         {
483             if (((DependencyObject)sender).ReadLocalValue(IndexProperty) != DependencyProperty.UnsetValue)
484             {
485                 int index = (int)((DependencyObject)sender).GetValue(IndexProperty);
486                 return index;
487             }
488             else
489             {
490                 return GetSpacerIndex(this.panel.Items.IndexOf(sender));
491             }
492         }
493
494         void OnSpacerGotFocus(object sender, RoutedEventArgs e)
495         {
496             int index = GetSpacerIndexFromView(sender);
497             selectedSpacerIndex = index;
498         }
499
500         void OnSpacerGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
501         {
502             // Handle the event so that it won't be routed to the containing designer to affect selection
503             if (handleSpacerGotKeyboardFocus)
504             {
505                 e.Handled = true;
506             }
507         }
508
509         void OnSpacerLostFocus(object sender, RoutedEventArgs e)
510         {
511             int index = GetSpacerIndexFromView(sender);
512             selectedSpacerIndex = addAtEndMarker;
513         }
514
515         void OnSpacerMouseDown(object sender, MouseButtonEventArgs e)
516         {
517             // do not move focus if it's a ctrl right click.
518             if (e.RightButton == MouseButtonState.Pressed)
519             {
520                 if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
521                 {
522                     return;
523                 }
524             }
525
526             // Schedule the Keyboard.Focus command to let it execute later than WorkflowViewElement.OnMouseDown
527             this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
528             {
529                 this.handleSpacerGotKeyboardFocus = true;
530                 Keyboard.Focus((FrameworkElement)sender);
531                 this.handleSpacerGotKeyboardFocus = false;
532             }));
533         }
534
535         private bool ShouldMoveItems(List<ModelItem> sortedModelItems, int index)
536         {
537             if (sortedModelItems.Count == 0)
538             {
539                 return false;
540             }
541
542             // Should move if the items are not next to each other
543             if (!AreItemsConsecutive(sortedModelItems))
544             {
545                 return true;
546             }
547
548             // Should not move if the new position is just before the first item or just after the last item or between them.
549             return index < this.Items.IndexOf(sortedModelItems[0]) || index > this.Items.IndexOf(sortedModelItems.Last()) + 1;
550         }
551
552         private bool AreItemsConsecutive(List<ModelItem> sortedModelItems)
553         {
554             Fx.Assert(sortedModelItems.Count > 0, "Should have at least one item.");
555             int oldIndex = this.Items.IndexOf(sortedModelItems[0]);
556             foreach (ModelItem item in sortedModelItems)
557             {
558                 if (oldIndex != this.Items.IndexOf(item))
559                 {
560                     return false;
561                 }
562                 oldIndex++;
563             }
564             return true;
565         }
566
567         public List<ModelItem> SortSelectedItems(List<ModelItem> selectedItems)
568         {
569             if (selectedItems == null)
570             {
571                 throw FxTrace.Exception.ArgumentNull("selectedItems");
572             }
573             if (selectedItems.Count < 2)
574             {
575                 return selectedItems;
576             }
577
578             List<ModelItem> list = new List<ModelItem>();
579             // If the performance here is bad, we can use HashSet for selectedItems
580             // to improve
581             foreach (ModelItem item in this.Items)
582             {
583                 int index = selectedItems.IndexOf(item);
584                 if (index >= 0)
585                 {
586                     // use the reference in selectedItems.
587                     list.Add(selectedItems[index]);
588                 }
589             }
590
591             // in case passing some items that are not in
592             // my container.
593             if (list.Count != selectedItems.Count)
594             {
595                 // throw FxTrace.Exception.
596                 throw FxTrace.Exception.AsError(new ArgumentException(SR.Error_CantFindItemInWIsP));
597             }
598             return list;
599         }
600
601         public void OnItemsMoved(List<ModelItem> movedItems)
602         {
603             if (movedItems == null)
604             {
605                 throw FxTrace.Exception.ArgumentNull("movedItems");
606             }
607
608             DragDropHelper.ValidateItemsAreOnView(movedItems, this.Items);
609             this.OnItemsDelete(movedItems);
610         }
611
612         void OnItemsDropped(DragEventArgs e, int index)
613         {
614             ModelItemHelper.TryCreateImmediateEditingScopeAndExecute(this.Items.GetEditingContext(), System.Activities.Presentation.SR.CollectionAddEditingScopeDescription, (es) =>
615             {
616                 DragDropHelper.SetDragDropCompletedEffects(e, DragDropEffects.None);
617                 List<object> droppedObjects = new List<object>(DragDropHelper.GetDroppedObjects(this, e, Context));
618                 List<WorkflowViewElement> movedViewElements = new List<WorkflowViewElement>();
619
620                 List<object> externalMoveList = new List<object>();
621                 List<ModelItem> internalMoveList = new List<ModelItem>();
622
623                 // Step 1: Sort the list
624                 List<object> sortedDroppingList = DragDropHelper.SortSelectedObjects(droppedObjects);
625
626
627                 // Step 2: Categorize dropped objects by their source container.
628                 foreach (object droppedObject in sortedDroppingList)
629                 {
630                     ModelItem modelItem = droppedObject as ModelItem;
631                     WorkflowViewElement view = (modelItem == null) ? null : (modelItem.View as WorkflowViewElement);
632                     if (view == null)
633                     {
634                         externalMoveList.Add(droppedObject);
635                         continue;
636                     }
637                     UIElement container = DragDropHelper.GetCompositeView(view);
638                     if (container == this)
639                     {
640                         internalMoveList.Add(modelItem);
641                         continue;
642                     }
643                     movedViewElements.Add(view);
644                     externalMoveList.Add(droppedObject);
645                 }
646
647                 // Step 3: Internal movement
648                 if (this.ShouldMoveItems(internalMoveList, index))
649                 {
650                     foreach (ModelItem modelItem in internalMoveList)
651                     {
652                         int oldIndex = this.Items.IndexOf(modelItem);
653                         this.Items.Remove(modelItem);
654
655                         //if element is placed ahead of old location, decrement the index not to include moved object
656                         if (oldIndex < index)
657                         {
658                             this.InsertItem(index - 1, modelItem);
659                         }
660                         else
661                         {
662                             this.InsertItem(index, modelItem);
663                             index++;
664                         }
665                     }
666                 }
667
668                 // Step 4: External move and drop from toolbox
669                 foreach (object droppedObject in externalMoveList)
670                 {
671                     if (!this.IsDropAllowed(droppedObject))
672                     {
673                         continue;
674                     }
675                     this.InsertItem(index++, droppedObject);
676                     DragDropHelper.SetDragDropCompletedEffects(e, DragDropEffects.Move);
677                 }
678                 DragDropHelper.SetDragDropMovedViewElements(e, movedViewElements);
679                 e.Handled = true;
680                 if (es != null)
681                 {
682                     es.Complete();
683                 }
684             });
685         }
686
687         private void CheckListConsistentAndThrow(List<ModelItem> src, List<ModelItem> copied)
688         {
689             bool valid = DragDropHelper.AreListsIdenticalExceptOrder(src, copied);
690             if (!valid)
691             {
692                 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.Error_BadOutputFromSortSelectedItems));
693             }
694         }
695
696         private bool IsDropAllowed(object droppedObject)
697         {
698             bool isDropAllowed = false;
699             ModelItem modelItem = droppedObject as ModelItem;
700             if (modelItem != null && !IsInParentChain(modelItem))
701             {
702                 if (this.AllowedItemType.IsAssignableFrom(modelItem.ItemType))
703                 {
704                     isDropAllowed = true;
705                 }
706             }
707             else if (droppedObject is Type && this.AllowedItemType.IsAssignableFrom((Type)droppedObject))
708             {
709                 isDropAllowed = true;
710             }
711             else
712             {
713                 if (this.AllowedItemType.IsAssignableFrom(droppedObject.GetType()))
714                 {
715                     isDropAllowed = true;
716                 }
717             }
718             return isDropAllowed;
719         }
720
721         private bool IsInParentChain(ModelItem droppedModelItem)
722         {
723             bool isInParentChain = false;
724             ModelItem parentModelItem = this.Items;
725             while (parentModelItem != null)
726             {
727                 if (parentModelItem == droppedModelItem)
728                 {
729                     isInParentChain = true;
730                     break;
731                 }
732                 parentModelItem = parentModelItem.Parent;
733             }
734             return isInParentChain;
735         }
736
737         void InsertItem(int index, object droppedObject)
738         {
739             ModelItem insertedItem = null;
740             if (index == addAtEndMarker)
741             {
742                 insertedItem = this.Items.Add(droppedObject);
743             }
744             else
745             {
746                 insertedItem = this.Items.Insert(index, droppedObject);
747             }
748             if (insertedItem != null)
749             {
750                 Selection.SelectOnly(this.Context, insertedItem);
751             }
752         }
753
754         protected override void OnDrop(DragEventArgs e)
755         {
756             int index = addAtEndMarker;
757             WorkflowViewElement dropTarget = null;
758             if (e.OriginalSource is WorkflowViewElement)
759             {
760                 dropTarget = (WorkflowViewElement)e.OriginalSource;
761             }
762             else
763             {
764                 dropTarget = VisualTreeUtils.FindFocusableParent<WorkflowViewElement>((UIElement)e.OriginalSource);
765             }
766
767             if (null != dropTarget && null != dropTarget.ModelItem)
768             {
769                 int targetIndex = this.Items.IndexOf(dropTarget.ModelItem);
770                 if (-1 != targetIndex)
771                 {
772                     index = targetIndex + 1;
773                 }
774             }
775             OnItemsDropped(e, index);
776             base.OnDrop(e);
777         }
778
779         void OnDrag(DragEventArgs e)
780         {
781             if (!e.Handled)
782             {
783                 if (!DragDropHelper.AllowDrop(e.Data, this.Context, this.AllowedItemType))
784                 {
785                     e.Effects = DragDropEffects.None;
786                 }
787                 e.Handled = true;
788             }
789         }
790
791         protected override void OnDragEnter(DragEventArgs e)
792         {
793             this.OnDrag(e);
794             base.OnDragEnter(e);
795         }
796
797         protected override void OnDragOver(DragEventArgs e)
798         {
799             this.OnDrag(e);
800             base.OnDragOver(e);
801         }
802
803
804         public void OnItemMoved(ModelItem modelItem)
805         {
806             if (this.Items.Contains(modelItem))
807             {
808                 this.Items.Remove(modelItem);
809             }
810         }
811
812         protected override AutomationPeer OnCreateAutomationPeer()
813         {
814             return new WorkflowItemsPresenterAutomationPeer(this);
815         }
816
817         public object OnItemsCut(List<ModelItem> itemsToCut)
818         {
819             List<object> orderMetaData = GetOrderMetaData(itemsToCut).ToList();
820             foreach (ModelItem item in itemsToCut)
821             {
822                 this.Items.Remove(item);
823                 this.Context.Items.SetValue(new Selection(new ArrayList()));
824             }
825             return orderMetaData;
826         }
827        
828         public object OnItemsCopied(List<ModelItem> itemsToCopy)
829         {
830             return this.GetOrderMetaData(itemsToCopy);
831         }
832
833         public void OnItemsPasted(List<object> itemsToPaste, List<object> metaData, Point pastePoint, WorkflowViewElement pastePointReference)
834         {
835             // first see if a spacer is selected.
836             int index = this.selectedSpacerIndex;
837             // else see if we can paste after a selected child
838             if (index < 0)
839             {
840                 Selection currentSelection = this.Context.Items.GetValue<Selection>();
841                 index = this.Items.IndexOf(currentSelection.PrimarySelection);
842                 //paste after the selected child
843                 if (index >= 0)
844                 {
845                     index++;
846                 }
847             }
848             if (index < 0)
849             {
850                 index = addAtEndMarker;
851             }
852
853             IList<object> mergedItemsToPaste = CutCopyPasteHelper.SortFromMetaData(itemsToPaste, metaData);
854
855             List<ModelItem> modelItemsToSelect = new List<ModelItem>();
856
857             foreach (object itemToPaste in mergedItemsToPaste)
858             {
859                 if (IsDropAllowed(itemToPaste))
860                 {
861                     if (index == addAtEndMarker)
862                     {
863                         modelItemsToSelect.Add(this.Items.Add(itemToPaste));
864                     }
865                     else
866                     {
867                         modelItemsToSelect.Add(this.Items.Insert(index, itemToPaste));
868                     }
869                     if (index >= 0)
870                     {
871                         index++;
872                     }
873                 }
874             }
875
876             this.Dispatcher.BeginInvoke(
877                 new Action(() =>
878                 {
879                     this.Context.Items.SetValue(new Selection(modelItemsToSelect));
880                 }),
881                 Windows.Threading.DispatcherPriority.ApplicationIdle,
882                 null);
883         }
884
885         public void OnItemsDelete(List<ModelItem> itemsToDelete)
886         {
887             if (null != itemsToDelete)
888             {
889                 itemsToDelete.ForEach(p =>
890                 {
891                     if (null != this.Items && this.Items.Contains(p))
892                     {
893                         this.Items.Remove(p);
894                     }
895                 }
896                     );
897             }
898         }
899
900         public bool CanPasteItems(List<object> itemsToPaste)
901         {
902             bool result = false;
903             if (null != itemsToPaste && itemsToPaste.Count > 0)
904             {
905                 result = itemsToPaste.All(p => this.IsDropAllowed(p));
906             }
907             return result;
908         }
909
910         class WorkflowItemsPresenterAutomationPeer : UIElementAutomationPeer
911         {
912             WorkflowItemsPresenter owner;
913
914             public WorkflowItemsPresenterAutomationPeer(WorkflowItemsPresenter owner)
915                 : base(owner)
916             {
917                 this.owner = owner;
918             }
919
920             protected override AutomationControlType GetAutomationControlTypeCore()
921             {
922                 return AutomationControlType.Custom;
923             }
924
925             protected override string GetAutomationIdCore()
926             {
927                 string automationId = base.GetAutomationIdCore();
928                 if (string.IsNullOrEmpty(automationId))
929                 {
930                     automationId = base.GetNameCore();
931                     if (string.IsNullOrEmpty(automationId))
932                     {
933                         automationId = this.owner.GetType().Name;
934                     }
935                 }
936                 return automationId;
937             }
938
939             protected override string GetNameCore()
940             {
941                 // Return an empty string if some activites are dropped on the presenter
942                 if (owner.Items.Count > 0)
943                 {
944                     return string.Empty;
945                 }
946                 string name = base.GetNameCore();
947                 if (string.IsNullOrEmpty(name))
948                 {
949                     name = this.owner.HintText;
950                 }
951                 return name;
952             }
953
954             protected override string GetClassNameCore()
955             {
956                 return this.owner.GetType().Name;
957             }
958         }
959
960     }
961 }