1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Model
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;
18 using System.Windows.Input;
19 using System.Windows.Threading;
21 public static class ModelItemExtensions
23 const int MaxExpandLevel = 50;
24 const string rootPath = "Root";
26 public static EditingContext GetEditingContext(this ModelItem modelItem)
28 EditingContext result = null;
29 IModelTreeItem modelTreeItem = modelItem as IModelTreeItem;
30 if (null != modelTreeItem && null != modelTreeItem.ModelTreeManager)
32 result = modelTreeItem.ModelTreeManager.Context;
37 internal static ModelItem FindParentModelItem(this ModelItem item, Type parentType)
41 throw FxTrace.Exception.ArgumentNull("item");
43 if (null == parentType)
45 throw FxTrace.Exception.ArgumentNull("parentType");
48 ModelItem result = null;
50 while (item != null && !parentType.IsAssignableFrom(item.ItemType))
54 if (null != item && parentType.IsAssignableFrom(item.ItemType))
61 internal static bool SwitchKeys(this ModelItemDictionary dictionary, ModelItem oldKey, ModelItem newKey)
63 if (null == dictionary)
65 throw FxTrace.Exception.AsError(new ArgumentNullException("dictionary"));
69 throw FxTrace.Exception.AsError(new ArgumentNullException("oldKey"));
73 throw FxTrace.Exception.AsError(new ArgumentNullException("newKey"));
75 if (!dictionary.ContainsKey(oldKey))
77 throw FxTrace.Exception.AsError(new KeyNotFoundException(null == oldKey.GetCurrentValue() ? "oldKey" : oldKey.GetCurrentValue().ToString()));
80 if (!dictionary.ContainsKey(newKey))
82 ModelItem value = dictionary[oldKey];
83 dictionary.Remove(oldKey);
84 dictionary[newKey] = value;
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)
94 if (null == dictionary)
96 throw FxTrace.Exception.AsError(new ArgumentNullException("dictionary"));
100 throw FxTrace.Exception.AsError(new ArgumentNullException("oldKey"));
104 throw FxTrace.Exception.AsError(new ArgumentNullException("newKey"));
106 if (!dictionary.ContainsKey(oldKey))
108 throw FxTrace.Exception.AsError(new KeyNotFoundException(oldKey.ToString()));
112 if (typeof(ModelItem).IsAssignableFrom(oldKey.GetType()) && typeof(ModelItem).IsAssignableFrom(newKey.GetType()))
114 result = SwitchKeys(dictionary, (ModelItem)oldKey, (ModelItem)newKey);
115 newKeyItem = (ModelItem)newKey;
119 if (typeof(ModelItem).IsAssignableFrom(oldKey.GetType()))
121 oldKey = ((ModelItem)oldKey).GetCurrentValue();
124 throw FxTrace.Exception.AsError(new InvalidOperationException("((ModelItem)oldKey).GetCurrentValue()"));
127 if (typeof(ModelItem).IsAssignableFrom(newKey.GetType()))
129 newKey = ((ModelItem)newKey).GetCurrentValue();
132 throw FxTrace.Exception.AsError(new InvalidOperationException("((ModelItem)newKey).GetCurrentValue()"));
136 if (!dictionary.ContainsKey(newKey))
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));
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)
153 bool result = TryGetPropertyValue(item, out temp, path);
156 value = (ModelItemCollection)temp;
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)
167 bool result = TryGetPropertyValue(item, out temp, path);
170 value = (ModelItemDictionary)temp;
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)
181 throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
185 throw FxTrace.Exception.AsError(new ArgumentNullException("path"));
189 throw FxTrace.Exception.AsError(new ArgumentException(SR.ModelItemPathArrayShouldNotBeEmpty));
193 for (int i = 0; i < path.Length && true == result && null != value; ++i)
195 ModelProperty property = value.Properties[path[i]];
196 if (null != property)
198 value = property.Value;
206 throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.PropertyDoesntExistFormatString, path[i])));
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)
218 throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
222 throw FxTrace.Exception.AsError(new ArgumentNullException("path"));
226 throw FxTrace.Exception.AsError(new ArgumentException(SR.ModelItemPathArrayShouldNotBeEmpty));
230 for (int i = 0; i < path.Length && true == result; ++i)
232 ModelProperty property = item.Properties[path[i]];
233 if (null != property)
235 if (i == path.Length - 1)
239 wrappedValue = property.SetValue(value);
243 property.ClearValue();
248 item = property.Value;
257 throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.PropertyDoesntExistFormatString, path[i])));
263 internal static bool HasAnnotation(this ModelItem modelItem)
265 Fx.Assert(modelItem != null, "modelItem should not be null.");
267 ModelProperty property = modelItem.Properties.Find(Annotation.AnnotationTextPropertyName);
269 Fx.Assert(property != null, "Annotation property should not be null");
271 if (property.ComputedValue == null)
279 public static string GetModelPath(this ModelItem modelItem)
281 if (modelItem == null)
285 StringBuilder sb = new StringBuilder();
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].----
291 // if modelItem doesn't have parent and it's not root, return string.Empty;
292 if (modelItem.Parent == null && modelItem.Root != modelItem)
297 while (modelItem != null)
299 // paths causing us to get into loops are invalid.
300 if (visited.Contains(modelItem))
305 // remember the visited.
306 visited.Add(modelItem);
307 // if parent is collection store just the index
308 if (modelItem.Parent is ModelItemCollection)
310 sb.Insert(0, "[" + ((ModelItemCollection)modelItem.Parent).IndexOf(modelItem).ToString(CultureInfo.InvariantCulture) + "]");
312 // if parent is a modelproperty store the property name
313 else if (modelItem.Source != null)
315 sb.Insert(0, "." + modelItem.Source.Name);
317 //Our model path doesnt work with dictionaries, so in dictionary case follow the mutablekeyvaluepair
318 if (modelItem.Parent is ModelItemDictionary)
320 if (modelItem.Source != null)
322 modelItem = modelItem.Source.Parent;
329 } // when parent is not a dictionary follow the parent up towards the root.
332 modelItem = modelItem.Parent;
338 sb.Insert(0, rootPath);
344 public static ModelItem GetModelItemFromPath(string path, ModelItem root)
348 throw FxTrace.Exception.AsError(new ArgumentNullException("root"));
352 throw FxTrace.Exception.AsError(new ArgumentNullException("path"));
354 ModelItem foundModelItem = null;
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)
360 foundModelItem = root;
361 for (int segmentIndex = 1; segmentIndex < segments.Length; segmentIndex++)
363 string segment = segments[segmentIndex];
364 if (!string.IsNullOrEmpty(segment))
366 ModelItem next = GetModelItemFromSegment(foundModelItem, segment);
369 foundModelItem = next;
373 foundModelItem = null;
379 foundModelItem = null;
384 return foundModelItem;
387 private static ModelItem GetModelItemFromSegment(ModelItem currentModelItem, string segment)
389 ModelItem modelItemFromSegment = null;
390 int indexOfSquareBrackets = segment.IndexOf('[');
391 // e.g Sequence.Activities[0] segment = "Activities[0]"
392 if (indexOfSquareBrackets > 0)
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)
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)
407 // now index into the collection
408 modelItemFromSegment = segmentCollection[index];
412 catch (FormatException)
415 catch (OverflowException)
420 // e.g SomeFoo.Then segment = "Then"
423 ModelProperty property = currentModelItem.Properties[segment];
424 if (property != null)
426 modelItemFromSegment = property.Value;
429 return modelItemFromSegment;
432 internal static IEnumerable<ModelItem> GetParentEnumerator(this ModelItem item)
434 return ModelItemExtensions.GetParentEnumerator(item, null);
437 internal static IEnumerable<ModelItem> GetParentEnumerator(this ModelItem item, Func<ModelItem, bool> continueEnumerationPredicate)
441 throw FxTrace.Exception.AsError(new ArgumentNullException("item"));
443 while (null != item.Parent)
445 if (null != continueEnumerationPredicate && !continueEnumerationPredicate(item.Parent))
449 yield return item.Parent;
455 internal static string GetUniqueName(this ModelItemCollection collection, string nameDefaultPrefix, Func<ModelItem, string> nameGetter)
457 return collection.GetUniqueName<ModelItem>(nameDefaultPrefix, nameGetter);
460 internal static string GetUniqueName(this ModelItemDictionary dictionary, string nameDefaultPrefix, Func<ModelItem, string> nameGetter)
462 if (dictionary != null)
464 return dictionary.Keys.GetUniqueName(nameDefaultPrefix, nameGetter);
468 throw FxTrace.Exception.ArgumentNull("dictionary");
472 internal static string GetUniqueName<T>(this IEnumerable<T> collection, string nameDefaultPrefix, Func<T, string> nameGetter)
474 if (null == collection)
476 throw FxTrace.Exception.ArgumentNull("collection");
478 if (null == nameDefaultPrefix)
480 throw FxTrace.Exception.ArgumentNull("nameDefaultPrefix");
482 if (nameDefaultPrefix.Length == 0)
484 throw FxTrace.Exception.Argument("nameDefaultPrefix", "length == 0");
486 if (null == nameGetter)
488 throw FxTrace.Exception.ArgumentNull("nameGetter");
491 var maxId = (int?)collection
494 var value = nameGetter(p);
497 return (0 == string.Compare(value, 0, nameDefaultPrefix, 0, nameDefaultPrefix.Length, CultureInfo.CurrentCulture, CompareOptions.None));
504 return (int.TryParse(nameGetter(p).Substring(nameDefaultPrefix.Length), out result))
507 .OrderByDescending(p => p)
510 int id = maxId.HasValue ? maxId.Value + 1 : 1;
512 return string.Format(CultureInfo.CurrentCulture, "{0}{1}", nameDefaultPrefix, id);
515 internal static bool IsAssignableFrom<T>(this ModelItem item) where T : class
519 throw FxTrace.Exception.ArgumentNull("item");
522 return typeof(T).IsAssignableFrom(item.ItemType);
525 internal static Activity GetRootActivity(this ModelItem item)
527 Object root = item.GetCurrentValue();
528 if (root is IDebuggableWorkflowTree)
530 return ((IDebuggableWorkflowTree)root).GetWorkflowRoot();
534 return root as Activity;
538 public static bool IsParentOf(this ModelItem item, ModelItem child)
542 throw FxTrace.Exception.ArgumentNull("item");
546 throw FxTrace.Exception.ArgumentNull("child");
549 bool isParent = false;
550 child.GetParentEnumerator(p => { isParent = ModelItem.Equals(p, item); return !isParent; }).LastOrDefault();
554 public static void Focus(this ModelItem item)
556 Focus(item, MaxExpandLevel);
559 internal static void Highlight(this ModelItem item)
561 ModelItemFocusHelper.Focus(item, MaxExpandLevel, false, Rect.Empty);
564 internal static void Highlight(this ModelItem item, Rect rectToBringIntoView)
566 ModelItemFocusHelper.Focus(item, MaxExpandLevel, false, rectToBringIntoView);
569 public static void Focus(this ModelItem item, int level)
573 throw FxTrace.Exception.ArgumentNull("item");
577 throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("level"));
579 ModelItemFocusHelper.Focus(item, level);
582 internal static ModelItem FindParent(this ModelItem item, Predicate<ModelItem> predicate)
584 ModelItem parent = item.Parent;
586 while (parent != null && !predicate(parent))
588 parent = parent.Parent;
594 private sealed class ModelItemFocusHelper
596 static ModelItemFocusHelper focusTicket = null;
598 ModelItem itemToFocus;
600 bool shouldAbort = false;
602 EditingContext context;
603 VirtualizedContainerService containerService;
604 WorkflowViewService viewService;
605 DesignerView designerView;
606 ModelItem[] itemsToExpand;
607 bool shouldGetKeyboardFocus;
608 Rect rectToBringIntoView;
610 EditingContext Context
614 if (null == this.context)
616 this.context = this.itemToFocus.GetEditingContext();
621 VirtualizedContainerService ContainerService
625 if (null == this.containerService)
627 this.containerService = this.Context.Services.GetService<VirtualizedContainerService>();
629 return this.containerService;
632 WorkflowViewService ViewService
636 if (null == this.viewService)
638 this.viewService = (WorkflowViewService)this.Context.Services.GetService<ViewService>();
640 return this.viewService;
643 DesignerView DesignerView
647 if (null == this.designerView)
649 this.designerView = this.Context.Services.GetService<DesignerView>();
651 return this.designerView;
655 Action<VirtualizedContainerService.VirtualizingContainer> onContainerPopulatingDelegate;
656 Action<ModelItem> onElementFocusingDelegate;
657 Action<Visibility> onSetDesignerContentVisibilityDelegate;
658 Action onForceElementFocusDelegate;
661 private ModelItemFocusHelper(ModelItem itemToFocus, int maxExpandLevel, bool shouldGetKeyboardFocus, Rect rectToBringIntoView)
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;
673 // Checks if a model item is rooted at a specific model item
674 static bool IsRootedAt(ModelItem item, ModelItem root)
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)
681 currentItem = currentItem.Parent;
683 return currentItem == root;
686 public static void Focus(ModelItem itemToFocus, int maxExpandLevel)
688 Focus(itemToFocus, maxExpandLevel, true);
691 internal static void Focus(ModelItem itemToFocus, int maxExpandLevel, bool shouldGetKeyboardFocus)
693 Focus(itemToFocus, maxExpandLevel, shouldGetKeyboardFocus, Rect.Empty);
696 internal static void Focus(ModelItem itemToFocus, int maxExpandLevel, bool shouldGetKeyboardFocus, Rect rectToBringIntoView)
698 // Check if this model item exist in the model tree
699 IModelTreeItem modelTreeItem = itemToFocus as IModelTreeItem;
700 if (modelTreeItem != null)
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))
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)
714 focusTicket.shouldAbort = true;
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);
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
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);
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);
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)
748 this.onElementFocusingDelegate(this.itemToFocus);
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)
760 //filter only items with designer attribute
762 var designerType = this.ViewService.GetDesignerType(p.ItemType);
763 if (null != designerType)
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)
772 shouldContinue = false;
777 .Take(this.currentLevel)
782 //nothing to expand, rather unlikely, but handle it anyway
783 if (visualItems.Length == 0)
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();
792 //get the first parent of an item, which is visible
793 var firstVisibleItem = visualItems.FirstOrDefault(p => null != p.View && ((UIElement)p.View).IsVisible);
795 bool enqueueFirstExpand = false;
797 //is there anything visible in the path between item and its parents?
798 if (null != firstVisibleItem)
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();
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);
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);
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)
823 if (this.shouldAbort)
828 //stop condition - prevents infinite loop (the method is delegated into dispatcher, so it would never cause stack overflow
829 if (0 > this.currentLevel)
831 ModelItemFocusHelper.focusTicket = null;
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)
838 //is given parent visible? (it would return container for given model item)
839 var container = this.ContainerService.QueryContainerForItem(this.itemsToExpand[index]);
841 if (null != container)
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]))
847 this.Populate(container);
850 //if it is --> it means it is collapsed and further expand doesn't make sense.
851 else if (null != currentItem)
854 //get index of item which we've tried to expand recently
855 for (; j < this.itemsToExpand.Length; ++j)
857 if (ModelItem.Equals(this.itemsToExpand[j], currentItem))
862 //starting at that point, see if given item can be a breadcrumb root
863 for (int skipLevel = 0; j >= 0; --j)
865 currentItem = this.itemsToExpand[j];
866 //if it can - make it a new breadcrumb root and restart
867 if (this.viewService.ShouldAppearOnBreadCrumb(currentItem, true))
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);
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))
880 this.DesignerView.MakeRootDesigner(this.itemToFocus, false);
881 ModelItemFocusHelper.Focus(this.itemToFocus, 1, this.shouldGetKeyboardFocus);
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);
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)
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 + ")");
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)
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;
920 if (null != virtualContainer)
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);
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);
933 void OnPopulateContainer(VirtualizedContainerService.VirtualizingContainer virtualContainer)
935 if (this.shouldAbort)
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
944 .FirstOrDefault(p => ModelItem.Equals(this.itemToFocus, p.ModelItem) || p.ModelItem.IsParentOf(this.itemToFocus));
946 //if one is found - populate it and bring it into view
950 target.BringIntoView();
954 void OnFocusElement(ModelItem currentItem)
956 if (this.shouldAbort)
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)
964 //yes! - it is visible, bring it into view and set focus
965 if (rectToBringIntoView != Rect.Empty)
967 ((FrameworkElement)this.itemToFocus.View).BringIntoView(rectToBringIntoView);
971 ((FrameworkElement)this.itemToFocus.View).BringIntoView();
973 if (this.shouldGetKeyboardFocus)
975 Keyboard.Focus(this.itemToFocus.View as IInputElement);
977 ModelItemFocusHelper.focusTicket = null;
979 else if (null != currentItem)
981 //no, it still isn't visible - try to expand next level
983 this.Expand(currentItem);
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 + ")");
994 void OnForceFocusElement()
996 if (this.shouldAbort)
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))
1003 ModelItem item = this.itemToFocus;
1004 while (item != null && !this.ViewService.ShouldAppearOnBreadCrumb(item, true))
1010 this.DesignerView.MakeRootDesigner(item, false, false);
1011 Dispatcher.CurrentDispatcher.BeginInvoke(this.onElementFocusingDelegate, DispatcherPriority.ContextIdle, item);
1016 void ChangeDesignerViewVisibility(Visibility state)
1018 if (!this.shouldAbort)
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);