Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / NetFx40 / Tools / System.Activities.Presentation / System / Activities / Presentation / Model / ModelItemExtensions.cs / 1407647 / ModelItemExtensions.cs
//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Model { using System; using System.Activities.Debugger; using System.Activities.Presentation.Services; using System.Activities.Presentation.View; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Runtime; using System.Text; using System.Windows; using System.Windows.Input; using System.Windows.Threading; public static class ModelItemExtensions { const int MaxExpandLevel = 5; const string rootPath = "Root"; public static EditingContext GetEditingContext(this ModelItem modelItem) { EditingContext result = null; IModelTreeItem modelTreeItem = modelItem as IModelTreeItem; if (null != modelTreeItem && null != modelTreeItem.ModelTreeManager) { result = modelTreeItem.ModelTreeManager.Context; } return result; } internal static ModelItem FindParentModelItem(this ModelItem item, Type parentType) { if (null == item) { throw FxTrace.Exception.ArgumentNull("item"); } if (null == parentType) { throw FxTrace.Exception.ArgumentNull("parentType"); } ModelItem result = null; item = item.Parent; while (item != null && !parentType.IsAssignableFrom(item.ItemType)) { item = item.Parent; } if (null != item && parentType.IsAssignableFrom(item.ItemType)) { result = item; } return result; } internal static bool SwitchKeys( this ModelItemDictionary dictionary, ModelItem oldKey, ModelItem newKey ) { if (null == dictionary) { throw FxTrace.Exception.AsError( new ArgumentNullException("dictionary")); } if (null == oldKey) { throw FxTrace.Exception.AsError( new ArgumentNullException("oldKey")); } if (null == newKey) { throw FxTrace.Exception.AsError( new ArgumentNullException("newKey")); } if (!dictionary.ContainsKey(oldKey)) { throw FxTrace.Exception.AsError( new KeyNotFoundException(null == oldKey.GetCurrentValue() ? "oldKey" : oldKey.GetCurrentValue().ToString())); } bool result = false; if (!dictionary.ContainsKey(newKey)) { ModelItem value = dictionary[oldKey]; dictionary.Remove(oldKey); dictionary[newKey] = value; result = true; } return result; } [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is a TryGet pattern that requires out parameters")] internal static bool SwitchKeys(this ModelItemDictionary dictionary, object oldKey, object newKey, out ModelItem newKeyItem) { if (null == dictionary) { throw FxTrace.Exception.AsError( new ArgumentNullException("dictionary")); } if (null == oldKey) { throw FxTrace.Exception.AsError( new ArgumentNullException("oldKey")); } if (null == newKey) { throw FxTrace.Exception.AsError( new ArgumentNullException("newKey")); } if (!dictionary.ContainsKey(oldKey)) { throw FxTrace.Exception.AsError( new KeyNotFoundException(oldKey.ToString())); } bool result = false; newKeyItem = null; if (typeof(ModelItem).IsAssignableFrom(oldKey.GetType()) && typeof(ModelItem).IsAssignableFrom(newKey.GetType())) { result = SwitchKeys(dictionary, (ModelItem)oldKey, (ModelItem)newKey); newKeyItem = (ModelItem)newKey; } else { if (typeof(ModelItem).IsAssignableFrom(oldKey.GetType())) { oldKey = ((ModelItem)oldKey).GetCurrentValue(); if (null == oldKey) { throw FxTrace.Exception.AsError( new InvalidOperationException("((ModelItem)oldKey).GetCurrentValue()")); } } if (typeof(ModelItem).IsAssignableFrom(newKey.GetType())) { newKey = ((ModelItem)newKey).GetCurrentValue(); if (null == newKey) { throw FxTrace.Exception.AsError(new InvalidOperationException("((ModelItem)newKey).GetCurrentValue()")); } } } if (!dictionary.ContainsKey(newKey)) { ModelItem value = dictionary[oldKey]; dictionary.Remove(oldKey); dictionary[newKey] = value; newKeyItem = dictionary.Keys.First(p => object.Equals(p.GetCurrentValue(), newKey)); result = true; } return result; } [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is a TryGet pattern that requires out parameters")] internal static bool TryGetPropertyValue(this ModelItem item, out ModelItemCollection value, params string[] path) { ModelItem temp; value = null; bool result = TryGetPropertyValue(item, out temp, path); if (null != item) { value = (ModelItemCollection)temp; } return result; } [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is a TryGet pattern that requires out parameters")] internal static bool TryGetPropertyValue(this ModelItem item, out ModelItemDictionary value, params string[] path) { ModelItem temp; value = null; bool result = TryGetPropertyValue(item, out temp, path); if (null != item) { value = (ModelItemDictionary)temp; } return result; } [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is a TryGet pattern that requires out parameters")] internal static bool TryGetPropertyValue(this ModelItem item, out ModelItem value, params string[] path) { if (null == item) { throw FxTrace.Exception.AsError( new ArgumentNullException("item")); } if (null == path) { throw FxTrace.Exception.AsError( new ArgumentNullException("path")); } if (path.Length < 1) { throw FxTrace.Exception.AsError(new ArgumentException(SR.ModelItemPathArrayShouldNotBeEmpty)); } value = item; bool result = true; for (int i = 0; i < path.Length && true == result && null != value; ++i) { ModelProperty property = value.Properties[path[i]]; if (null != property) { value = property.Value; if (null == value) { result = false; } } else { throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.PropertyDoesntExistFormatString, path[i]))); } } return result; } [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is a TryGet pattern that requires out parameters")] internal static bool TrySetPropertyValue(this ModelItem item, object value, out ModelItem wrappedValue, params string[] path) { if (null == item) { throw FxTrace.Exception.AsError( new ArgumentNullException("item")); } if (null == path) { throw FxTrace.Exception.AsError( new ArgumentNullException("path")); } if (path.Length < 1) { throw FxTrace.Exception.AsError(new ArgumentException(SR.ModelItemPathArrayShouldNotBeEmpty)); } wrappedValue = null; bool result = true; for (int i = 0; i < path.Length && true == result; ++i) { ModelProperty property = item.Properties[path[i]]; if (null != property) { if (i == path.Length - 1) { if (null != value) { wrappedValue = property.SetValue(value); } else { property.ClearValue(); } } else { item = property.Value; if (null == item) { result = false; } } } else { throw FxTrace.Exception.AsError( new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.PropertyDoesntExistFormatString, path[i]))); } } return result; } public static string GetModelPath(this ModelItem modelItem) { if (modelItem == null) { return null; } StringBuilder sb = new StringBuilder(); bool isValid = true; HashSet visited = new HashSet (); // walk up the parent chain and create a modelpath from reverse // eg. Root.Foo.Bar.Collectionproperty[3].---- while (modelItem != null) { // paths causing us to get into loops are invalid. if (visited.Contains(modelItem)) { isValid = false; break; } // remember the visited. visited.Add(modelItem); // if parent is collection store just the index if (modelItem.Parent is ModelItemCollection) { sb.Insert(0, "[" + ((ModelItemCollection)modelItem.Parent).IndexOf(modelItem).ToString(CultureInfo.InvariantCulture) + "]"); } // if parent is a modelproperty store the property name else if (modelItem.Source != null) { sb.Insert(0, "." + modelItem.Source.Name); } //Our model path doesnt work with dictionaries else if (modelItem.Parent is ModelItemDictionary) { isValid = false; break; } if (modelItem.Source != null) { modelItem = modelItem.Source.Parent; } else { modelItem = modelItem.Parent; } } string s = null; if (isValid) { sb.Insert(0, rootPath); s = sb.ToString(); } return s; } public static ModelItem GetModelItemFromPath(string path, ModelItem root) { if (null == root) { throw FxTrace.Exception.AsError(new ArgumentNullException("root")); } if (null == path) { throw FxTrace.Exception.AsError(new ArgumentNullException("path")); } ModelItem foundModelItem = null; path = path.Trim(); string[] segments = path.Split('.'); // process each path. path should atleast be 'Root' and should always begin with 'Root' if (segments.Length > 0 && segments[0] == rootPath) { foundModelItem = root; for (int segmentIndex = 1; segmentIndex < segments.Length; segmentIndex++) { string segment = segments[segmentIndex]; if (!string.IsNullOrEmpty(segment)) { ModelItem next = GetModelItemFromSegment(foundModelItem, segment); if (next != null) { foundModelItem = next; } else { foundModelItem = null; break; } } else { foundModelItem = null; break; } } } return foundModelItem; } private static ModelItem GetModelItemFromSegment(ModelItem currentModelItem, string segment) { ModelItem modelItemFromSegment = null; int indexOfSquareBrackets = segment.IndexOf('['); // e.g Sequence.Activities[0] segment = "Activities[0]" if (indexOfSquareBrackets > 0) { string collectionProperty = segment.Substring(0, indexOfSquareBrackets); // find the value of the collection property ModelItemCollection segmentCollection = GetModelItemFromSegment(currentModelItem, collectionProperty) as ModelItemCollection; if (segmentCollection != null) { try { // parse the [index] to find the index string indexString = segment.Substring(indexOfSquareBrackets + 1); indexString = indexString.Substring(0, indexString.Length - 1); int index = Int32.Parse(indexString, CultureInfo.InvariantCulture); if (index >= 0 && index < segmentCollection.Count) { // now index into the collection modelItemFromSegment = segmentCollection[index]; } } // dont crash ever. catch (FormatException) { } catch (OverflowException) { } } } // e.g SomeFoo.Then segment = "Then" else { ModelProperty property = currentModelItem.Properties[segment]; if (property != null) { modelItemFromSegment = property.Value; } } return modelItemFromSegment; } internal static IEnumerable GetParentEnumerator(this ModelItem item) { return ModelItemExtensions.GetParentEnumerator(item, null); } internal static IEnumerable GetParentEnumerator(this ModelItem item, Func continueEnumerationPredicate) { if (null == item) { throw FxTrace.Exception.AsError( new ArgumentNullException("item")); } while (null != item.Parent) { if (null != continueEnumerationPredicate && !continueEnumerationPredicate(item.Parent)) { break; } yield return item.Parent; item = item.Parent; } yield break; } internal static string GetUniqueName(this ModelItemCollection collection, string nameDefaultPrefix, Func nameGetter) { return collection.GetUniqueName (nameDefaultPrefix, nameGetter); } internal static string GetUniqueName(this ModelItemDictionary dictionary, string nameDefaultPrefix, Func nameGetter) { if (dictionary != null) { return dictionary.Keys.GetUniqueName(nameDefaultPrefix, nameGetter); } else { throw FxTrace.Exception.ArgumentNull("dictionary"); } } internal static string GetUniqueName (this IEnumerable collection, string nameDefaultPrefix, Func nameGetter) { if (null == collection) { throw FxTrace.Exception.ArgumentNull("collection"); } if (null == nameDefaultPrefix) { throw FxTrace.Exception.ArgumentNull("nameDefaultPrefix"); } if (nameDefaultPrefix.Length == 0) { throw FxTrace.Exception.Argument("nameDefaultPrefix", "length == 0"); } if (null == nameGetter) { throw FxTrace.Exception.ArgumentNull("nameGetter"); } var maxId = (int?)collection. Where(p => { var value = nameGetter(p); if (null != value ) { return (0 == string.Compare(value, 0, nameDefaultPrefix, 0, nameDefaultPrefix.Length, CultureInfo.CurrentCulture, CompareOptions.None)); } return false; }). Select(p => { int result = 0; return (int.TryParse(nameGetter(p).Substring(nameDefaultPrefix.Length), out result)) ? result : 0; }). OrderByDescending(p => p). FirstOrDefault(); int id = maxId.HasValue ? maxId.Value + 1 : 1; return string.Format(CultureInfo.CurrentCulture, "{0}{1}", nameDefaultPrefix, id); } internal static bool IsAssignableFrom (this ModelItem item) where T : class { if (null == item) { throw FxTrace.Exception.ArgumentNull("item"); } return typeof(T).IsAssignableFrom(item.ItemType); } internal static Activity GetRootActivity(this ModelItem item) { Object root = item.GetCurrentValue(); if (root is IDebuggableWorkflowTree) { return ((IDebuggableWorkflowTree)root).GetWorkflowRoot(); } else { return root as Activity; } } public static bool IsParentOf(this ModelItem item, ModelItem child) { if (null == item) { throw FxTrace.Exception.ArgumentNull("item"); } if (null == child) { throw FxTrace.Exception.ArgumentNull("child"); } bool isParent = false; child.GetParentEnumerator(p => { isParent = ModelItem.Equals(p, item); return !isParent; }).LastOrDefault(); return isParent; } public static void Focus(this ModelItem item) { Focus(item, MaxExpandLevel); } public static void Focus(this ModelItem item, int level) { if (null == item) { throw FxTrace.Exception.ArgumentNull("item"); } if (level < 1) { throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("level")); } ModelItemFocusHelper.Focus(item, level); } private sealed class ModelItemFocusHelper { static ModelItemFocusHelper focusTicket = null; ModelItem itemToFocus; int currentLevel; bool shouldAbort = false; EditingContext context; VirtualizedContainerService containerService; WorkflowViewService viewService; DesignerView designerView; ModelItem[] itemsToExpand; EditingContext Context { get { if (null == this.context) { this.context = this.itemToFocus.GetEditingContext(); } return this.context; } } VirtualizedContainerService ContainerService { get { if (null == this.containerService) { this.containerService = this.Context.Services.GetService (); } return this.containerService; } } WorkflowViewService ViewService { get { if (null == this.viewService) { this.viewService = (WorkflowViewService)this.Context.Services.GetService (); } return this.viewService; } } DesignerView DesignerView { get { if (null == this.designerView) { this.designerView = this.Context.Services.GetService (); } return this.designerView; } } Action onContainerPopulatingDelegate; Action onElementFocusingDelegate; Action onSetDesignerContentVisibilityDelegate; Action onForceElementFocusDelegate; private ModelItemFocusHelper(ModelItem itemToFocus, int maxExpandLevel) { this.itemToFocus = itemToFocus; this.currentLevel = maxExpandLevel; this.onContainerPopulatingDelegate = this.OnPopulateContainer; this.onElementFocusingDelegate = this.OnFocusElement; this.onSetDesignerContentVisibilityDelegate = this.ChangeDesignerViewVisibility; this.onForceElementFocusDelegate = this.OnForceFocusElement; } public static void Focus(ModelItem itemToFocus, int maxExpandLevel) { //if there is another focus operation in progress, mark it so it would abort itself on next OnContextIdle processing - //we don't want to multiple elements racing for keyboard focus if (null != focusTicket) { // Only cancel previous focus call if it is to focus to other activity. if (!(itemToFocus.GetCurrentValue() is Activity)) { return; } focusTicket.shouldAbort = true; } //create new focus ticket focusTicket = new ModelItemFocusHelper(itemToFocus, maxExpandLevel); //and start its processing as soon as application gets idle Dispatcher.CurrentDispatcher.BeginInvoke(new Action ((p) => { p.Focus(); }), DispatcherPriority.ContextIdle, focusTicket); } // Entry point method for setting focus. // it is executed exactly once, on application idle event // there are 3 basic paths: // a) optimistic - element we are looking for, is visible - i bring it into view and set keyboard focus to it // 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 // c) pesimistic/complex - element isn't in the view, moreover, it is located in a tree branch which is not (or is partialy) visible void Focus() { //can i continue? if (shouldAbort) { return; } //hide the designer view until focus is set this.onSetDesignerContentVisibilityDelegate(Visibility.Hidden); //delegate visibility restore for designer view after focus update is complete Dispatcher.CurrentDispatcher.BeginInvoke(this.onSetDesignerContentVisibilityDelegate, DispatcherPriority.ApplicationIdle, Visibility.Visible); //easy path - if the current designer is available and visible - bring it to view and focus if (null != this.itemToFocus.View && ((UIElement)this.itemToFocus.View).IsVisible) { this.onElementFocusingDelegate(this.itemToFocus); return; } //set selection to the item to focus, so all apropriate designers get a chance to update themselfs before we start expanding - this may //result in visual tree change Selection.SelectOnly(this.Context, this.itemToFocus); //get items up to the tree root, which can be visualized (have associated designer) //include only up to "level" items (avoid expanding whole tree) bool shouldContinue = true; int visualItemsCount = 0; var visualItems = this.itemToFocus .GetParentEnumerator(p => shouldContinue) .Where(p => { //filter only items with designer attribute bool result = false; var designerType = this.ViewService.GetDesignerType(p.ItemType); if (null != designerType) { result = true; visualItemsCount++; //if designer has Options attribute, check if it always collapsed children - if so, this will be the topmost parent //(displaying anything above, will never display its children) var options = WorkflowViewService.GetAttribute (designerType); if (null != options && options.AlwaysCollapseChildren && visualItemsCount > 2) { shouldContinue = false; } } return result; }) .Take(this.currentLevel) .ToArray(); //nothing to expand, rather unlikely, but handle it anyway if (visualItems.Length == 0) { //reset ticket, to prevent any further calls from executing ModelItemFocusHelper.focusTicket = null; //force item to be root designer (this is last resort, it is executed only if focusTicket is null) this.onForceElementFocusDelegate(); return; } //get the first parent of an item, which is visible var firstVisibleItem = visualItems.FirstOrDefault(p => null != p.View && ((UIElement)p.View).IsVisible); bool enqueueFirstExpand = false; //is there anything visible in the path between item and its parents? if (null != firstVisibleItem) { //yes - limit the amount of items to expand to only designers which are not visible yet //(include the first visible designer, so algorithm can have a start point with something visible) this.itemsToExpand = visualItems.TakeWhile(p => firstVisibleItem != p ).Concat( new ModelItem[] { firstVisibleItem} ).ToArray(); } else { //no, nothing is visible yet this.itemsToExpand = visualItems; enqueueFirstExpand = true; //make the top most parent as root designer this.DesignerView.MakeRootDesigner(this.itemsToExpand[this.itemsToExpand.Length - 1], false); } //delegate Expand call - if nothing is visible yet - onIdle - give new designer time to fully render, if someting is visible - execute immediatelly Dispatcher.CurrentDispatcher.BeginInvoke(new Action( () => { this.Expand(null); }), enqueueFirstExpand ? DispatcherPriority.ContextIdle : DispatcherPriority.Send); } //Expand method is executed repeatadly until maximum expand level is reached. it iterates through the model item tree //(from child up to MaximumExpandLevel parents) and tries to find first visible designer and populate it with content //If one elemnt is visited twice (denoted by currentItem argument) it means that expansion failed - (i.e. element is collapsed), //so i try to set that element as root designer and restart algoritm with that designer beeing new root void Expand(ModelItem currentItem) { //can i continue? if (this.shouldAbort) { return; } //stop condition - prevents infinite loop (the method is delegated into dispatcher, so it would never cause stack overflow if (0 > this.currentLevel) { ModelItemFocusHelper.focusTicket = null; return; } //browse direct parents, and Populate the fist one which is visible for (int index = 0; null != this.itemsToExpand && index < this.itemsToExpand.Length; ++index) { //is given parent visible? (it would return container for given model item) var container = this.ContainerService.QueryContainerForItem(this.itemsToExpand[index]); if (null != container) { //check if container we are trying to expand is not the same as the one in previous iteration //if it isn't --> populate its content if (!ModelItem.Equals(currentItem, this.itemsToExpand[index])) { this.Populate(container); return; } //if it is --> it means it is collapsed and further expand doesn't make sense. else if (null != currentItem) { int j = 0; //get index of item which we've tried to expand recently for (; j < this.itemsToExpand.Length; ++j) { if (ModelItem.Equals(this.itemsToExpand[j], currentItem)) { break; } } //starting at that point, see if given item can be a breadcrumb root for (int skipLevel = 0; j >= 0; --j) { currentItem = this.itemsToExpand[j]; //if it can - make it a new breadcrumb root and restart if (this.viewService.ShouldAppearOnBreadCrumb(currentItem, true)) { //make that designer a new root (don't set selection) this.DesignerView.MakeRootDesigner(currentItem, false); //and try to set focus with less maximum expand level, assuming that current designer is now expanded ModelItemFocusHelper.Focus(this.itemToFocus, this.currentLevel - skipLevel); return; } ++skipLevel; } //nothing in parent list can be made a breadcrumb, try set item which is supposed to get focus as a root if (this.viewService.ShouldAppearOnBreadCrumb(this.itemToFocus, true)) { this.DesignerView.MakeRootDesigner(this.itemToFocus, false); ModelItemFocusHelper.Focus(this.itemToFocus, 1); return; } //the item we want to set focus to, also cannot be displayed as root; //at this point - simply set selection to given item, check if visibility has changed due to selection change this.Context.Items.SetValue(new Selection(this.itemToFocus)); Dispatcher.CurrentDispatcher.BeginInvoke(this.onElementFocusingDelegate, DispatcherPriority.ContextIdle, currentItem ); //the final check - if item is still not visible, force it to be Dispatcher.CurrentDispatcher.BeginInvoke(this.onForceElementFocusDelegate, DispatcherPriority.ContextIdle); return; } } } ModelItemFocusHelper.focusTicket = null; //if we end up here and itemsToExpand is not null - something is wrong... //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 //visualized and doesn't have any visual parent - i.e. Service or ActivityBuilder (they have a child property Body which can be visualized, but themselfs - are not) if (null != this.itemsToExpand) { var displayProperty = this.itemToFocus.Properties["DisplayName"]; var displayName = displayProperty == null ? "(unknown)" : displayProperty.ComputedValue.ToString(); Fx.Assert("Expand is in invalid state - we should never end up here. Item to focus: " + displayName + " (" + this.itemToFocus.ItemType.Name + ")"); } } //Populate method is executed by Expand method. It is supposed to bring container element into view, //find the elemennt we are looking for (or at least container which contains it). After bringing contaner into view, it delegates calls to //OnPopulateContainer (if we have virutal container) and then to OnFocusElement delegate void Populate(FrameworkElement container) { //ensure container is in the view container.BringIntoView(); //is it virtualized container? var virtualContainer = container as VirtualizedContainerService.VirtualizingContainer; var viewElement = container as WorkflowViewElement; var modelItem = (null != virtualContainer ? virtualContainer.ModelItem : (viewElement != null ? viewElement.ModelItem : null)); var dispatchParameter = new object[] { modelItem }; DispatcherPriority priority = DispatcherPriority.Send; if (null != virtualContainer) { priority = DispatcherPriority.ContextIdle; //yes - ensure its content is populated virtualContainer.Populate(); //wait until container content renders (delegate calls to application idle) Dispatcher.CurrentDispatcher.BeginInvoke(this.onContainerPopulatingDelegate, priority, virtualContainer); } //if we have a virtual contianer - we may need to drill further or simply display an element, //otherwise - just try to focus on element (it should be visible, so execute callback immediately) Dispatcher.CurrentDispatcher.BeginInvoke(this.onElementFocusingDelegate, priority, dispatchParameter); } void OnPopulateContainer(VirtualizedContainerService.VirtualizingContainer virtualContainer) { if (this.shouldAbort) { return; } //if this is virutal container, it might contain multiple other virtual containers - i need to find the one //which either is a container for item i want to focus, or one which is parent designer for the item i'm looking for //look for the container which contains or is a parent of container i look for var target = virtualContainer .ChildContainers .FirstOrDefault(p => ModelItem.Equals(this.itemToFocus, p.ModelItem) || p.ModelItem.IsParentOf(this.itemToFocus)); //if one is found - populate it and bring it into view if (null != target) { target.Populate(); target.BringIntoView(); } } void OnFocusElement(ModelItem currentItem) { if (this.shouldAbort) { return; } //after virtual container is loaded and populated, check if the item i'm looking for is visible if (null != this.itemToFocus.View && ((FrameworkElement)this.itemToFocus.View).IsVisible) { //yes! - it is visible, bring it into view and set focus ((FrameworkElement)this.itemToFocus.View).BringIntoView(); Keyboard.Focus(this.itemToFocus.View as IInputElement); ModelItemFocusHelper.focusTicket = null; } else if (null != currentItem) { //no, it still isn't visible - try to expand next level --this.currentLevel; this.Expand(currentItem); } else { ModelItemFocusHelper.focusTicket = null; var displayProperty = this.itemToFocus.Properties["DisplayName"]; var displayName = displayProperty == null ? "(unknown)" : displayProperty.ComputedValue.ToString(); Fx.Assert("OnFocusElement is in invalid state - we should never get here. Item to focus: " + displayName + " (" + this.itemToFocus.ItemType.Name + ")"); } } void OnForceFocusElement() { if (this.shouldAbort) { return; } //if we did exploit all possibilites but model item is still not visible and focused - force it as a root designer if (null == ModelItemFocusHelper.focusTicket && (null == this.itemToFocus.View || !((UIElement)this.itemToFocus.View).IsVisible)) { this.DesignerView.MakeRootDesigner(this.itemToFocus, false, false); Dispatcher.CurrentDispatcher.BeginInvoke(this.onElementFocusingDelegate, DispatcherPriority.ContextIdle, this.itemToFocus); } } void ChangeDesignerViewVisibility(Visibility state) { if (!this.shouldAbort) { //i can't set visibility to hidden, so in order to avoid flickering, i simply set opacity to very low value - //visual tree is still visible, but user won't notice it. //this.DesignerView.ScrollableContent.Opacity = (state == Visibility.Visible ? 1.0 : 0.01); Mouse.OverrideCursor = (state == Visibility.Visible ? Cursors.Arrow : Cursors.Wait); } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Model { using System; using System.Activities.Debugger; using System.Activities.Presentation.Services; using System.Activities.Presentation.View; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Runtime; using System.Text; using System.Windows; using System.Windows.Input; using System.Windows.Threading; public static class ModelItemExtensions { const int MaxExpandLevel = 5; const string rootPath = "Root"; public static EditingContext GetEditingContext(this ModelItem modelItem) { EditingContext result = null; IModelTreeItem modelTreeItem = modelItem as IModelTreeItem; if (null != modelTreeItem && null != modelTreeItem.ModelTreeManager) { result = modelTreeItem.ModelTreeManager.Context; } return result; } internal static ModelItem FindParentModelItem(this ModelItem item, Type parentType) { if (null == item) { throw FxTrace.Exception.ArgumentNull("item"); } if (null == parentType) { throw FxTrace.Exception.ArgumentNull("parentType"); } ModelItem result = null; item = item.Parent; while (item != null && !parentType.IsAssignableFrom(item.ItemType)) { item = item.Parent; } if (null != item && parentType.IsAssignableFrom(item.ItemType)) { result = item; } return result; } internal static bool SwitchKeys( this ModelItemDictionary dictionary, ModelItem oldKey, ModelItem newKey ) { if (null == dictionary) { throw FxTrace.Exception.AsError( new ArgumentNullException("dictionary")); } if (null == oldKey) { throw FxTrace.Exception.AsError( new ArgumentNullException("oldKey")); } if (null == newKey) { throw FxTrace.Exception.AsError( new ArgumentNullException("newKey")); } if (!dictionary.ContainsKey(oldKey)) { throw FxTrace.Exception.AsError( new KeyNotFoundException(null == oldKey.GetCurrentValue() ? "oldKey" : oldKey.GetCurrentValue().ToString())); } bool result = false; if (!dictionary.ContainsKey(newKey)) { ModelItem value = dictionary[oldKey]; dictionary.Remove(oldKey); dictionary[newKey] = value; result = true; } return result; } [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is a TryGet pattern that requires out parameters")] internal static bool SwitchKeys(this ModelItemDictionary dictionary, object oldKey, object newKey, out ModelItem newKeyItem) { if (null == dictionary) { throw FxTrace.Exception.AsError( new ArgumentNullException("dictionary")); } if (null == oldKey) { throw FxTrace.Exception.AsError( new ArgumentNullException("oldKey")); } if (null == newKey) { throw FxTrace.Exception.AsError( new ArgumentNullException("newKey")); } if (!dictionary.ContainsKey(oldKey)) { throw FxTrace.Exception.AsError( new KeyNotFoundException(oldKey.ToString())); } bool result = false; newKeyItem = null; if (typeof(ModelItem).IsAssignableFrom(oldKey.GetType()) && typeof(ModelItem).IsAssignableFrom(newKey.GetType())) { result = SwitchKeys(dictionary, (ModelItem)oldKey, (ModelItem)newKey); newKeyItem = (ModelItem)newKey; } else { if (typeof(ModelItem).IsAssignableFrom(oldKey.GetType())) { oldKey = ((ModelItem)oldKey).GetCurrentValue(); if (null == oldKey) { throw FxTrace.Exception.AsError( new InvalidOperationException("((ModelItem)oldKey).GetCurrentValue()")); } } if (typeof(ModelItem).IsAssignableFrom(newKey.GetType())) { newKey = ((ModelItem)newKey).GetCurrentValue(); if (null == newKey) { throw FxTrace.Exception.AsError(new InvalidOperationException("((ModelItem)newKey).GetCurrentValue()")); } } } if (!dictionary.ContainsKey(newKey)) { ModelItem value = dictionary[oldKey]; dictionary.Remove(oldKey); dictionary[newKey] = value; newKeyItem = dictionary.Keys.First (p => object.Equals(p.GetCurrentValue(), newKey)); result = true; } return result; } [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is a TryGet pattern that requires out parameters")] internal static bool TryGetPropertyValue(this ModelItem item, out ModelItemCollection value, params string[] path) { ModelItem temp; value = null; bool result = TryGetPropertyValue(item, out temp, path); if (null != item) { value = (ModelItemCollection)temp; } return result; } [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is a TryGet pattern that requires out parameters")] internal static bool TryGetPropertyValue(this ModelItem item, out ModelItemDictionary value, params string[] path) { ModelItem temp; value = null; bool result = TryGetPropertyValue(item, out temp, path); if (null != item) { value = (ModelItemDictionary)temp; } return result; } [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is a TryGet pattern that requires out parameters")] internal static bool TryGetPropertyValue(this ModelItem item, out ModelItem value, params string[] path) { if (null == item) { throw FxTrace.Exception.AsError( new ArgumentNullException("item")); } if (null == path) { throw FxTrace.Exception.AsError( new ArgumentNullException("path")); } if (path.Length < 1) { throw FxTrace.Exception.AsError(new ArgumentException(SR.ModelItemPathArrayShouldNotBeEmpty)); } value = item; bool result = true; for (int i = 0; i < path.Length && true == result && null != value; ++i) { ModelProperty property = value.Properties[path[i]]; if (null != property) { value = property.Value; if (null == value) { result = false; } } else { throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.PropertyDoesntExistFormatString, path[i]))); } } return result; } [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "This is a TryGet pattern that requires out parameters")] internal static bool TrySetPropertyValue(this ModelItem item, object value, out ModelItem wrappedValue, params string[] path) { if (null == item) { throw FxTrace.Exception.AsError( new ArgumentNullException("item")); } if (null == path) { throw FxTrace.Exception.AsError( new ArgumentNullException("path")); } if (path.Length < 1) { throw FxTrace.Exception.AsError(new ArgumentException(SR.ModelItemPathArrayShouldNotBeEmpty)); } wrappedValue = null; bool result = true; for (int i = 0; i < path.Length && true == result; ++i) { ModelProperty property = item.Properties[path[i]]; if (null != property) { if (i == path.Length - 1) { if (null != value) { wrappedValue = property.SetValue(value); } else { property.ClearValue(); } } else { item = property.Value; if (null == item) { result = false; } } } else { throw FxTrace.Exception.AsError( new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.PropertyDoesntExistFormatString, path[i]))); } } return result; } public static string GetModelPath(this ModelItem modelItem) { if (modelItem == null) { return null; } StringBuilder sb = new StringBuilder(); bool isValid = true; HashSet visited = new HashSet (); // walk up the parent chain and create a modelpath from reverse // eg. Root.Foo.Bar.Collectionproperty[3].---- while (modelItem != null) { // paths causing us to get into loops are invalid. if (visited.Contains(modelItem)) { isValid = false; break; } // remember the visited. visited.Add(modelItem); // if parent is collection store just the index if (modelItem.Parent is ModelItemCollection) { sb.Insert(0, "[" + ((ModelItemCollection)modelItem.Parent).IndexOf(modelItem).ToString(CultureInfo.InvariantCulture) + "]"); } // if parent is a modelproperty store the property name else if (modelItem.Source != null) { sb.Insert(0, "." + modelItem.Source.Name); } //Our model path doesnt work with dictionaries else if (modelItem.Parent is ModelItemDictionary) { isValid = false; break; } if (modelItem.Source != null) { modelItem = modelItem.Source.Parent; } else { modelItem = modelItem.Parent; } } string s = null; if (isValid) { sb.Insert(0, rootPath); s = sb.ToString(); } return s; } public static ModelItem GetModelItemFromPath(string path, ModelItem root) { if (null == root) { throw FxTrace.Exception.AsError(new ArgumentNullException("root")); } if (null == path) { throw FxTrace.Exception.AsError(new ArgumentNullException("path")); } ModelItem foundModelItem = null; path = path.Trim(); string[] segments = path.Split('.'); // process each path. path should atleast be 'Root' and should always begin with 'Root' if (segments.Length > 0 && segments[0] == rootPath) { foundModelItem = root; for (int segmentIndex = 1; segmentIndex < segments.Length; segmentIndex++) { string segment = segments[segmentIndex]; if (!string.IsNullOrEmpty(segment)) { ModelItem next = GetModelItemFromSegment(foundModelItem, segment); if (next != null) { foundModelItem = next; } else { foundModelItem = null; break; } } else { foundModelItem = null; break; } } } return foundModelItem; } private static ModelItem GetModelItemFromSegment(ModelItem currentModelItem, string segment) { ModelItem modelItemFromSegment = null; int indexOfSquareBrackets = segment.IndexOf('['); // e.g Sequence.Activities[0] segment = "Activities[0]" if (indexOfSquareBrackets > 0) { string collectionProperty = segment.Substring(0, indexOfSquareBrackets); // find the value of the collection property ModelItemCollection segmentCollection = GetModelItemFromSegment(currentModelItem, collectionProperty) as ModelItemCollection; if (segmentCollection != null) { try { // parse the [index] to find the index string indexString = segment.Substring(indexOfSquareBrackets + 1); indexString = indexString.Substring(0, indexString.Length - 1); int index = Int32.Parse(indexString, CultureInfo.InvariantCulture); if (index >= 0 && index < segmentCollection.Count) { // now index into the collection modelItemFromSegment = segmentCollection[index]; } } // dont crash ever. catch (FormatException) { } catch (OverflowException) { } } } // e.g SomeFoo.Then segment = "Then" else { ModelProperty property = currentModelItem.Properties[segment]; if (property != null) { modelItemFromSegment = property.Value; } } return modelItemFromSegment; } internal static IEnumerable GetParentEnumerator(this ModelItem item) { return ModelItemExtensions.GetParentEnumerator(item, null); } internal static IEnumerable GetParentEnumerator(this ModelItem item, Func continueEnumerationPredicate) { if (null == item) { throw FxTrace.Exception.AsError( new ArgumentNullException("item")); } while (null != item.Parent) { if (null != continueEnumerationPredicate && !continueEnumerationPredicate(item.Parent)) { break; } yield return item.Parent; item = item.Parent; } yield break; } internal static string GetUniqueName(this ModelItemCollection collection, string nameDefaultPrefix, Func nameGetter) { return collection.GetUniqueName (nameDefaultPrefix, nameGetter); } internal static string GetUniqueName(this ModelItemDictionary dictionary, string nameDefaultPrefix, Func nameGetter) { if (dictionary != null) { return dictionary.Keys.GetUniqueName(nameDefaultPrefix, nameGetter); } else { throw FxTrace.Exception.ArgumentNull("dictionary"); } } internal static string GetUniqueName (this IEnumerable collection, string nameDefaultPrefix, Func nameGetter) { if (null == collection) { throw FxTrace.Exception.ArgumentNull("collection"); } if (null == nameDefaultPrefix) { throw FxTrace.Exception.ArgumentNull("nameDefaultPrefix"); } if (nameDefaultPrefix.Length == 0) { throw FxTrace.Exception.Argument("nameDefaultPrefix", "length == 0"); } if (null == nameGetter) { throw FxTrace.Exception.ArgumentNull("nameGetter"); } var maxId = (int?)collection. Where(p => { var value = nameGetter(p); if (null != value ) { return (0 == string.Compare(value, 0, nameDefaultPrefix, 0, nameDefaultPrefix.Length, CultureInfo.CurrentCulture, CompareOptions.None)); } return false; }). Select(p => { int result = 0; return (int.TryParse(nameGetter(p).Substring(nameDefaultPrefix.Length), out result)) ? result : 0; }). OrderByDescending(p => p). FirstOrDefault(); int id = maxId.HasValue ? maxId.Value + 1 : 1; return string.Format(CultureInfo.CurrentCulture, "{0}{1}", nameDefaultPrefix, id); } internal static bool IsAssignableFrom (this ModelItem item) where T : class { if (null == item) { throw FxTrace.Exception.ArgumentNull("item"); } return typeof(T).IsAssignableFrom(item.ItemType); } internal static Activity GetRootActivity(this ModelItem item) { Object root = item.GetCurrentValue(); if (root is IDebuggableWorkflowTree) { return ((IDebuggableWorkflowTree)root).GetWorkflowRoot(); } else { return root as Activity; } } public static bool IsParentOf(this ModelItem item, ModelItem child) { if (null == item) { throw FxTrace.Exception.ArgumentNull("item"); } if (null == child) { throw FxTrace.Exception.ArgumentNull("child"); } bool isParent = false; child.GetParentEnumerator(p => { isParent = ModelItem.Equals(p, item); return !isParent; }).LastOrDefault(); return isParent; } public static void Focus(this ModelItem item) { Focus(item, MaxExpandLevel); } public static void Focus(this ModelItem item, int level) { if (null == item) { throw FxTrace.Exception.ArgumentNull("item"); } if (level < 1) { throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("level")); } ModelItemFocusHelper.Focus(item, level); } private sealed class ModelItemFocusHelper { static ModelItemFocusHelper focusTicket = null; ModelItem itemToFocus; int currentLevel; bool shouldAbort = false; EditingContext context; VirtualizedContainerService containerService; WorkflowViewService viewService; DesignerView designerView; ModelItem[] itemsToExpand; EditingContext Context { get { if (null == this.context) { this.context = this.itemToFocus.GetEditingContext(); } return this.context; } } VirtualizedContainerService ContainerService { get { if (null == this.containerService) { this.containerService = this.Context.Services.GetService (); } return this.containerService; } } WorkflowViewService ViewService { get { if (null == this.viewService) { this.viewService = (WorkflowViewService)this.Context.Services.GetService (); } return this.viewService; } } DesignerView DesignerView { get { if (null == this.designerView) { this.designerView = this.Context.Services.GetService (); } return this.designerView; } } Action onContainerPopulatingDelegate; Action onElementFocusingDelegate; Action onSetDesignerContentVisibilityDelegate; Action onForceElementFocusDelegate; private ModelItemFocusHelper(ModelItem itemToFocus, int maxExpandLevel) { this.itemToFocus = itemToFocus; this.currentLevel = maxExpandLevel; this.onContainerPopulatingDelegate = this.OnPopulateContainer; this.onElementFocusingDelegate = this.OnFocusElement; this.onSetDesignerContentVisibilityDelegate = this.ChangeDesignerViewVisibility; this.onForceElementFocusDelegate = this.OnForceFocusElement; } public static void Focus(ModelItem itemToFocus, int maxExpandLevel) { //if there is another focus operation in progress, mark it so it would abort itself on next OnContextIdle processing - //we don't want to multiple elements racing for keyboard focus if (null != focusTicket) { // Only cancel previous focus call if it is to focus to other activity. if (!(itemToFocus.GetCurrentValue() is Activity)) { return; } focusTicket.shouldAbort = true; } //create new focus ticket focusTicket = new ModelItemFocusHelper(itemToFocus, maxExpandLevel); //and start its processing as soon as application gets idle Dispatcher.CurrentDispatcher.BeginInvoke(new Action ((p) => { p.Focus(); }), DispatcherPriority.ContextIdle, focusTicket); } // Entry point method for setting focus. // it is executed exactly once, on application idle event // there are 3 basic paths: // a) optimistic - element we are looking for, is visible - i bring it into view and set keyboard focus to it // 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 // c) pesimistic/complex - element isn't in the view, moreover, it is located in a tree branch which is not (or is partialy) visible void Focus() { //can i continue? if (shouldAbort) { return; } //hide the designer view until focus is set this.onSetDesignerContentVisibilityDelegate(Visibility.Hidden); //delegate visibility restore for designer view after focus update is complete Dispatcher.CurrentDispatcher.BeginInvoke(this.onSetDesignerContentVisibilityDelegate, DispatcherPriority.ApplicationIdle, Visibility.Visible); //easy path - if the current designer is available and visible - bring it to view and focus if (null != this.itemToFocus.View && ((UIElement)this.itemToFocus.View).IsVisible) { this.onElementFocusingDelegate(this.itemToFocus); return; } //set selection to the item to focus, so all apropriate designers get a chance to update themselfs before we start expanding - this may //result in visual tree change Selection.SelectOnly(this.Context, this.itemToFocus); //get items up to the tree root, which can be visualized (have associated designer) //include only up to "level" items (avoid expanding whole tree) bool shouldContinue = true; int visualItemsCount = 0; var visualItems = this.itemToFocus .GetParentEnumerator(p => shouldContinue) .Where(p => { //filter only items with designer attribute bool result = false; var designerType = this.ViewService.GetDesignerType(p.ItemType); if (null != designerType) { result = true; visualItemsCount++; //if designer has Options attribute, check if it always collapsed children - if so, this will be the topmost parent //(displaying anything above, will never display its children) var options = WorkflowViewService.GetAttribute (designerType); if (null != options && options.AlwaysCollapseChildren && visualItemsCount > 2) { shouldContinue = false; } } return result; }) .Take(this.currentLevel) .ToArray(); //nothing to expand, rather unlikely, but handle it anyway if (visualItems.Length == 0) { //reset ticket, to prevent any further calls from executing ModelItemFocusHelper.focusTicket = null; //force item to be root designer (this is last resort, it is executed only if focusTicket is null) this.onForceElementFocusDelegate(); return; } //get the first parent of an item, which is visible var firstVisibleItem = visualItems.FirstOrDefault(p => null != p.View && ((UIElement)p.View).IsVisible); bool enqueueFirstExpand = false; //is there anything visible in the path between item and its parents? if (null != firstVisibleItem) { //yes - limit the amount of items to expand to only designers which are not visible yet //(include the first visible designer, so algorithm can have a start point with something visible) this.itemsToExpand = visualItems.TakeWhile(p => firstVisibleItem != p ).Concat( new ModelItem[] { firstVisibleItem} ).ToArray(); } else { //no, nothing is visible yet this.itemsToExpand = visualItems; enqueueFirstExpand = true; //make the top most parent as root designer this.DesignerView.MakeRootDesigner(this.itemsToExpand[this.itemsToExpand.Length - 1], false); } //delegate Expand call - if nothing is visible yet - onIdle - give new designer time to fully render, if someting is visible - execute immediatelly Dispatcher.CurrentDispatcher.BeginInvoke(new Action( () => { this.Expand(null); }), enqueueFirstExpand ? DispatcherPriority.ContextIdle : DispatcherPriority.Send); } //Expand method is executed repeatadly until maximum expand level is reached. it iterates through the model item tree //(from child up to MaximumExpandLevel parents) and tries to find first visible designer and populate it with content //If one elemnt is visited twice (denoted by currentItem argument) it means that expansion failed - (i.e. element is collapsed), //so i try to set that element as root designer and restart algoritm with that designer beeing new root void Expand(ModelItem currentItem) { //can i continue? if (this.shouldAbort) { return; } //stop condition - prevents infinite loop (the method is delegated into dispatcher, so it would never cause stack overflow if (0 > this.currentLevel) { ModelItemFocusHelper.focusTicket = null; return; } //browse direct parents, and Populate the fist one which is visible for (int index = 0; null != this.itemsToExpand && index < this.itemsToExpand.Length; ++index) { //is given parent visible? (it would return container for given model item) var container = this.ContainerService.QueryContainerForItem(this.itemsToExpand[index]); if (null != container) { //check if container we are trying to expand is not the same as the one in previous iteration //if it isn't --> populate its content if (!ModelItem.Equals(currentItem, this.itemsToExpand[index])) { this.Populate(container); return; } //if it is --> it means it is collapsed and further expand doesn't make sense. else if (null != currentItem) { int j = 0; //get index of item which we've tried to expand recently for (; j < this.itemsToExpand.Length; ++j) { if (ModelItem.Equals(this.itemsToExpand[j], currentItem)) { break; } } //starting at that point, see if given item can be a breadcrumb root for (int skipLevel = 0; j >= 0; --j) { currentItem = this.itemsToExpand[j]; //if it can - make it a new breadcrumb root and restart if (this.viewService.ShouldAppearOnBreadCrumb(currentItem, true)) { //make that designer a new root (don't set selection) this.DesignerView.MakeRootDesigner(currentItem, false); //and try to set focus with less maximum expand level, assuming that current designer is now expanded ModelItemFocusHelper.Focus(this.itemToFocus, this.currentLevel - skipLevel); return; } ++skipLevel; } //nothing in parent list can be made a breadcrumb, try set item which is supposed to get focus as a root if (this.viewService.ShouldAppearOnBreadCrumb(this.itemToFocus, true)) { this.DesignerView.MakeRootDesigner(this.itemToFocus, false); ModelItemFocusHelper.Focus(this.itemToFocus, 1); return; } //the item we want to set focus to, also cannot be displayed as root; //at this point - simply set selection to given item, check if visibility has changed due to selection change this.Context.Items.SetValue(new Selection(this.itemToFocus)); Dispatcher.CurrentDispatcher.BeginInvoke(this.onElementFocusingDelegate, DispatcherPriority.ContextIdle, currentItem ); //the final check - if item is still not visible, force it to be Dispatcher.CurrentDispatcher.BeginInvoke(this.onForceElementFocusDelegate, DispatcherPriority.ContextIdle); return; } } } ModelItemFocusHelper.focusTicket = null; //if we end up here and itemsToExpand is not null - something is wrong... //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 //visualized and doesn't have any visual parent - i.e. Service or ActivityBuilder (they have a child property Body which can be visualized, but themselfs - are not) if (null != this.itemsToExpand) { var displayProperty = this.itemToFocus.Properties["DisplayName"]; var displayName = displayProperty == null ? "(unknown)" : displayProperty.ComputedValue.ToString(); Fx.Assert("Expand is in invalid state - we should never end up here. Item to focus: " + displayName + " (" + this.itemToFocus.ItemType.Name + ")"); } } //Populate method is executed by Expand method. It is supposed to bring container element into view, //find the elemennt we are looking for (or at least container which contains it). After bringing contaner into view, it delegates calls to //OnPopulateContainer (if we have virutal container) and then to OnFocusElement delegate void Populate(FrameworkElement container) { //ensure container is in the view container.BringIntoView(); //is it virtualized container? var virtualContainer = container as VirtualizedContainerService.VirtualizingContainer; var viewElement = container as WorkflowViewElement; var modelItem = (null != virtualContainer ? virtualContainer.ModelItem : (viewElement != null ? viewElement.ModelItem : null)); var dispatchParameter = new object[] { modelItem }; DispatcherPriority priority = DispatcherPriority.Send; if (null != virtualContainer) { priority = DispatcherPriority.ContextIdle; //yes - ensure its content is populated virtualContainer.Populate(); //wait until container content renders (delegate calls to application idle) Dispatcher.CurrentDispatcher.BeginInvoke(this.onContainerPopulatingDelegate, priority, virtualContainer); } //if we have a virtual contianer - we may need to drill further or simply display an element, //otherwise - just try to focus on element (it should be visible, so execute callback immediately) Dispatcher.CurrentDispatcher.BeginInvoke(this.onElementFocusingDelegate, priority, dispatchParameter); } void OnPopulateContainer(VirtualizedContainerService.VirtualizingContainer virtualContainer) { if (this.shouldAbort) { return; } //if this is virutal container, it might contain multiple other virtual containers - i need to find the one //which either is a container for item i want to focus, or one which is parent designer for the item i'm looking for //look for the container which contains or is a parent of container i look for var target = virtualContainer .ChildContainers .FirstOrDefault(p => ModelItem.Equals(this.itemToFocus, p.ModelItem) || p.ModelItem.IsParentOf(this.itemToFocus)); //if one is found - populate it and bring it into view if (null != target) { target.Populate(); target.BringIntoView(); } } void OnFocusElement(ModelItem currentItem) { if (this.shouldAbort) { return; } //after virtual container is loaded and populated, check if the item i'm looking for is visible if (null != this.itemToFocus.View && ((FrameworkElement)this.itemToFocus.View).IsVisible) { //yes! - it is visible, bring it into view and set focus ((FrameworkElement)this.itemToFocus.View).BringIntoView(); Keyboard.Focus(this.itemToFocus.View as IInputElement); ModelItemFocusHelper.focusTicket = null; } else if (null != currentItem) { //no, it still isn't visible - try to expand next level --this.currentLevel; this.Expand(currentItem); } else { ModelItemFocusHelper.focusTicket = null; var displayProperty = this.itemToFocus.Properties["DisplayName"]; var displayName = displayProperty == null ? "(unknown)" : displayProperty.ComputedValue.ToString(); Fx.Assert("OnFocusElement is in invalid state - we should never get here. Item to focus: " + displayName + " (" + this.itemToFocus.ItemType.Name + ")"); } } void OnForceFocusElement() { if (this.shouldAbort) { return; } //if we did exploit all possibilites but model item is still not visible and focused - force it as a root designer if (null == ModelItemFocusHelper.focusTicket && (null == this.itemToFocus.View || !((UIElement)this.itemToFocus.View).IsVisible)) { this.DesignerView.MakeRootDesigner(this.itemToFocus, false, false); Dispatcher.CurrentDispatcher.BeginInvoke(this.onElementFocusingDelegate, DispatcherPriority.ContextIdle, this.itemToFocus); } } void ChangeDesignerViewVisibility(Visibility state) { if (!this.shouldAbort) { //i can't set visibility to hidden, so in order to avoid flickering, i simply set opacity to very low value - //visual tree is still visible, but user won't notice it. //this.DesignerView.ScrollableContent.Opacity = (state == Visibility.Visible ? 1.0 : 0.01); Mouse.OverrideCursor = (state == Visibility.Visible ? Cursors.Arrow : Cursors.Wait); } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ActivityDesignerLayoutSerializers.cs
- GroupBox.cs
- ClientSponsor.cs
- XPathDocument.cs
- MenuCommands.cs
- CultureSpecificCharacterBufferRange.cs
- SerializationFieldInfo.cs
- DataTemplateKey.cs
- SystemInfo.cs
- RangeBase.cs
- ServiceDescriptionReflector.cs
- DBBindings.cs
- DriveInfo.cs
- PropertyTabAttribute.cs
- LocatorPartList.cs
- PropertyPath.cs
- CommandManager.cs
- TableLayoutColumnStyleCollection.cs
- ShortcutKeysEditor.cs
- MethodBuilderInstantiation.cs
- MaskedTextBox.cs
- SmtpNegotiateAuthenticationModule.cs
- CollaborationHelperFunctions.cs
- DataControlLinkButton.cs
- DataGridViewComboBoxColumn.cs
- ContentTypeSettingClientMessageFormatter.cs
- MessageQueue.cs
- EntityDataSourceColumn.cs
- CharacterBufferReference.cs
- SHA1Managed.cs
- Byte.cs
- HWStack.cs
- CounterSet.cs
- WorkflowInstanceContextProvider.cs
- Vector3DConverter.cs
- Itemizer.cs
- LinqDataSourceUpdateEventArgs.cs
- XhtmlBasicCommandAdapter.cs
- EntityProviderFactory.cs
- XpsFilter.cs
- FrameworkRichTextComposition.cs
- CodeGotoStatement.cs
- DrawItemEvent.cs
- MultiDataTrigger.cs
- RelatedEnd.cs
- Native.cs
- HtmlTernaryTree.cs
- EnumMember.cs
- Logging.cs
- ObjectDataSourceEventArgs.cs
- CaseInsensitiveComparer.cs
- XmlSchemaProviderAttribute.cs
- TemplateControl.cs
- precedingquery.cs
- SiteMapDesignerDataSourceView.cs
- DbConnectionPoolGroup.cs
- SiteMembershipCondition.cs
- XmlWellformedWriter.cs
- EntityTypeEmitter.cs
- DesignBindingConverter.cs
- IsolatedStoragePermission.cs
- MobileControl.cs
- PasswordBox.cs
- WindowsTooltip.cs
- CompositeControl.cs
- TextServicesDisplayAttribute.cs
- InfoCardListRequest.cs
- TwoPhaseCommitProxy.cs
- StringConverter.cs
- MailAddress.cs
- AccessText.cs
- KeyedHashAlgorithm.cs
- InputEventArgs.cs
- RelationshipConstraintValidator.cs
- PolygonHotSpot.cs
- CodeExpressionRuleDeclaration.cs
- PointAnimationClockResource.cs
- XmlReflectionMember.cs
- PointHitTestParameters.cs
- DesignTimeHTMLTextWriter.cs
- ParseNumbers.cs
- SqlDataSourceCustomCommandPanel.cs
- TimelineGroup.cs
- ListBindingConverter.cs
- VarRemapper.cs
- BeginStoryboard.cs
- ContextMenu.cs
- ClientApiGenerator.cs
- ControlDesigner.cs
- ScaleTransform3D.cs
- InputLangChangeRequestEvent.cs
- LoggedException.cs
- InputLanguageManager.cs
- OdbcConnection.cs
- Vector3dCollection.cs
- SafeProcessHandle.cs
- WorkflowClientDeliverMessageWrapper.cs
- HorizontalAlignConverter.cs
- remotingproxy.cs
- LinqToSqlWrapper.cs