// Tries to figure out what property to select and selects is private void UpdateSelectedProperty(View.Selection selection) { // If we are not loaded, skip any and all selection magic if (!this.IsLoaded) { return; } if (selection != null) { // See what the view would like us to select if we run out of things // we can think of selecting // SelectionPath fallbackSelection = null; if (_propertyToolBar.CurrentViewManager != null) { fallbackSelection = _propertyToolBar.CurrentViewManager.GetDefaultSelectionPath(_categoryList); } // Select the first thing we request that exists, using the following // precedence order: // // * LastSelectionPath // * DefaultProperty // * Whatever the view wants to show (first category, first property, ...) // _categoryList.UpdateSelectedProperty( this.LastSelectionPath, ModelPropertyMerger.GetMergedDefaultProperty(selection.SelectedObjects), fallbackSelection); } if (DesignerPerfEventProvider != null) { DesignerPerfEventProvider.PropertyInspectorUpdatePropertyListEnd(); } }
// This is the work-horse that refreshes the list of properties and categories within a PropertyInspector // window, including refreshing of CategoryEditors, based on the specified selection private void UpdateCategories(View.Selection selection, bool attachedOnly) { // Optimization stolen from Sparkle: // re-rendering the categories is the number one perf issue. Clearing // the databound collection results in massive Avalon code execution, and // then re-adding everything causes another huge shuffle. What is more, // even when changing the selection between different objects, most properties // do not change. Therefore we are going to take the new list of properties // and we are going to merge them into the existing stuff, using an // approach I call Mark, Match, and Cull. // // First we mark all the properties in the current collection. Those which // are still marked at the end will be culled out foreach (ModelCategoryEntry category in _categoryList) { if (attachedOnly) { category.MarkAttachedPropertiesDisassociated(); } else { category.MarkAllPropertiesDisassociated(); } } // Second we try to match each property in the list of properties for the newly selected objects // against something that we already have. If we have a match, then we reset the existing // ModelPropertyEntry and clear the mark // foreach (IEnumerable<ModelProperty> propertySet in ModelPropertyMerger.GetMergedProperties( selection == null ? null : selection.SelectedObjects, selection == null ? 0 : selection.SelectionCount)) { string propertyName = GetPropertyName(propertySet); // Specifically filter out the Name property // PS 40699 - Name is not a special property for WF //if ("Name".Equals(propertyName)) //{ // continue; //} if (attachedOnly && propertyName.IndexOf('.') < 0) { continue; } ModelPropertyEntry wrappedProperty = _propertyToolBar.CurrentViewManager.AddProperty(propertySet, propertyName, _categoryList); // Make sure no valid properties get culled out wrappedProperty.Disassociated = false; } // Third, we walk the properties and categories, and we cull out all of the // marked properties. Empty categories are removed. // for (int i = _categoryList.Count - 1; i >= 0; i--) { ModelCategoryEntry category = (ModelCategoryEntry)_categoryList[i]; category.CullDisassociatedProperties(); if (category.IsEmpty) { _categoryList.RemoveAt(i); } } _categoryList.RefreshFilter(); }
// Helper method that adjusts the visible set of CategoryEditors based on the specified selection private void UpdateCategoryEditors(View.Selection selection) { // Figure out which category editors to show Dictionary<Type, object> newCategoryEditorTypes = _propertyToolBar.CurrentViewManager.GetCategoryEditors( FindCommonType(selection == null ? null : selection.SelectedObjects), _categoryList); // Figure out which CategoryEditors are no longer needed and remove them List<Type> editorTypesToRemove = null; foreach (KeyValuePair<Type, string> item in _activeCategoryEditors) { if (!newCategoryEditorTypes.ContainsKey(item.Key) || !IsCategoryShown(item.Key)) { // New selection does not include this existing category editor // or the category that contains this editor // so remove the editor. if (editorTypesToRemove == null) { editorTypesToRemove = new List<Type>(); } editorTypesToRemove.Add(item.Key); } else { // This category editor already exists, so don't re-add it newCategoryEditorTypes.Remove(item.Key); } } if (editorTypesToRemove != null) { foreach (Type editorTypeToRemove in editorTypesToRemove) { ModelCategoryEntry affectedCategory = _categoryList.FindCategory(_activeCategoryEditors[editorTypeToRemove]) as ModelCategoryEntry; if (affectedCategory != null) { affectedCategory.RemoveCategoryEditor(editorTypeToRemove); } _activeCategoryEditors.Remove(editorTypeToRemove); } } // Figure out which CategoryEditors are now required and add them foreach (Type editorTypeToAdd in newCategoryEditorTypes.Keys) { CategoryEditor editor = (CategoryEditor)ExtensibilityAccessor.SafeCreateInstance(editorTypeToAdd); if (editor == null) { continue; } ModelCategoryEntry affectedCategory = _categoryList.FindCategory(editor.TargetCategory) as ModelCategoryEntry; if (affectedCategory == null) { continue; } affectedCategory.AddCategoryEditor(editor); _activeCategoryEditors[editorTypeToAdd] = editor.TargetCategory; } }
private static bool AreSelectionsEquivalent(View.Selection a, View.Selection b) { if (a == null && b == null) { return true; } if (a == null || b == null) { return false; } if (a.SelectionCount != b.SelectionCount) { return false; } // POSSIBLE OPTIMIZATION: be smarter about same selection in a different order IEnumerator<ModelItem> ea = a.SelectedObjects.GetEnumerator(); IEnumerator<ModelItem> eb = b.SelectedObjects.GetEnumerator(); while (ea.MoveNext() && eb.MoveNext()) { if (!object.Equals(ea.Current, eb.Current)) { return false; } } return true; }
// The user can only specify the name for the selected objects iff exactly one // object is selected. private static bool CanSetSelectionName(View.Selection selection) { return selection != null && selection.SelectionCount == 1; }
// Looks for common parent ModelItem among all the items in the selection private static ModelItem GetCommonParent(View.Selection selection) { if (selection == null || selection.SelectionCount < 1) { return null; } ModelItem parent = null; foreach (ModelItem item in selection.SelectedObjects) { if (parent == null) { parent = item.Parent; } else if (parent != item.Parent) { return null; } } return parent; }
// Removes / adds a PropertyChanged listener from / to the previous / current selection private void UpdateSelectionPropertyChangedEventHooks(View.Selection previousSelection, View.Selection currentSelection) { if (previousSelection != null && previousSelection.PrimarySelection != null) { previousSelection.PrimarySelection.PropertyChanged -= OnSelectedItemPropertyChanged; } if (currentSelection != null && currentSelection.PrimarySelection != null) { currentSelection.PrimarySelection.PropertyChanged += OnSelectedItemPropertyChanged; } }
// Selection Logic // SelectionPathStateContainer // <summary> // Called externally whenever selection changes // </summary> // <param name="selection">New selection</param> public void OnSelectionChanged(View.Selection selection) { _lastNotifiedSelection = selection; RefreshSelection(); }