File: System.Activities.Presentation\System\Activities\Presentation\Base\Core\Internal\PropertyEditing\CiderCategoryContainer.cs
Project: ndp\cdf\src\NetFx40\Tools\System.Activities.Presentation.csproj (System.Activities.Presentation)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Presentation.Internal.PropertyEditing 
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Text;
    using System.Windows;
    using System.Windows.Automation.Peers;
    using System.Windows.Controls;
 
    using System.Activities.Presentation.PropertyEditing;
    using Blend = System.Activities.Presentation.Internal.PropertyEditing.FromExpression.Framework.PropertyInspector;
 
    using System.Activities.Presentation.Internal.PropertyEditing.Automation;
    using System.Activities.Presentation.Internal.PropertyEditing.Model;
    using System.Activities.Presentation.Internal.PropertyEditing.Selection;
 
    // <summary>
    // HACK: wrapper around Sparkle's CategoryContainer that doesn't come with
    // any initial set of visuals, since we re-brand the control ourselves.
    // Sparkle doesn't plan to change this code before they ship, so we need
    // this work-around.
    // </summary>
    internal class CiderCategoryContainer : Blend.CategoryContainer 
    {
 
        private static readonly DependencyPropertyKey IsEmptyPropertyKey = DependencyProperty.RegisterReadOnly(
            "IsEmpty",
            typeof(bool),
            typeof(CiderCategoryContainer),
            new PropertyMetadata(true, null, new CoerceValueCallback(CiderCategoryContainer.CoerceIsEmpty)));
 
        // <summary>
        // IsEmpty property indicates whether this CategoryContainer contains any editors or unconsumed properties.
        // If the value is true, UI for this container will hide itself
        // </summary>
        public static readonly DependencyProperty IsEmptyProperty = IsEmptyPropertyKey.DependencyProperty;
 
        // <summary>
        // Property indicating whether the header for the category should be rendered or not
        // </summary>
        public static DependencyProperty ShowCategoryHeaderProperty = DependencyProperty.Register(
            "ShowCategoryHeader",
            typeof(bool),
            typeof(CiderCategoryContainer),
            new PropertyMetadata(true));
 
        // Exposed ItemsControls that know how to convert objects into their
        // visual counterparts
        private ItemsControl _basicCategoryEditorsContainer;
        private ItemsControl _advancedCategoryEditorsContainer;
        private ItemsControl _basicPropertyContainersContainer;
        private ItemsControl _advancedPropertyContainersContainer;
        private bool _UIHooksInitialized;
 
        // Keyboard navigation helpers
        private CategorySelectionStop _basicCategorySelectionStop;
        private CategorySelectionStop _advancedCategorySelectionStop;
 
        public CiderCategoryContainer()
            :
            base(false) 
        {
 
            // Note: this logic, along with the IsEmpty DP should be pushed into the base class
            this.BasicCategoryEditors.CollectionChanged += new NotifyCollectionChangedEventHandler(OnContentChanged);
            this.AdvancedCategoryEditors.CollectionChanged += new NotifyCollectionChangedEventHandler(OnContentChanged);
            this.UnconsumedBasicProperties.CollectionChanged += new NotifyCollectionChangedEventHandler(OnContentChanged);
            this.UnconsumedAdvancedProperties.CollectionChanged += new NotifyCollectionChangedEventHandler(OnContentChanged);
        }
 
        // IsEmpty ReadOnly DP
 
        // Events that would have ideally been baked in the base class
        public event EventHandler ExpandedChanged;
        public event EventHandler AdvancedSectionPinnedChanged;
 
        // <summary>
        // Gets the value for IsEmpty DP
        // </summary>
        public bool IsEmpty 
        {
            get { return (bool)this.GetValue(IsEmptyProperty); }
        }
 
        // <summary>
        // Gets or set the value of ShowCategoryHeaderProperty
        // </summary>
        public bool ShowCategoryHeader 
        {
            get { return (bool)this.GetValue(ShowCategoryHeaderProperty); }
            set { this.SetValue(ShowCategoryHeaderProperty, value); }
        }
 
        // <summary>
        // Wrapper around the ExpandedProperty that keyboard navigation understands.  Called from Xaml.
        // </summary>
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        public ISelectionStop BasicCategorySelectionStop 
        {
            get {
                if (_basicCategorySelectionStop == null)
                {
                    _basicCategorySelectionStop = new CategorySelectionStop(this, false);
                }
 
                return _basicCategorySelectionStop;
            }
        }
 
        // <summary>
        // Wrapper around the AdvancedSectionPinnedProperty that keyboard navigation understands
        // </summary>
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        public ISelectionStop AdvancedCategorySelectionStop 
        {
            get {
                if (_advancedCategorySelectionStop == null)
                {
                    _advancedCategorySelectionStop = new CategorySelectionStop(this, true);
                }
 
                return _advancedCategorySelectionStop;
            }
        }
 
        private ItemsControl BasicCategoryEditorsContainer 
        {
            get {
                //
                // If called before the UI itself is rendered, this property
                // will return null, which needs to be handled by the caller
                //
                EnsureUIHooksInitialized();
                return _basicCategoryEditorsContainer;
            }
        }
 
        private ItemsControl AdvancedCategoryEditorsContainer 
        {
            get {
                //
                // If called before the UI itself is rendered, this property
                // will return null, which needs to be handled by the caller
                //
                EnsureUIHooksInitialized();
                return _advancedCategoryEditorsContainer;
            }
        }
 
        private ItemsControl BasicPropertyContainersContainer 
        {
            get {
                //
                // If called before the UI itself is rendered, this property
                // will return null, which needs to be handled by the caller
                //
                EnsureUIHooksInitialized();
                return _basicPropertyContainersContainer;
            }
        }
 
        private ItemsControl AdvancedPropertyContainersContainer 
        {
            get {
                //
                // If called before the UI itself is rendered, this property
                // will return null, which needs to be handled by the caller
                //
                EnsureUIHooksInitialized();
                return _advancedPropertyContainersContainer;
            }
        }
 
        private static object CoerceIsEmpty(DependencyObject obj, object value) 
        {
            CiderCategoryContainer theThis = obj as CiderCategoryContainer;
 
            if (theThis == null)
            {
                return value;
            }
 
            return theThis.BasicCategoryEditors.Count == 0 &&
                theThis.UnconsumedBasicProperties.Count == 0 &&
                theThis.AdvancedCategoryEditors.Count == 0 &&
                theThis.UnconsumedAdvancedProperties.Count == 0;
        }
 
        private void OnContentChanged(object sender, NotifyCollectionChangedEventArgs e) 
        {
            this.CoerceValue(IsEmptyProperty);
        }
 
        // 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;
        }
 
        // <summary>
        // Attempts to look up the corresponding UI representation of the specified CategoryEditor
        // </summary>
        // <param name="editor">Editor to look up</param>
        // <param name="pendingGeneration">Set to true if the specified editor may have a container
        // but the visual does not exist yet and should be requested later.</param>
        // <returns>UI representation of the specified CategoryEditor, if found, null otherwise.</returns>
        public UIElement ContainerFromEditor(CategoryEditor editor, out bool pendingGeneration) 
        {
            pendingGeneration = false;
            if (editor == null)
            {
                return null;
            }
 
            if (this.BasicCategoryEditorsContainer != null) 
            {
                UIElement categoryEditor = this.BasicCategoryEditorsContainer.ItemContainerGenerator.ContainerFromItem(editor) as UIElement;
                if (categoryEditor != null)
                {
                    return categoryEditor;
                }
            }
 
            if (this.AdvancedCategoryEditorsContainer != null) 
            {
                UIElement categoryEditor = this.AdvancedCategoryEditorsContainer.ItemContainerGenerator.ContainerFromItem(editor) as UIElement;
                if (categoryEditor != null)
                {
                    return categoryEditor;
                }
            }
 
            if (!_UIHooksInitialized)
            {
                pendingGeneration = true;
            }
 
            return null;
        }
 
        protected override AutomationPeer OnCreateAutomationPeer() 
        {
            return CategoryContainerAutomationPeer.CreateStandAloneAutomationPeer(this);
        }
 
        // This method fires ExpandedChanged and AdvancedSectionPinnedChanged events.  Ideally we would include
        // these events in Blend.CategoryContainer, but the assembly containing that class has already been locked
        protected override void OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e) 
        {
            base.OnPropertyChanged(e);
 
            if (e.Property == ExpandedProperty &&
                ExpandedChanged != null) 
            {
                ExpandedChanged(this, EventArgs.Empty);
            }
 
            if (e.Property == AdvancedSectionPinnedProperty &&
                AdvancedSectionPinnedChanged != null) 
            {
                AdvancedSectionPinnedChanged(this, EventArgs.Empty);
            }
        }
 
        // Returns true if the hooks should have already been generated
        // false otherwise.
        //
        private bool EnsureUIHooksInitialized() 
        {
            if (_UIHooksInitialized)
            {
                return true;
            }
 
            _basicCategoryEditorsContainer = VisualTreeUtils.GetNamedChild<ItemsControl>(this, "PART_BasicCategoryEditors");
            _advancedCategoryEditorsContainer = VisualTreeUtils.GetNamedChild<ItemsControl>(this, "PART_AdvancedCategoryEditors");
            _basicPropertyContainersContainer = VisualTreeUtils.GetNamedChild<ItemsControl>(this, "PART_BasicPropertyList");
            _advancedPropertyContainersContainer = VisualTreeUtils.GetNamedChild<ItemsControl>(this, "PART_AdvancedPropertyList");
 
            if (_basicCategoryEditorsContainer == null &&
                _advancedCategoryEditorsContainer == null &&
                _basicPropertyContainersContainer == null &&
                _advancedPropertyContainersContainer == null) 
            {
 
                return false;
            }
 
            if (_basicCategoryEditorsContainer == null ||
                _advancedCategoryEditorsContainer == null ||
                _basicPropertyContainersContainer == null ||
                _advancedPropertyContainersContainer == null) 
            {
 
                Debug.Fail("UI for CategoryContainer changed.  Need to update CiderCategoryContainer logic.");
            }
 
            _UIHooksInitialized = true;
            return true;
        }
    }
}