// <summary>
        // Basic ctor
        // </summary>
        // <param name="property">Property to display</param>
        // <param name="valueDialogTemplate">Template to use</param>
        public PropertyValueDialogControl(PropertyEntry property, DataTemplate valueDialogTemplate) 
        {
            Fx.Assert(property != null, "property parameter is null");
            Fx.Assert(valueDialogTemplate != null, "valueDialogTemplate parameter is null");

            ModelPropertyEntry modelPropertyValue = property as ModelPropertyEntry;
            if (modelPropertyValue != null) 
            {
                _rootTransaction = modelPropertyValue.FirstModelProperty.Value.BeginEdit();
            }

            InitializeComponent();

            // Make sure we use PI-specific resources within this control
            this.Resources.MergedDictionaries.Add(PropertyInspectorResources.GetResources());

            // Hook into an opening of nested dialogs.  PropertyInspector class takes care of this for us.
            // However, we are using Blend's collection editor which doesn't do the same thing, so
            // we need to reproduce that behavior manually.
            PropertyValueDialogHost.AttachOpenDialogHandlers(this);

            // Hook into the standard set of Commands
            _defaultCommandHandler = new PropertyValueEditorCommandHandler(this);


            _OKButton.Click += new RoutedEventHandler(OnOkButtonClicked);
            _cancelButton.Click += new RoutedEventHandler(OnCancelButtonClicked);
            _contentControl.Content = property.PropertyValue;
            _contentControl.ContentTemplate = valueDialogTemplate;

            //Handle the commit and cancel keys within the property inspector, that is hosted in the collection editor
            ValueEditorUtils.SetHandlesCommitKeys(this, true);

        }
 // <summary>
 // Returns either the basic or the advanced bucket based on the IsAdvanced flag
 // set in the PropertyEntry itself
 // </summary>
 // <param name="property">Property to examine</param>
 // <returns>The corresponding basic or advanced bucket</returns>
 internal ObservableCollectionWorkaround<PropertyEntry> GetBucket(PropertyEntry property) 
 {
     if (property == null) 
     {
         throw FxTrace.Exception.ArgumentNull("property");
     }
     return property.IsAdvanced ? _advancedProperties : _basicProperties;
 }
 // <summary>
 // Creates a new PropertySelectionStop wrapping around the specified PropertyEntry
 // </summary>
 // <param name="property">PropertyEntry to wrap around</param>
 public PropertySelectionStop(PropertyEntry property) 
 {
     if (property == null) 
     {
         throw FxTrace.Exception.ArgumentNull("property");
     }
     _property = property;
 }
        // <summary>
        // Stores the current ActiveEditMode for the specified property
        // </summary>
        // <param name="property">Property to key off of</param>
        // <param name="editMode">ActiveEditMode value to store</param>
        public void StoreActiveEditMode(PropertyEntry property, PropertyContainerEditMode editMode) 
        {
            string path = ModelUtilities.GetCachedSubPropertyHierarchyPath(property);

            // We only care about storing the ExtendedPinned state.  Everything
            // else is transitory and shouldn't be persisted.
            //
            if (editMode == PropertyContainerEditMode.ExtendedPinned)
            {
                _expandedPropertyEditors[path] = null;
            }
            else
            {
                _expandedPropertyEditors.Remove(path);
            }
        }
        // <summary>
        // Returns ',' separated property name for sub-properties, going all the way
        // to the root ancestor in the property editing OM.  (ie. you get strings
        // such as 'ContextMenu,IsEnabled' instead of just 'IsEnabled'.
        // </summary>
        // <param name="property">Property to get the name of</param>
        // <returns>',' separated property name for sub-properties</returns>
        public static string GetSubPropertyHierarchyPath(PropertyEntry property)
        {
            if (property == null)
            {
                return null;
            }

            if (property.ParentValue == null)
            {
                return property.PropertyName;
            }

            StringBuilder sb = new StringBuilder();
            do
            {
                if (sb.Length > 0)
                {
                    sb.Insert(0, ',');
                }

                sb.Insert(0, property.PropertyName);
                property = property.ParentValue == null ? null : property.ParentValue.ParentProperty;

            } while (property != null && !(property is ModelPropertyIndexer));

            return sb.ToString();
        }
 // <summary>
 // Creates an instance of SelectionPath to the specified property that
 // this class knows how to interpret.
 // </summary>
 // <param name="property">Property to create a path to</param>
 // <returns>A new instance of SelectionPath to the specified property</returns>
 public SelectionPath ConstructSelectionPath(PropertyEntry property) 
 {
     StringBuilder path = new StringBuilder();
     path.Append(ModelUtilities.GetCachedSubPropertyHierarchyPath(property));
     return new SelectionPath(PathTypeId, path.ToString());
 }
 // Helper that hooks into PropertyChanged events on the given Property
 private void AssociatePropertyEventHandlers(PropertyEntry property)
 {
     if (!_attachedToPropertyEntryEvents)
     {
         property.PropertyChanged += new PropertyChangedEventHandler(OnPropertyPropertyChanged);
         _attachedToPropertyEntryEvents = true;
     }
 }
 // <summary>
 // Adds the given property to the specified property bucket (use
 // ModelCategoryEntry.BasicProperties, ModelCategoryEntry.AdvancedProperties, or
 // ModelCategoryEntry.GetBucket()) sorted using the specified comparer.
 // </summary>
 // <param name="property">Property to add</param>
 // <param name="bucket">Property bucket to populate</param>
 // <param name="comparer">Sort algorithm to use</param>
 // <param name="fireCollectionChangedEvent">If set to true, NotifyCollectionChanged event is fired</param>
 internal void Add(
     PropertyEntry property,
     ObservableCollection<PropertyEntry> bucket,
     IComparer<PropertyEntry> comparer) 
 {
     Add(property, bucket, comparer, true);
 }
        /// <summary>
        /// Creates a PropertyValue.  For host infrastructure.
        /// </summary>
        /// <param name="parentProperty">The PropertyEntry that corresponds to this PropertyValue</param>
        /// <exception cref="ArgumentNullException">When parentProperty is null</exception>
        protected PropertyValue(PropertyEntry parentProperty) {
            if (parentProperty == null)
                throw FxTrace.Exception.ArgumentNull("parentProperty");

            _parentProperty = parentProperty;
        }
 // <summary>
 // Adds the specified property into the specified category.
 // </summary>
 // <param name="property">Property to add</param>
 // <param name="category">Category to populate</param>
 public void AddProperty(PropertyEntry property, ModelCategoryEntry category) 
 {
     category.Add(property, category.BasicProperties, this.PropertyComparer);
 }
        // Blend's CollectionEditor compatibility APIs

        //
        // Since Blend's API uses PropertyEntries instead of ModelProperties, we need
        // to provide methods that consume those instead.  Ideally, with the two code
        // bases merged, we wouldn't need these at all.
        //

        // <summary>
        // Gets the category name of the specified property
        // </summary>
        // <param name="property">Property to examine</param>
        // <returns>Category name the property belongs to.</returns>
        public string GetCategoryName(PropertyEntry property) 
        {
            return property.CategoryName;
        }
 // <summary>
 // Adds the specified property into the specified category.
 // </summary>
 // <param name="property">Property to add</param>
 // <param name="category">Category to populate</param>
 public void AddProperty(PropertyEntry property, ModelCategoryEntry category) 
 {
     category.Add(property as ModelPropertyEntry, category.GetBucket(property), this.PropertyComparer);
 }
 bool IsFlagsProperty(PropertyEntry property)
 {
     return(property != null && property.PropertyType != null && property.PropertyType.IsEnum &&
            ExtensibilityAccessor.GetAttribute <FlagsAttribute>(property.PropertyType) != null);
 }
 bool IsFlagsProperty(PropertyEntry property)
 {
     return property != null && property.PropertyType != null && property.PropertyType.IsEnum &&
            ExtensibilityAccessor.GetAttribute<FlagsAttribute>(property.PropertyType) != null;
 }
 // Helper that unhooks from PropertyChanged events on the given Property
 private void DisassociatePropertyEventHandlers(PropertyEntry property)
 {
     if (_attachedToPropertyEntryEvents)
     {
         property.PropertyChanged -= new PropertyChangedEventHandler(OnPropertyPropertyChanged);
         _attachedToPropertyEntryEvents = false;
     }
 }
 // <summary>
 // Same as GetSubPropertyHierarchyPath(), but it looks up a cached version
 // of this path, if one exists, or calculates one from scratch and caches it
 // if it doesn't.
 // </summary>
 // <param name="property">Property to get the name of</param>
 // <returns>',' separated property name for sub-properties</returns>
 public static string GetCachedSubPropertyHierarchyPath(PropertyEntry property)
 {
     ModelPropertyEntry mpe = property as ModelPropertyEntry;
     return mpe == null ? GetSubPropertyHierarchyPath(property) : mpe.SubPropertyHierarchyPath;
 }
        // <summary>
        // Looks for and returns the PropertyContainer used to display
        // the specified PropertyEntry
        // </summary>
        // <param name="property">Property to look for</param>
        // <returns>Corresponding PropertyContainer if found, null otherwise.</returns>
        internal PropertyContainer FindSubPropertyEntryVisual(PropertyEntry property) 
        {
            if (property == null) 
            {
                return null;
            }

            ItemsControl subPropertyListControl = this.SubPropertyListControl;
            if (subPropertyListControl == null)
            {
                return null;
            }

            return subPropertyListControl.ItemContainerGenerator.ContainerFromItem(property) as PropertyContainer;
        }
        // Blend's CollectionEditor compatibility APIs

        //
        // Since Blend's API uses PropertyEntries instead of ModelProperties, we need
        // to provide methods that consume those instead.  Ideally, with the two code
        // bases merged, we wouldn't need these at all.
        //

        // <summary>
        // Gets the category name of the specified property
        // </summary>
        // <param name="property">Property to examine</param>
        // <returns>Category name the property belongs to.</returns>
        public string GetCategoryName(PropertyEntry property) 
        {
            return System.Activities.Presentation.Internal.Properties.Resources.PropertyCategoryAllProperties;
        }
        // Another Blend work-around - we expose all properties through the OM, not just the
        // Browsable ones.  However, as a result, we need to cull the non-browsable ones from
        // consideration.  Otherwise, empty categories may appear.
        protected override bool DoesPropertyMatchFilter(PropertyFilter filter, PropertyEntry property) 
        {
            property.ApplyFilter(filter);

            bool isBrowsable = true;
            ModelPropertyEntry modelPropertyEntry = property as ModelPropertyEntry;
            if (modelPropertyEntry != null)
            {
                //display given property if it is browsable or
                isBrowsable = modelPropertyEntry.IsBrowsable || 
                    // it may not be browsable, but if there is a category editor associated - display it anyway
                    (this.CategoryEditors != null && this.CategoryEditors.Count != 0);
            }

            return isBrowsable && property.MatchesFilter;
        }
        // ###################################################
        // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - BEGIN
        // ###################################################

        // This method used to be non-virtual, private
        protected virtual void AddProperty(PropertyEntry property, ObservableCollection<PropertyEntry> unconsumedProperties, ObservableCollection<PropertyEntry> referenceOrder, ObservableCollection<CategoryEditor> categoryEditors)
        {

            // ###################################################
            // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - END
            // ###################################################

            bool consumed = false;

            foreach (CategoryEditor categoryEditor in categoryEditors)
            {
                if (categoryEditor.ConsumesProperty(property))
                {
                    consumed = true;
                }
            }
            if (!consumed)
            {
                // We need to insert this property in the correct location.  Reference order is sorted and contains all properties in the unconsumed properties collection.
                Fx.Assert(referenceOrder.Contains(property), "Reference order should contain the property to be added.");
#if DEBUG
                foreach (PropertyEntry unconsumedProperty in unconsumedProperties)
                {
                    Fx.Assert(referenceOrder.Contains(unconsumedProperty), "Reference order should contain all unconsumed properties.");
                }
#endif

                // We'll walk both collections, and advance the insertion index whenever we see an unconsumed property come ahead of the target in the reference order.
                int referenceIndex = 0;
                int insertionIndex = 0;
                while (referenceOrder[referenceIndex] != property && insertionIndex < unconsumedProperties.Count)
                {
                    if (unconsumedProperties[insertionIndex] == referenceOrder[referenceIndex])
                    {
                        insertionIndex++;
                    }
                    referenceIndex++;
                }
                unconsumedProperties.Insert(insertionIndex, property);
            }
        }
        private static DataTemplate GetDialogEditorTemplate(PropertyEntry property)
        {
            if (property == null)
            {
                return null;
            }

            // Does the current PropertyEntry have a dialog editor?
            DialogPropertyValueEditor dialogEditor = property.PropertyValueEditor as DialogPropertyValueEditor;
            if (dialogEditor == null)
            {
                return null;
            }

            return dialogEditor.DialogEditorTemplate;
        }
        //
        // Adds the given property to the specified property bucket (use
        // ModelCategoryEntry.BasicProperties, ModelCategoryEntry.AdvancedProperties, or
        // ModelCategoryEntry.GetBucket()) sorted using the specified comparer.
        //
        private void Add(
            PropertyEntry property,
            ObservableCollection<PropertyEntry> bucket,
            IComparer<PropertyEntry> comparer,
            bool fireCollectionChangedEvent) 
        {

            if (property == null) 
            {
                throw FxTrace.Exception.ArgumentNull("property");
            }
            if (bucket == null) 
            {
                throw FxTrace.Exception.ArgumentNull("bucket");
            }
            if (comparer == null) 
            {
                throw FxTrace.Exception.ArgumentNull("comparer");
            }

            ObservableCollectionWorkaround<PropertyEntry> castBucket = bucket as ObservableCollectionWorkaround<PropertyEntry>;
            int insertionIndex = 0;

            if (castBucket == null) 
            {
                Debug.Fail("Invalid property bucket.  The property sort order will be broken.");
            }
            else 
            {
                insertionIndex = castBucket.BinarySearch(property, comparer);
                if (insertionIndex < 0) 
                {
                    insertionIndex = ~insertionIndex;
                }
            }

            bucket.Insert(insertionIndex, property);

            if (fireCollectionChangedEvent)
            {
                FirePropertiesChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, property));
            }
        }
        // ShowCategoryHeader DP

        // In Cider and unlike in Blend, we expose all properties (browsable and non-browsable) through the
        // property editing object model.  Hence, we need to make sure that the UI representing it (CategoryContainer)
        // does the filtering instead.  This is by design and, for consistency, it should be pushed into Blend as well
        protected override void AddProperty(PropertyEntry property, ObservableCollection<PropertyEntry> unconsumedProperties, ObservableCollection<PropertyEntry> referenceOrder, ObservableCollection<CategoryEditor> categoryEditors) 
        {

            // Is this property browsable?
            ModelPropertyEntry modelPropertyEntry = property as ModelPropertyEntry;
            if (modelPropertyEntry != null && !modelPropertyEntry.IsBrowsable)
            {
                return;
            }

            // Yes, so we can safely add it to the list
            base.AddProperty(property, unconsumedProperties, referenceOrder, categoryEditors);
        }
        // <summary>
        // Attempts to look up the corresponding PropertyContainer to the specified PropertyEntry
        // </summary>
        // <param name="property">Property to look up</param>
        // <param name="pendingGeneration">Set to true if the specified property may have a container
        // but the visual does not exist yet and should be requested later.</param>
        // <returns>Corresponding PropertyContainer, if found, null otherwise</returns>
        public PropertyContainer ContainerFromProperty(PropertyEntry property, out bool pendingGeneration) 
        {
            pendingGeneration = false;
            if (property == null)
            {
                return null;
            }

            if (this.BasicPropertyContainersContainer != null) 
            {
                PropertyContainer propertyContainer = this.BasicPropertyContainersContainer.ItemContainerGenerator.ContainerFromItem(property) as PropertyContainer;
                if (propertyContainer != null)
                {
                    return propertyContainer;
                }
            }

            if (this.AdvancedPropertyContainersContainer != null) 
            {
                PropertyContainer propertyContainer = this.AdvancedPropertyContainersContainer.ItemContainerGenerator.ContainerFromItem(property) as PropertyContainer;
                if (propertyContainer != null)
                {
                    return propertyContainer;
                }
            }

            if (!_UIHooksInitialized)
            {
                pendingGeneration = true;
            }

            return null;
        }
 void UpdateCategoryEditorDataContext(PropertyEntry property, FrameworkElement editor, Container context)
 {
     try
     {
         editor.DataContext = null;
         ModelItem modelItem = null;
         ModelItem propertyParentItem = null;
         EditingContext editingContext = null;
         this.GetPropertyData(property, out modelItem, out propertyParentItem, out editingContext);
         context.Context = editingContext;
         context.WorkflowViewElement = (null == modelItem ? null : modelItem.View);
         context.ModelItem = modelItem;
         editor.DataContext = context;
     }
     catch (Exception err)
     {
         Fx.Assert(false, err.Message);
     }
 }
 // <summary>
 // Gets the last stored ActiveEditMode for the specified property
 // </summary>
 // <param name="property">PropertyEntry to look up</param>
 // <returns>Last stored ActiveEditMode for the specified property</returns>
 public PropertyContainerEditMode GetActiveEditMode(PropertyEntry property) 
 {
     return _expandedPropertyEditors.ContainsKey(ModelUtilities.GetCachedSubPropertyHierarchyPath(property)) ?
         PropertyContainerEditMode.ExtendedPinned :
         PropertyContainerEditMode.Inline;
 }
 void GetPropertyData(PropertyEntry property, out ModelItem activityItem, out ModelItem propertyParentItem, out EditingContext context)
 {
     activityItem = (ModelItem)this.propertyEntryConverter.Convert(property, typeof(ModelItem), false, null);
     propertyParentItem = (ModelItem)this.propertyEntryConverter.Convert(property, typeof(ModelItem), true, null);
     context = activityItem.GetEditingContext();
 }
 /// <summary>
 /// This method is called once for each property in the category to determine which properties
 /// are edited by this CategoryEditor.  When a property is consumed by a CategoryEditor, it does
 /// not show up as a separate row in that category.
 /// </summary>
 /// <param name="propertyEntry">The PropertyEntry to check to see if its edited by this CategoryEditor</param>
 /// <returns>true if this editor edits that property, otherwise false</returns>
 public abstract bool ConsumesProperty(PropertyEntry propertyEntry);
Beispiel #29
0
 /// <summary>
 /// This method is called once for each property in the category to determine which properties
 /// are edited by this CategoryEditor.  When a property is consumed by a CategoryEditor, it does
 /// not show up as a separate row in that category.
 /// </summary>
 /// <param name="propertyEntry">The PropertyEntry to check to see if its edited by this CategoryEditor</param>
 /// <returns>true if this editor edits that property, otherwise false</returns>
 public abstract bool ConsumesProperty(PropertyEntry propertyEntry);