1 //----------------------------------------------------------------
2 // <copyright company="Microsoft Corporation">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //----------------------------------------------------------------
7 namespace System.Activities.Presentation.View.TreeView
10 using System.Activities.Presentation.Internal.PropertyEditing;
11 using System.Activities.Presentation.Model;
12 using System.Activities.Presentation.Services;
13 using System.Activities.Presentation.Utility;
14 using System.Activities.Presentation.View;
15 using System.Activities.Presentation.View.OutlineView;
16 using System.Activities.Statements;
17 using System.ComponentModel;
18 using System.Diagnostics.CodeAnalysis;
21 using System.Windows.Media;
23 internal sealed class TreeViewItemModelItemViewModel : TreeViewItemViewModel<ModelItem>
25 private ModelProperty promotedProperty;
27 public TreeViewItemModelItemViewModel(TreeViewItemViewModel parent, ModelItem modelItem, bool lazyLoad)
30 this.Value = modelItem;
31 ShowInOutlineViewAttribute attr = ExtensibilityAccessor.GetAttribute<ShowInOutlineViewAttribute>(modelItem);
32 if (attr != null && !string.IsNullOrEmpty(attr.PromotedProperty))
34 // Only consider one level of promoted property.
35 this.promotedProperty = modelItem.Properties[attr.PromotedProperty];
36 if (this.promotedProperty != null)
38 this.VisualValue = this.promotedProperty.Value;
42 // is this what we really want?
43 Fx.Assert(attr.PromotedProperty + " not found on " + modelItem.Name);
44 this.VisualValue = null;
49 this.VisualValue = modelItem;
52 IsHighlighted = Selection.IsSelection(this.VisualValue);
59 this.InternalChildren.Add(TreeViewItemViewModel.DummyNode);
68 public TreeViewItemModelItemViewModel(TreeViewItemViewModel parent, ModelItem modelItem)
69 : this(parent, modelItem, true)
74 /// Set VisualValue will update Icon and binding to designer.
76 public override ModelItem VisualValue
80 return base.VisualValue;
85 if (base.VisualValue != value)
87 // Remove old event chain
88 if (base.VisualValue != null)
90 base.VisualValue.PropertyChanged -= this.VisualValue_PropertyChanged;
93 base.VisualValue = value;
95 if (ModelItemHasDesigner(base.VisualValue))
97 base.VisualValue.PropertyChanged += this.VisualValue_PropertyChanged;
100 this.Icon = this.GetIconByVisualValue();
101 NotifyPropertyChanged("VisualValue");
106 internal bool HasDesigner
110 return ModelItemHasDesigner(this.VisualValue);
114 internal override void LoadChildren()
116 if (this.PerfEventProvider != null)
118 this.PerfEventProvider.DesignerTreeViewLoadChildrenStart();
122 TreeViewItemViewModel.AddModelItem(this, this.Value, null);
123 if (this.PerfEventProvider != null)
125 this.PerfEventProvider.DesignerTreeViewLoadChildrenEnd();
129 internal override void UpdateChildren(ChangeNotificationTracker tracker, EventArgs e)
131 if (this.PerfEventProvider != null)
133 this.PerfEventProvider.DesignerTreeViewUpdateStart();
136 // Update VisualValue when promotedProperty's got changed.
137 if (this.promotedProperty != null && this.promotedProperty == tracker.ParentProperty)
139 this.VisualValue = this.promotedProperty.Value;
142 if (this.Children.Count == 1 && this.Children[0] == DummyNode)
144 // If the node never expanded before, LoadChildren instead of UpdateChildren.
145 // Otherwise, when expanding node, the LoadChildren method won't invoke
146 // Then other tracking properties cannot be setup correctly.
147 this.InternalChildren.Remove(DummyNode);
152 // If requireUpdateChildren = false, the related TreeViewItemModelPropertyViewModel take care of updating child nodes.
153 bool requireUpdateChildren = true;
154 if (e is PropertyChangedEventArgs && this.IsModelPropertyNodeExisted(tracker.ParentProperty))
156 ModelProperty modelProperty = tracker.ParentProperty;
157 if (modelProperty.Value != null)
159 string changedPropertyName = ((PropertyChangedEventArgs)e).PropertyName;
160 bool isPromotedPropertyChanged = TreeViewItemViewModel.IsPromotedProperty(modelProperty.Value, changedPropertyName);
161 if (isPromotedPropertyChanged)
163 if (modelProperty.Value.Properties[changedPropertyName].Value != null)
165 requireUpdateChildren = false;
170 requireUpdateChildren = false;
175 if (requireUpdateChildren)
177 base.UpdateChildren(tracker, e);
179 TreeViewItemViewModel.AddModelProperty(this, this.Value, tracker.ParentProperty, tracker.ParentProperty);
183 if (this.PerfEventProvider != null)
185 this.PerfEventProvider.DesignerTreeViewUpdateEnd();
189 internal override void UpdateState()
192 if (this.Value != null)
194 this.State |= this.UpdateModelItemState(this.Value);
198 internal override int FindInsertionIndex(ChangeNotificationTracker tracker)
200 int insertionIndex = 0;
201 if (tracker != null && (tracker.ChildViewModels == null || tracker.ChildViewModels.Count < 1))
203 foreach (ModelProperty property in this.Value.Properties)
205 if (property != tracker.ParentProperty)
207 // assume this would increament
208 ChangeNotificationTracker propertyTracker = this.GetTracker(property, false);
209 if (propertyTracker != null)
211 insertionIndex = base.FindInsertionIndex(propertyTracker);
216 // we've reach the property and hence the last of the previous property
223 insertionIndex = base.FindInsertionIndex(tracker);
226 return insertionIndex;
229 internal override ChangeNotificationTracker GetTracker(ModelProperty modelProperty, bool createNew)
231 ChangeNotificationTracker tracker = base.GetTracker(modelProperty, createNew);
234 if (this.VisualValue == modelProperty.Parent)
236 // If this TreeViewModelItem use Promopted property and the property belongs to Promoted activity
237 // add the tracked property by default.
238 tracker.Add(this.VisualValue, modelProperty);
242 // If it's an model item, add the tracked property by default
243 tracker.Add(this.Value, modelProperty);
250 protected override void CleanUpCore()
252 if (this.VisualValue != null)
254 this.VisualValue.PropertyChanged -= this.VisualValue_PropertyChanged;
257 this.promotedProperty = null;
262 protected override EditingContext GetEditingContext()
264 if (this.Value != null)
266 return this.Value.GetEditingContext();
270 return base.GetEditingContext();
274 private static bool ModelItemHasDesigner(ModelItem modelItem)
276 if (modelItem != null)
278 DesignerAttribute attribute = WorkflowViewService.GetAttribute<DesignerAttribute>(modelItem.ItemType);
279 if (attribute != null && !string.IsNullOrEmpty(attribute.DesignerTypeName))
288 private static DrawingBrush GetIconFromUnInitializedDesigner(ActivityDesigner designer)
290 DrawingBrush icon = null;
291 if (designer != null)
293 // force the designer to load
294 designer.BeginInit();
296 // An exception will be thrown, if BeginInit is called more than once on
297 // the same activity designer prior to EndInit being called. So we call
298 // EndInit to avoid that, note this will cause an Initialized event.
301 if (designer.Icon == null)
303 // the loading of the default icon depends on Activity.Loaded event.
304 // however the designer might not be loaded unless it is added to the
305 // designer surface. So we load the default icon manually here.
306 icon = designer.GetDefaultIcon();
310 icon = designer.Icon;
317 private void ExpandToNode()
319 TreeViewItemViewModel viewModel = this.Parent;
320 while (viewModel != null)
322 viewModel.IsExpanded = true;
323 viewModel = viewModel.Parent;
327 private DrawingBrush GetIconByVisualValue()
329 if (this.VisualValue != null)
331 DrawingBrush icon = null;
332 Type modelItemType = this.VisualValue.ItemType;
333 if (modelItemType.IsGenericType)
335 // If Type is generic type, whatever T, it should display same icon, so use generic type instead.
336 modelItemType = this.VisualValue.ItemType.GetGenericTypeDefinition();
339 // If the user specifies the attribute, then the Designer would be providing the icon,
340 // bypassing the pipeline of retrieving the icons via reflection and attached properties.
341 ActivityDesignerOptionsAttribute attr = ExtensibilityAccessor.GetAttribute<ActivityDesignerOptionsAttribute>(modelItemType);
342 if (attr != null && attr.OutlineViewIconProvider != null)
344 icon = attr.OutlineViewIconProvider(this.VisualValue);
347 if (icon == null && !TreeViewItemViewModel.IconCache.TryGetValue(modelItemType, out icon))
349 EditingContext context = this.VisualValue.GetEditingContext();
350 ViewService service = context.Services.GetService<ViewService>();
351 WorkflowViewService workflowViewService = service as WorkflowViewService;
352 ActivityDesigner designer = null;
354 // first try to create an detached view element that won't participate in the designer,
355 // if the view service is WorkflowViewService
356 if (workflowViewService != null)
358 designer = workflowViewService.CreateDetachedViewElement(this.VisualValue) as ActivityDesigner;
359 icon = GetIconFromUnInitializedDesigner(designer);
363 // fall back if the view service is not the default implementation
364 // We only need to get the icon from the designer, so we don't need to make sure the view is parented.
365 designer = this.VisualValue.View as ActivityDesigner;
366 if (designer == null && service != null)
368 designer = service.GetView(this.VisualValue) as ActivityDesigner;
371 if (designer != null)
373 if (designer.Icon != null || designer.IsLoaded)
375 icon = designer.Icon;
379 icon = GetIconFromUnInitializedDesigner(designer);
384 // Cache even a null icon since answers found above won't change within this AppDomain
385 TreeViewItemViewModel.IconCache.Add(modelItemType, icon);
396 private bool IsModelPropertyNodeExisted(ModelProperty property)
398 bool isModelPropertyNodeExisted = false;
400 foreach (TreeViewItemViewModel viewModel in Children)
402 TreeViewItemModelPropertyViewModel modelPropertyViewModel = viewModel as TreeViewItemModelPropertyViewModel;
403 if (modelPropertyViewModel != null)
405 if (modelPropertyViewModel.Value == property)
407 isModelPropertyNodeExisted = true;
413 return isModelPropertyNodeExisted;
416 private TreeViewItemState UpdateModelItemState(ModelItem modelItem)
418 TreeViewItemState state = TreeViewItemState.Default;
420 foreach (ModelProperty property in modelItem.Properties)
422 if (ExtensibilityAccessor.GetAttribute<HidePropertyInOutlineViewAttribute>(property) != null)
426 else if (ExtensibilityAccessor.GetAttribute<ShowPropertyInOutlineViewAttribute>(property) != null)
428 if (property.Value != null)
430 state |= TreeViewItemState.HasChildren;
433 // create the property change notification tracker
434 this.GetTracker(property, true);
436 else if (ExtensibilityAccessor.GetAttribute<ShowPropertyInOutlineViewAsSiblingAttribute>(property) != null)
438 // First of all, ShowPropertyInOutlineViewAsSiblingAttribute property's tracker will be setup at the LoadChildren() time.
439 // The reason we cannot do it here is because during the constructor, this.Parent is null.
440 // If all other properties don't flag HasChildren, the current node won't be able to expand.
441 // So we cannot rely on expand operation to invoke LoadChildren() to setup tracker.
442 // TreeViewItemViewModel.AddChild(TreeViewItemViewModel, ModelProperty) will by default invoke LoadChildren() if the node HasSibling.
443 // So even if property's value == null, we should flag it HasSibling, and let LoadChildren invoked by default.
444 state |= TreeViewItemState.HasSibling;
446 else if (ExtensibilityAccessor.GetAttribute<ShowInOutlineViewAttribute>(property) != null)
448 if (property.Value != null)
450 // Only consider one level of PromotedProperty.
451 if (property != this.promotedProperty)
453 state |= TreeViewItemState.HasChildren;
457 // Since the property has been promoted, need to check whether this property has children.
458 // If this promoted property.Value has children, those children should belong to this node.
459 state |= this.UpdateModelItemState(property.Value);
463 this.GetTracker(property, true);
465 else if (property.IsDictionary && property.PropertyType.IsGenericType)
467 // if the values in the dictionary is viewvisible, note this only works with generic dictionary
468 Type[] arguments = property.PropertyType.GetGenericArguments();
469 if (ExtensibilityAccessor.GetAttribute<ShowInOutlineViewAttribute>(arguments[1]) != null)
471 if (property.Value != null)
473 state |= TreeViewItemState.HasChildren;
476 this.GetTracker(property, true);
484 private void VisualValue_PropertyChanged(object sender, ComponentModel.PropertyChangedEventArgs e)
486 if (e.PropertyName.Equals("IsSelection"))
488 this.IsHighlighted = Selection.IsSelection(this.VisualValue);
489 if (this.IsHighlighted)
494 else if (e.PropertyName.Equals("IsPrimarySelection"))
496 if (TreeViewItem == null)
498 // TreeViewItem is not initialized at the first time of SelectionChanged by WorkflowViewElement.OnGotFocusEvent.
502 bool isPrimarySelection = Selection.IsPrimarySelection(this.VisualValue);
503 if (isPrimarySelection)
505 TreeViewItem.Select();
509 TreeViewItem.Unselect();