[reflection] Coop handles icalls in System.Reflection and System.RuntimeTypeHandle...
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Model / ModelItemExtensions.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Model
5 {
6     using System;
7     using System.Activities.Debugger;
8     using System.Activities.Presentation.Annotations;
9     using System.Activities.Presentation.Services;
10     using System.Activities.Presentation.View;
11     using System.Collections.Generic;
12     using System.Diagnostics.CodeAnalysis;
13     using System.Globalization;
14     using System.Linq;
15     using System.Runtime;
16     using System.Text;
17     using System.Windows;
18     using System.Windows.Input;
19     using System.Windows.Threading;
20
21     public static class ModelItemExtensions
22     {
23         const int MaxExpandLevel = 50;
24         const string rootPath = "Root";
25
26         public static EditingContext GetEditingContext(this ModelItem modelItem)
27         {
28             EditingContext result = null;
29             IModelTreeItem modelTreeItem = modelItem as IModelTreeItem;
30             if (null != modelTreeItem && null != modelTreeItem.ModelTreeManager)
31             {
32                 result = modelTreeItem.ModelTreeManager.Context;
33             }
34             return result;
35         }
36
37         internal static ModelItem FindParentModelItem(this ModelItem item, Type parentType)
38         {
39             if (null == item)
40             {
41                 throw FxTrace.Exception.ArgumentNull("item");
42             }
43             if (null == parentType)
44             {
45                 throw FxTrace.Exception.ArgumentNull("parentType");
46             }
47
48             ModelItem result = null;
49             item = item.Parent;
50             while (item != null && !parentType.IsAssignableFrom(item.ItemType))
51             {
52                 item = item.Parent;
53             }
54             if (null != item && parentType.IsAssignableFrom(item.ItemType))
55             {
56                 result = item;
57             }
58             return result;
59         }
60
61         internal static bool SwitchKeys(this ModelItemDictionary dictionary, ModelItem oldKey, ModelItem newKey)
62         {
63             if (null == dictionary)
64             {
65                 throw FxTrace.Exception.AsError(new ArgumentNullException("dictionary"));
66             }
67             if (null == oldKey)
68             {
69                 throw FxTrace.Exception.AsError(new ArgumentNullException("oldKey"));
70             }
71             if (null == newKey)
72             {
73                 throw FxTrace.Exception.AsError(new ArgumentNullException("newKey"));
74             }
75             if (!dictionary.ContainsKey(oldKey))
76             {
77                 throw FxTrace.Exception.AsError(new KeyNotFoundException(null == oldKey.GetCurrentValue() ? "oldKey" : oldKey.GetCurrentValue().ToString()));
78             }
79             bool result = false;
80             if (!dictionary.ContainsKey(newKey))
81             {
82                 ModelItem value = dictionary[oldKey];
83                 dictionary.Remove(oldKey);
84                 dictionary[newKey] = value;
85                 result = true;
86             }
87             return result;
88         }
89
90         [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters",
91            Justification = "This is a TryGet pattern that requires out parameters")]
92         internal static bool SwitchKeys(this ModelItemDictionary dictionary, object oldKey, object newKey, out ModelItem newKeyItem)
93         {
94             if (null == dictionary)
95             {
96                 throw FxTrace.Exception.AsError(new ArgumentNullException("dictionary"));
97             }
98             if (null == oldKey)
99             {
100                 throw FxTrace.Exception.AsError(new ArgumentNullException("oldKey"));
101             }
102             if (null == newKey)
103             {
104                 throw FxTrace.Exception.AsError(new ArgumentNullException("newKey"));
105             }
106             if (!dictionary.ContainsKey(oldKey))
107             {
108                 throw FxTrace.Exception.AsError(new KeyNotFoundException(oldKey.ToString()));
109             }
110             bool result = false;
111             newKeyItem = null;
112             if (typeof(ModelItem).IsAssignableFrom(oldKey.GetType()) && typeof(ModelItem).IsAssignableFrom(newKey.GetType()))
113             {
114                 result = SwitchKeys(dictionary, (ModelItem)oldKey, (ModelItem)newKey);
115                 newKeyItem = (ModelItem)newKey;
116             }
117             else
118             {
119                 if (typeof(ModelItem).IsAssignableFrom(oldKey.GetType()))
120                 {
121                     oldKey = ((ModelItem)oldKey).GetCurrentValue();
122                     if (null == oldKey)
123                     {
124                         throw FxTrace.Exception.AsError(new InvalidOperationException("((ModelItem)oldKey).GetCurrentValue()"));
125                     }
126                 }
127                 if (typeof(ModelItem).IsAssignableFrom(newKey.GetType()))
128                 {
129                     newKey = ((ModelItem)newKey).GetCurrentValue();
130                     if (null == newKey)
131                     {
132                         throw FxTrace.Exception.AsError(new InvalidOperationException("((ModelItem)newKey).GetCurrentValue()"));
133                     }
134                 }
135             }
136             if (!dictionary.ContainsKey(newKey))
137             {
138                 ModelItem value = dictionary[oldKey];
139                 dictionary.Remove(oldKey);
140                 dictionary[newKey] = value;
141                 newKeyItem = dictionary.Keys.First<ModelItem>(p => object.Equals(p.GetCurrentValue(), newKey));
142                 result = true;
143             }
144             return result;
145         }
146
147         [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters",
148            Justification = "This is a TryGet pattern that requires out parameters")]
149         internal static bool TryGetPropertyValue(this ModelItem item, out ModelItemCollection value, params string[] path)
150         {
151             ModelItem temp;
152             value = null;
153             bool result = TryGetPropertyValue(item, out temp, path);
154             if (null != item)
155             {
156                 value = (ModelItemCollection)temp;
157             }
158             return result;
159         }
160
161         [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters",
162            Justification = "This is a TryGet pattern that requires out parameters")]
163         internal static bool TryGetPropertyValue(this ModelItem item, out ModelItemDictionary value, params string[] path)
164         {
165             ModelItem temp;
166             value = null;
167             bool result = TryGetPropertyValue(item, out temp, path);
168             if (null != item)
169             {
170                 value = (ModelItemDictionary)temp;
171             }
172             return result;
173         }
174
175         [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters",
176            Justification = "This is a TryGet pattern that requires out parameters")]
177         internal static bool TryGetPropertyValue(this ModelItem item, out ModelItem value, params string[] path)
178         {
179             if (null == item)
180             {
181                 throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
182             }
183             if (null == path)
184             {
185                 throw FxTrace.Exception.AsError(new ArgumentNullException("path"));
186             }
187             if (path.Length < 1)
188             {
189                 throw FxTrace.Exception.AsError(new ArgumentException(SR.ModelItemPathArrayShouldNotBeEmpty));
190             }
191             value = item;
192             bool result = true;
193             for (int i = 0; i < path.Length && true == result && null != value; ++i)
194             {
195                 ModelProperty property = value.Properties[path[i]];
196                 if (null != property)
197                 {
198                     value = property.Value;
199                     if (null == value)
200                     {
201                         result = false;
202                     }
203                 }
204                 else
205                 {
206                     throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.PropertyDoesntExistFormatString, path[i])));
207                 }
208             }
209             return result;
210         }
211
212         [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters",
213            Justification = "This is a TryGet pattern that requires out parameters")]
214         internal static bool TrySetPropertyValue(this ModelItem item, object value, out ModelItem wrappedValue, params string[] path)
215         {
216             if (null == item)
217             {
218                 throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
219             }
220             if (null == path)
221             {
222                 throw FxTrace.Exception.AsError(new ArgumentNullException("path"));
223             }
224             if (path.Length < 1)
225             {
226                 throw FxTrace.Exception.AsError(new ArgumentException(SR.ModelItemPathArrayShouldNotBeEmpty));
227             }
228             wrappedValue = null;
229             bool result = true;
230             for (int i = 0; i < path.Length && true == result; ++i)
231             {
232                 ModelProperty property = item.Properties[path[i]];
233                 if (null != property)
234                 {
235                     if (i == path.Length - 1)
236                     {
237                         if (null != value)
238                         {
239                             wrappedValue = property.SetValue(value);
240                         }
241                         else
242                         {
243                             property.ClearValue();
244                         }
245                     }
246                     else
247                     {
248                         item = property.Value;
249                         if (null == item)
250                         {
251                             result = false;
252                         }
253                     }
254                 }
255                 else
256                 {
257                     throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.PropertyDoesntExistFormatString, path[i])));
258                 }
259             }
260             return result;
261         }
262
263         internal static bool HasAnnotation(this ModelItem modelItem)
264         {
265             Fx.Assert(modelItem != null, "modelItem should not be null.");
266
267             ModelProperty property = modelItem.Properties.Find(Annotation.AnnotationTextPropertyName);
268
269             Fx.Assert(property != null, "Annotation property should not be null");
270
271             if (property.ComputedValue == null)
272             {
273                 return false;
274             }
275
276             return true;
277         }
278
279         public static string GetModelPath(this ModelItem modelItem)
280         {
281             if (modelItem == null)
282             {
283                 return null;
284             }
285             StringBuilder sb = new StringBuilder();
286             bool isValid = true;
287             HashSet<ModelItem> visited = new HashSet<ModelItem>();
288             // walk up the parent chain and create a modelpath from reverse
289             // eg. Root.Foo.Bar.Collectionproperty[3].----
290
291             // if modelItem doesn't have parent and it's not root, return string.Empty;
292             if (modelItem.Parent == null && modelItem.Root != modelItem)
293             {
294                 return null;
295             }
296
297             while (modelItem != null)
298             {
299                 // paths causing us to get into loops are invalid.
300                 if (visited.Contains(modelItem))
301                 {
302                     isValid = false;
303                     break;
304                 }
305                 // remember the visited.
306                 visited.Add(modelItem);
307                 // if parent is collection store just the index
308                 if (modelItem.Parent is ModelItemCollection)
309                 {
310                     sb.Insert(0, "[" + ((ModelItemCollection)modelItem.Parent).IndexOf(modelItem).ToString(CultureInfo.InvariantCulture) + "]");
311                 }
312                 // if parent is a modelproperty store the property name
313                 else if (modelItem.Source != null)
314                 {
315                     sb.Insert(0, "." + modelItem.Source.Name);
316                 }
317                 //Our model path doesnt work with dictionaries, so in dictionary case follow the mutablekeyvaluepair 
318                 if (modelItem.Parent is ModelItemDictionary)
319                 {
320                     if (modelItem.Source != null)
321                     {
322                         modelItem = modelItem.Source.Parent;
323                     }
324                     else
325                     {
326                         isValid = false;
327                         break;
328                     }
329                 } // when parent is not a dictionary follow the parent up towards the root.
330                 else
331                 {
332                     modelItem = modelItem.Parent;
333                 }
334             }
335             string s = null;
336             if (isValid)
337             {
338                 sb.Insert(0, rootPath);
339                 s = sb.ToString();
340             }
341             return s;
342         }
343
344         public static ModelItem GetModelItemFromPath(string path, ModelItem root)
345         {
346             if (null == root)
347             {
348                 throw FxTrace.Exception.AsError(new ArgumentNullException("root"));
349             }
350             if (null == path)
351             {
352                 throw FxTrace.Exception.AsError(new ArgumentNullException("path"));
353             }
354             ModelItem foundModelItem = null;
355             path = path.Trim();
356             string[] segments = path.Split('.');
357             // process each path. path should atleast be 'Root' and should always  begin with 'Root'
358             if (segments.Length > 0 && segments[0] == rootPath)
359             {
360                 foundModelItem = root;
361                 for (int segmentIndex = 1; segmentIndex < segments.Length; segmentIndex++)
362                 {
363                     string segment = segments[segmentIndex];
364                     if (!string.IsNullOrEmpty(segment))
365                     {
366                         ModelItem next = GetModelItemFromSegment(foundModelItem, segment);
367                         if (next != null)
368                         {
369                             foundModelItem = next;
370                         }
371                         else
372                         {
373                             foundModelItem = null;
374                             break;
375                         }
376                     }
377                     else
378                     {
379                         foundModelItem = null;
380                         break;
381                     }
382                 }
383             }
384             return foundModelItem;
385         }
386
387         private static ModelItem GetModelItemFromSegment(ModelItem currentModelItem, string segment)
388         {
389             ModelItem modelItemFromSegment = null;
390             int indexOfSquareBrackets = segment.IndexOf('[');
391             // e.g Sequence.Activities[0] segment = "Activities[0]"
392             if (indexOfSquareBrackets > 0)
393             {
394                 string collectionProperty = segment.Substring(0, indexOfSquareBrackets);
395                 // find the value of the collection property
396                 ModelItemCollection segmentCollection = GetModelItemFromSegment(currentModelItem, collectionProperty) as ModelItemCollection;
397                 if (segmentCollection != null)
398                 {
399                     try
400                     {
401                         // parse the [index] to find the index
402                         string indexString = segment.Substring(indexOfSquareBrackets + 1);
403                         indexString = indexString.Substring(0, indexString.Length - 1);
404                         int index = Int32.Parse(indexString, CultureInfo.InvariantCulture);
405                         if (index >= 0 && index < segmentCollection.Count)
406                         {
407                             // now index into the collection
408                             modelItemFromSegment = segmentCollection[index];
409                         }
410                     }
411                     // dont crash ever.
412                     catch (FormatException)
413                     {
414                     }
415                     catch (OverflowException)
416                     {
417                     }
418                 }
419             }
420             // e.g SomeFoo.Then segment = "Then"
421             else
422             {
423                 ModelProperty property = currentModelItem.Properties[segment];
424                 if (property != null)
425                 {
426                     modelItemFromSegment = property.Value;
427                 }
428             }
429             return modelItemFromSegment;
430         }
431
432         internal static IEnumerable<ModelItem> GetParentEnumerator(this ModelItem item)
433         {
434             return ModelItemExtensions.GetParentEnumerator(item, null);
435         }
436
437         internal static IEnumerable<ModelItem> GetParentEnumerator(this ModelItem item, Func<ModelItem, bool> continueEnumerationPredicate)
438         {
439             if (null == item)
440             {
441                 throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
442             }
443             while (null != item.Parent)
444             {
445                 if (null != continueEnumerationPredicate && !continueEnumerationPredicate(item.Parent))
446                 {
447                     break;
448                 }
449                 yield return item.Parent;
450                 item = item.Parent;
451             }
452             yield break;
453         }
454
455         internal static string GetUniqueName(this ModelItemCollection collection, string nameDefaultPrefix, Func<ModelItem, string> nameGetter)
456         {
457             return collection.GetUniqueName<ModelItem>(nameDefaultPrefix, nameGetter);
458         }
459
460         internal static string GetUniqueName(this ModelItemDictionary dictionary, string nameDefaultPrefix, Func<ModelItem, string> nameGetter)
461         {
462             if (dictionary != null)
463             {
464                 return dictionary.Keys.GetUniqueName(nameDefaultPrefix, nameGetter);
465             }
466             else
467             {
468                 throw FxTrace.Exception.ArgumentNull("dictionary");
469             }
470         }
471
472         internal static string GetUniqueName<T>(this IEnumerable<T> collection, string nameDefaultPrefix, Func<T, string> nameGetter)
473         {
474             if (null == collection)
475             {
476                 throw FxTrace.Exception.ArgumentNull("collection");
477             }
478             if (null == nameDefaultPrefix)
479             {
480                 throw FxTrace.Exception.ArgumentNull("nameDefaultPrefix");
481             }
482             if (nameDefaultPrefix.Length == 0)
483             {
484                 throw FxTrace.Exception.Argument("nameDefaultPrefix", "length == 0");
485             }
486             if (null == nameGetter)
487             {
488                 throw FxTrace.Exception.ArgumentNull("nameGetter");
489             }
490
491             var maxId = (int?)collection
492                 .Where(p =>
493                 {
494                     var value = nameGetter(p);
495                     if (null != value)
496                     {
497                         return (0 == string.Compare(value, 0, nameDefaultPrefix, 0, nameDefaultPrefix.Length, CultureInfo.CurrentCulture, CompareOptions.None));
498                     }
499                     return false;
500                 })
501                 .Select(p =>
502                 {
503                     int result = 0;
504                     return (int.TryParse(nameGetter(p).Substring(nameDefaultPrefix.Length), out result))
505                         ? result : 0;
506                 })
507                 .OrderByDescending(p => p)
508                 .FirstOrDefault();
509
510             int id = maxId.HasValue ? maxId.Value + 1 : 1;
511
512             return string.Format(CultureInfo.CurrentCulture, "{0}{1}", nameDefaultPrefix, id);
513         }
514
515         internal static bool IsAssignableFrom<T>(this ModelItem item) where T : class
516         {
517             if (null == item)
518             {
519                 throw FxTrace.Exception.ArgumentNull("item");
520             }
521
522             return typeof(T).IsAssignableFrom(item.ItemType);
523         }
524
525         internal static Activity GetRootActivity(this ModelItem item)
526         {
527             Object root = item.GetCurrentValue();
528             if (root is IDebuggableWorkflowTree)
529             {
530                 return ((IDebuggableWorkflowTree)root).GetWorkflowRoot();
531             }
532             else
533             {
534                 return root as Activity;
535             }
536         }
537
538         public static bool IsParentOf(this ModelItem item, ModelItem child)
539         {
540             if (null == item)
541             {
542                 throw FxTrace.Exception.ArgumentNull("item");
543             }
544             if (null == child)
545             {
546                 throw FxTrace.Exception.ArgumentNull("child");
547             }
548
549             bool isParent = false;
550             child.GetParentEnumerator(p => { isParent = ModelItem.Equals(p, item); return !isParent; }).LastOrDefault();
551             return isParent;
552         }
553
554         public static void Focus(this ModelItem item)
555         {
556             Focus(item, MaxExpandLevel);
557         }
558
559         internal static void Highlight(this ModelItem item)
560         {
561             ModelItemFocusHelper.Focus(item, MaxExpandLevel, false, Rect.Empty);
562         }
563
564         internal static void Highlight(this ModelItem item, Rect rectToBringIntoView)
565         {
566             ModelItemFocusHelper.Focus(item, MaxExpandLevel, false, rectToBringIntoView);
567         }
568
569         public static void Focus(this ModelItem item, int level)
570         {
571             if (null == item)
572             {
573                 throw FxTrace.Exception.ArgumentNull("item");
574             }
575             if (level < 1)
576             {
577                 throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("level"));
578             }
579             ModelItemFocusHelper.Focus(item, level);
580         }
581
582         internal static ModelItem FindParent(this ModelItem item, Predicate<ModelItem> predicate)
583         {
584             ModelItem parent = item.Parent;
585
586             while (parent != null && !predicate(parent))
587             {
588                 parent = parent.Parent;
589             }
590
591             return parent;
592         }
593
594         private sealed class ModelItemFocusHelper
595         {
596             static ModelItemFocusHelper focusTicket = null;
597
598             ModelItem itemToFocus;
599             int currentLevel;
600             bool shouldAbort = false;
601
602             EditingContext context;
603             VirtualizedContainerService containerService;
604             WorkflowViewService viewService;
605             DesignerView designerView;
606             ModelItem[] itemsToExpand;
607             bool shouldGetKeyboardFocus;
608             Rect rectToBringIntoView;
609
610             EditingContext Context
611             {
612                 get
613                 {
614                     if (null == this.context)
615                     {
616                         this.context = this.itemToFocus.GetEditingContext();
617                     }
618                     return this.context;
619                 }
620             }
621             VirtualizedContainerService ContainerService
622             {
623                 get
624                 {
625                     if (null == this.containerService)
626                     {
627                         this.containerService = this.Context.Services.GetService<VirtualizedContainerService>();
628                     }
629                     return this.containerService;
630                 }
631             }
632             WorkflowViewService ViewService
633             {
634                 get
635                 {
636                     if (null == this.viewService)
637                     {
638                         this.viewService = (WorkflowViewService)this.Context.Services.GetService<ViewService>();
639                     }
640                     return this.viewService;
641                 }
642             }
643             DesignerView DesignerView
644             {
645                 get
646                 {
647                     if (null == this.designerView)
648                     {
649                         this.designerView = this.Context.Services.GetService<DesignerView>();
650                     }
651                     return this.designerView;
652                 }
653             }
654
655             Action<VirtualizedContainerService.VirtualizingContainer> onContainerPopulatingDelegate;
656             Action<ModelItem> onElementFocusingDelegate;
657             Action<Visibility> onSetDesignerContentVisibilityDelegate;
658             Action onForceElementFocusDelegate;
659
660
661             private ModelItemFocusHelper(ModelItem itemToFocus, int maxExpandLevel, bool shouldGetKeyboardFocus, Rect rectToBringIntoView)
662             {
663                 this.itemToFocus = itemToFocus;
664                 this.currentLevel = maxExpandLevel;
665                 this.onContainerPopulatingDelegate = this.OnPopulateContainer;
666                 this.onElementFocusingDelegate = this.OnFocusElement;
667                 this.onSetDesignerContentVisibilityDelegate = this.ChangeDesignerViewVisibility;
668                 this.onForceElementFocusDelegate = this.OnForceFocusElement;
669                 this.shouldGetKeyboardFocus = shouldGetKeyboardFocus;
670                 this.rectToBringIntoView = rectToBringIntoView;
671             }
672
673             // Checks if a model item is rooted at a specific model item
674             static bool IsRootedAt(ModelItem item, ModelItem root)
675             {
676                 Fx.Assert(item != null, "item must not be null");
677                 Fx.Assert(root != null, "root must not be null");
678                 ModelItem currentItem = item;
679                 while (currentItem.Parent != null)
680                 {
681                     currentItem = currentItem.Parent;
682                 }
683                 return currentItem == root;
684             }
685
686             public static void Focus(ModelItem itemToFocus, int maxExpandLevel)
687             {
688                 Focus(itemToFocus, maxExpandLevel, true);
689             }
690
691             internal static void Focus(ModelItem itemToFocus, int maxExpandLevel, bool shouldGetKeyboardFocus)
692             {
693                 Focus(itemToFocus, maxExpandLevel, shouldGetKeyboardFocus, Rect.Empty);
694             }
695
696             internal static void Focus(ModelItem itemToFocus, int maxExpandLevel, bool shouldGetKeyboardFocus, Rect rectToBringIntoView)
697             {
698                 // Check if this model item exist in the model tree
699                 IModelTreeItem modelTreeItem = itemToFocus as IModelTreeItem;
700                 if (modelTreeItem != null)
701                 {
702                     // If this model item doesn't exist in the tree, don't do anything,
703                     //  chances are it's an activity that has been deleted.
704                     if (!IsRootedAt(itemToFocus, modelTreeItem.ModelTreeManager.Root) && !(itemToFocus is FakeModelItemImpl))
705                     {
706                         return;
707                     }
708                 }
709
710                 //if there is another focus operation in progress, mark it so it would abort itself on next OnContextIdle processing - 
711                 //we don't want to multiple elements racing for keyboard focus
712                 if (null != focusTicket)
713                 {
714                     focusTicket.shouldAbort = true;
715                 }
716                 //create new focus ticket
717                 focusTicket = new ModelItemFocusHelper(itemToFocus, maxExpandLevel, shouldGetKeyboardFocus, rectToBringIntoView);
718                 //and start its processing as soon as application gets idle
719                 Dispatcher.CurrentDispatcher.BeginInvoke(new Action<ModelItemFocusHelper>((p) => { p.Focus(); }), DispatcherPriority.ContextIdle, focusTicket);
720             }
721
722             // Entry point method for setting focus.
723             // it is executed exactly once, on application idle event
724             // there are 3 basic paths:
725             // a) optimistic - element we are looking for, is visible - i bring it into view and set keyboard focus to it
726             // b) unlikely - element doesn't have any visual parents - i make it a root designer, wait for it to load and set keyboard focus to it
727             // c) pesimistic/complex - element isn't in the view, moreover, it is located in a tree branch which is not (or is partialy) visible
728             void Focus()
729             {
730                 //can i continue?
731                 if (shouldAbort)
732                 {
733                     return;
734                 }
735
736                 //hide the designer view until focus is set
737                 this.onSetDesignerContentVisibilityDelegate(Visibility.Hidden);
738                 //delegate visibility restore for designer view after focus update is complete
739                 Dispatcher.CurrentDispatcher.BeginInvoke(this.onSetDesignerContentVisibilityDelegate, DispatcherPriority.ApplicationIdle, Visibility.Visible);
740
741                 //set selection to the item to focus, so all apropriate designers get a chance to update themselfs before we start expanding - this may 
742                 //result in visual tree change
743                 Selection.SelectOnly(this.Context, this.itemToFocus);
744
745                 //easy path - if the current designer is available and visible - bring it to view and focus
746                 if (null != this.itemToFocus.View && ((UIElement)this.itemToFocus.View).IsVisible)
747                 {
748                     this.onElementFocusingDelegate(this.itemToFocus);
749                     return;
750                 }
751
752                 //get items up to the tree root, which can be visualized (have associated designer)
753                 //include only up to "level" items (avoid expanding whole tree)                
754                 bool shouldContinue = true;
755                 int visualItemsCount = 0;
756                 var visualItems = this.itemToFocus
757                     .GetParentEnumerator(p => shouldContinue)
758                     .Where(p =>
759                     {
760                         //filter only items with designer attribute 
761                         bool result = false;
762                         var designerType = this.ViewService.GetDesignerType(p.ItemType);
763                         if (null != designerType)
764                         {
765                             result = true;
766                             visualItemsCount++;
767                             //if designer has Options attribute, check if it always collapsed children - if so, this will be the topmost parent
768                             //(displaying anything above, will never display its children)
769                             var options = WorkflowViewService.GetAttribute<ActivityDesignerOptionsAttribute>(designerType);
770                             if (null != options && options.AlwaysCollapseChildren && visualItemsCount > 2)
771                             {
772                                 shouldContinue = false;
773                             }
774                         }
775                         return result;
776                     })
777                     .Take(this.currentLevel)
778                     .ToArray();
779
780
781
782                 //nothing to expand, rather unlikely, but handle it anyway
783                 if (visualItems.Length == 0)
784                 {
785                     //reset ticket, to prevent any further calls from executing
786                     ModelItemFocusHelper.focusTicket = null;
787                     //force item to be root designer (this is last resort, it is executed only if focusTicket is null)
788                     this.onForceElementFocusDelegate();
789                     return;
790                 }
791
792                 //get the first parent of an item, which is visible 
793                 var firstVisibleItem = visualItems.FirstOrDefault(p => null != p.View && ((UIElement)p.View).IsVisible);
794
795                 bool enqueueFirstExpand = false;
796
797                 //is there anything visible in the path between item and its parents?
798                 if (null != firstVisibleItem)
799                 {
800                     //yes - limit the amount of items to expand to only designers which are not visible yet 
801                     //(include the first visible designer, so algorithm can have a start point with something visible)
802                     this.itemsToExpand = visualItems.TakeWhile(p => firstVisibleItem != p).Concat(new ModelItem[] { firstVisibleItem }).ToArray();
803                 }
804                 else
805                 {
806                     //no, nothing is visible yet
807                     this.itemsToExpand = visualItems;
808                     enqueueFirstExpand = true;
809                     //make the top most parent as root designer
810                     this.DesignerView.MakeRootDesigner(this.itemsToExpand[this.itemsToExpand.Length - 1], false);
811                 }
812                 //delegate Expand call - if nothing is visible yet - onIdle - give new designer time to fully render, if someting is visible - execute immediatelly
813                 Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => { this.Expand(null); }), enqueueFirstExpand ? DispatcherPriority.ContextIdle : DispatcherPriority.Send);
814             }
815
816             //Expand method is executed repeatadly until maximum expand level is reached. it iterates through the model item tree 
817             //(from child up to MaximumExpandLevel parents) and tries to find first visible designer and populate it with content
818             //If one elemnt is visited twice (denoted by currentItem argument) it means that expansion failed - (i.e. element is collapsed),
819             //so i try to set that element as root designer and restart algoritm with that designer beeing new root
820             void Expand(ModelItem currentItem)
821             {
822                 //can i continue?
823                 if (this.shouldAbort)
824                 {
825                     return;
826                 }
827
828                 //stop condition - prevents infinite loop (the method is delegated into dispatcher, so it would never cause stack overflow
829                 if (0 > this.currentLevel)
830                 {
831                     ModelItemFocusHelper.focusTicket = null;
832                     return;
833                 }
834
835                 //browse direct parents, and Populate the fist one which is visible
836                 for (int index = 0; null != this.itemsToExpand && index < this.itemsToExpand.Length; ++index)
837                 {
838                     //is given parent visible? (it would return container for given model item)
839                     var container = this.ContainerService.QueryContainerForItem(this.itemsToExpand[index]);
840
841                     if (null != container)
842                     {
843                         //check if container we are trying to expand is not the same as the one in previous iteration 
844                         //if it isn't --> populate its content
845                         if (!ModelItem.Equals(currentItem, this.itemsToExpand[index]))
846                         {
847                             this.Populate(container);
848                             return;
849                         }
850                         //if it is --> it means it is collapsed and further expand doesn't make sense. 
851                         else if (null != currentItem)
852                         {
853                             int j = 0;
854                             //get index of item which we've tried to expand recently
855                             for (; j < this.itemsToExpand.Length; ++j)
856                             {
857                                 if (ModelItem.Equals(this.itemsToExpand[j], currentItem))
858                                 {
859                                     break;
860                                 }
861                             }
862                             //starting at that point, see if given item can be a breadcrumb root
863                             for (int skipLevel = 0; j >= 0; --j)
864                             {
865                                 currentItem = this.itemsToExpand[j];
866                                 //if it can - make it a new breadcrumb root and restart
867                                 if (this.viewService.ShouldAppearOnBreadCrumb(currentItem, true))
868                                 {
869                                     //make that designer a new root (don't set selection)
870                                     this.DesignerView.MakeRootDesigner(currentItem, false);
871                                     //and try to set focus with less maximum expand level, assuming that current designer is now expanded
872                                     ModelItemFocusHelper.Focus(this.itemToFocus, this.currentLevel - skipLevel, this.shouldGetKeyboardFocus);
873                                     return;
874                                 }
875                                 ++skipLevel;
876                             }
877                             //nothing in parent list can be made a breadcrumb, try set item which is supposed to get focus as a root 
878                             if (this.viewService.ShouldAppearOnBreadCrumb(this.itemToFocus, true))
879                             {
880                                 this.DesignerView.MakeRootDesigner(this.itemToFocus, false);
881                                 ModelItemFocusHelper.Focus(this.itemToFocus, 1, this.shouldGetKeyboardFocus);
882                                 return;
883                             }
884                             //the item we want to set focus to, also cannot be displayed as root;
885                             //at this point - simply set selection to the current item, check if visibility has changed due to selection change
886                             this.Context.Items.SetValue(new Selection(currentItem));
887                             Dispatcher.CurrentDispatcher.BeginInvoke(this.onElementFocusingDelegate, DispatcherPriority.ContextIdle, currentItem);
888                             //the final check - if item is still not visible, force it to be 
889                             Dispatcher.CurrentDispatcher.BeginInvoke(this.onForceElementFocusDelegate, DispatcherPriority.ContextIdle);
890                             return;
891                         }
892                     }
893                 }
894                 ModelItemFocusHelper.focusTicket = null;
895                 //if we end up here and itemsToExpand is not null - something is wrong...
896                 //it is possible that algorithm stops here and itemsToExpand is null - this would be scenario when user tries to set focus to model item which cannot be
897                 //visualized and doesn't have any visual parent - i.e. Service or ActivityBuilder (they have a child property Body which can be visualized, but themselves - are not)
898                 if (null != this.itemsToExpand)
899                 {
900                     var displayProperty = this.itemToFocus.Properties["DisplayName"];
901                     var displayName = displayProperty == null ? "(unknown)" : displayProperty.ComputedValue.ToString();
902                     Fx.Assert("Expand is in invalid state - we should never end up here. Item to focus: " + displayName + " (" + this.itemToFocus.ItemType.Name + ")");
903                 }
904             }
905
906             //Populate method is executed by Expand method. It is supposed to bring container element into view, 
907             //find the elemennt we are looking for (or at least container which contains it). After bringing contaner into view, it delegates calls to 
908             //OnPopulateContainer (if we have virutal container) and then to OnFocusElement delegate
909             void Populate(FrameworkElement container)
910             {
911                 //ensure container is in the view
912                 container.BringIntoView();
913                 //is it virtualized container?
914                 var virtualContainer = container as VirtualizedContainerService.VirtualizingContainer;
915                 var viewElement = container as WorkflowViewElement;
916                 var modelItem = (null != virtualContainer ? virtualContainer.ModelItem : (viewElement != null ? viewElement.ModelItem : null));
917                 var dispatchParameter = new object[] { modelItem };
918                 DispatcherPriority priority = DispatcherPriority.Send;
919
920                 if (null != virtualContainer)
921                 {
922                     priority = DispatcherPriority.ContextIdle;
923                     //yes - ensure its content is populated
924                     virtualContainer.Populate();
925                     //wait until container content renders (delegate calls to application idle)
926                     Dispatcher.CurrentDispatcher.BeginInvoke(this.onContainerPopulatingDelegate, priority, virtualContainer);
927                 }
928                 //if we have a virtual contianer - we may need to drill further or simply display an element, 
929                 //otherwise - just try to focus on element (it should be visible, so execute callback immediately)
930                 Dispatcher.CurrentDispatcher.BeginInvoke(this.onElementFocusingDelegate, priority, dispatchParameter);
931             }
932
933             void OnPopulateContainer(VirtualizedContainerService.VirtualizingContainer virtualContainer)
934             {
935                 if (this.shouldAbort)
936                 {
937                     return;
938                 }
939                 //if this is virutal container, it might contain multiple other virtual containers - i need to find the one
940                 //which either is a container for item i want to focus, or one which is parent designer for the item i'm looking for
941                 //look for the container which contains or is a parent of container i look for
942                 var target = virtualContainer
943                     .ChildContainers
944                     .FirstOrDefault(p => ModelItem.Equals(this.itemToFocus, p.ModelItem) || p.ModelItem.IsParentOf(this.itemToFocus));
945
946                 //if one is found - populate it and bring it into view
947                 if (null != target)
948                 {
949                     target.Populate();
950                     target.BringIntoView();
951                 }
952             }
953
954             void OnFocusElement(ModelItem currentItem)
955             {
956                 if (this.shouldAbort)
957                 {
958                     return;
959                 }
960
961                 //after virtual container is loaded and populated, check if the item i'm looking for is visible
962                 if (null != this.itemToFocus.View && ((FrameworkElement)this.itemToFocus.View).IsVisible)
963                 {
964                     //yes! - it is visible, bring it into view and set focus
965                     if (rectToBringIntoView != Rect.Empty)
966                     {
967                         ((FrameworkElement)this.itemToFocus.View).BringIntoView(rectToBringIntoView);
968                     }
969                     else
970                     {
971                         ((FrameworkElement)this.itemToFocus.View).BringIntoView();
972                     }
973                     if (this.shouldGetKeyboardFocus)
974                     {
975                         Keyboard.Focus(this.itemToFocus.View as IInputElement);
976                     }
977                     ModelItemFocusHelper.focusTicket = null;
978                 }
979                 else if (null != currentItem)
980                 {
981                     //no, it still isn't visible - try to expand next level
982                     --this.currentLevel;
983                     this.Expand(currentItem);
984                 }
985                 else
986                 {
987                     ModelItemFocusHelper.focusTicket = null;
988                     var displayProperty = this.itemToFocus.Properties["DisplayName"];
989                     var displayName = displayProperty == null ? "(unknown)" : displayProperty.ComputedValue.ToString();
990                     Fx.Assert("OnFocusElement is in invalid state - we should never get here. Item to focus: " + displayName + " (" + this.itemToFocus.ItemType.Name + ")");
991                 }
992             }
993
994             void OnForceFocusElement()
995             {
996                 if (this.shouldAbort)
997                 {
998                     return;
999                 }
1000                 //if we did exploit all possibilites but model item is still not visible and focused - force the lowest parent that can be made root as the root designer
1001                 if (null == ModelItemFocusHelper.focusTicket && (null == this.itemToFocus.View || !((UIElement)this.itemToFocus.View).IsVisible))
1002                 {
1003                     ModelItem item = this.itemToFocus;
1004                     while (item != null && !this.ViewService.ShouldAppearOnBreadCrumb(item, true))
1005                     {
1006                         item = item.Parent;
1007                     }
1008                     if (item != null)
1009                     {
1010                         this.DesignerView.MakeRootDesigner(item, false, false);
1011                         Dispatcher.CurrentDispatcher.BeginInvoke(this.onElementFocusingDelegate, DispatcherPriority.ContextIdle, item);
1012                     }
1013                 }
1014             }
1015
1016             void ChangeDesignerViewVisibility(Visibility state)
1017             {
1018                 if (!this.shouldAbort)
1019                 {
1020                     //i can't set visibility to hidden, so in order to avoid flickering, i simply set opacity to very low value - 
1021                     //visual tree is still visible, but user won't notice it.
1022                     //this.DesignerView.ScrollableContent.Opacity = (state == Visibility.Visible ? 1.0 : 0.01);
1023                     Mouse.OverrideCursor = (state == Visibility.Visible ? null : Cursors.Wait);
1024                 }
1025             }
1026         }
1027     }
1028 }