File: System.Activities.Presentation\System\Activities\Presentation\View\ViewUtilities.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.View
{
    using System.Activities.Presentation.Model;
    using System.Activities.Presentation.Services;
    using System.Collections.Generic;
    using System.Runtime;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
 
    internal static class ViewUtilities
    {
        const string ExpandViewStateKey = "IsExpanded";
        internal static bool DoesParentAlwaysExpandChildren(ModelItem modelItem, EditingContext context)
        {
            return IsParentOfType(modelItem, typeof(IExpandChild), context);
        }
 
        internal static bool DoesParentAlwaysCollapseChildren(ModelItem modelItem, EditingContext context)
        {
            bool parentAlwaysCollapsesChild = false;
            Type parentDesignerType = GetParentDesignerType(modelItem, context);
            if (typeof(WorkflowViewElement).IsAssignableFrom(parentDesignerType))
            {
                ActivityDesignerOptionsAttribute options = WorkflowViewService.GetAttribute<ActivityDesignerOptionsAttribute>(parentDesignerType);
                parentAlwaysCollapsesChild = (options != null && options.AlwaysCollapseChildren);
            }
            return parentAlwaysCollapsesChild;
        }
 
        // Determines whether a particular ModelItem's view will be visible for a given breadcrumb root. 
        //It depends on whether the intermediate designers are expanded or collapsed.
        internal static bool IsViewVisible(ModelItem child, ModelItem root, EditingContext context)
        {
            if (child == root)
            {
                return !IsDefaultDesigner(context, root);
            }
 
            if (child.Parent == null)
            {
                return false;
            }
 
            WorkflowViewService viewService = GetViewService(context);
            ModelItem parent = ModelUtilities.ReverseFindFirst(child.Parent, (ModelItem current) =>
            {
                return object.Equals(current, root) ||
                    HasView(current, viewService, false) &&
                    (!IsViewExpanded(current, context) || IsDefaultDesigner(context, current));
            });
 
            return object.Equals(parent, root) && !IsDefaultDesigner(context, root);
        }
 
        private static bool IsDefaultDesigner(EditingContext context, ModelItem item)
        {
            WorkflowViewService viewService = GetViewService(context);
            Type viewType = viewService.GetDesignerType(item.ItemType);
            return viewType == typeof(ActivityDesigner);
        }
 
        private static bool HasView(ModelItem modelItem, WorkflowViewService viewService, bool allowDrillIn)
        {
            ActivityDesignerOptionsAttribute options = WorkflowViewService.GetAttribute<ActivityDesignerOptionsAttribute>(modelItem.ItemType);
            Type viewType = viewService.GetDesignerType(modelItem.ItemType);
            return typeof(WorkflowViewElement).IsAssignableFrom(viewType) && (!allowDrillIn || options == null || options.AllowDrillIn);
        }
 
        // Get the first parent ModelItem that has a view
        internal static ModelItem GetParentModelItemWithView(ModelItem modelItem, EditingContext context, bool allowDrillIn)
        {
            if (modelItem == null || modelItem.Parent == null)
            {
                return null;
            }
 
            WorkflowViewService viewService = GetViewService(context);
 
            return ModelUtilities.ReverseFindFirst(modelItem.Parent, (ModelItem current) =>
                {
                    return HasView(current, viewService, allowDrillIn);
                });
        }
 
        // Determine whether the view of a ModelItem is expanded without querying the view itself - the view may have not been constructed.
        internal static bool IsViewExpanded(ModelItem modelItem, EditingContext context)
        {
            if (modelItem == null)
            {
                return false;
            }
 
            bool isDesignerExpanded = true;
            bool isDesignerPinned = false;
            object isExpandedViewState = GetViewStateService(context).RetrieveViewState(modelItem, ExpandViewStateKey);
            object isPinnedViewState = GetViewStateService(context).RetrieveViewState(modelItem, WorkflowViewElement.PinnedViewStateKey);
            if (isExpandedViewState != null)
            {
                isDesignerExpanded = (bool)isExpandedViewState;
            }
            if (isPinnedViewState != null)
            {
                isDesignerPinned = (bool)isPinnedViewState;
            }
 
            DesignerView designerView = context.Services.GetService<DesignerView>();
             
            return ShouldShowExpanded(IsBreadcrumbRoot(modelItem, context), DoesParentAlwaysExpandChildren(modelItem, context),
                DoesParentAlwaysCollapseChildren(modelItem, context), isDesignerExpanded, designerView.ShouldExpandAll, designerView.ShouldCollapseAll, isDesignerPinned);
        }
 
        internal static bool IsBreadcrumbRoot(ModelItem modelItem, EditingContext context)
        {
            DesignerView designerView = context.Services.GetService<DesignerView>();
            return modelItem != null && modelItem.View != null && modelItem.View.Equals(designerView.RootDesigner);
        }
 
        internal static bool ShouldShowExpanded(
            bool isRootDesigner,
            bool parentAlwaysExpandChildren,
            bool parentAlwaysCollapseChildren,
            bool expandState,
            bool expandAll,
            bool collapseAll,
            bool pinState)
        {
            //ShowExpanded based on ExpandAll, CollapseAll, PinState, ExpandState
            bool showExpanded = ShouldShowExpanded(expandState, expandAll, collapseAll, pinState);
 
            //return value based on the position of the element in the workflow tree.
            return (isRootDesigner || parentAlwaysExpandChildren || (!parentAlwaysCollapseChildren && showExpanded));
        }
 
        internal static bool ShouldShowExpanded(bool isExpanded, bool shouldExpandAll, bool shouldCollapseAll, bool isPinned)
        {
            if (isPinned)
            {
                return isExpanded;
            }
            else
            {
                return !shouldCollapseAll && (shouldExpandAll || isExpanded);
            }
        }
 
        static WorkflowViewService GetViewService(EditingContext context)
        {
            return context.Services.GetService<ViewService>() as WorkflowViewService;
        }
 
        static ViewStateService GetViewStateService(EditingContext context)
        {
            return context.Services.GetService<ViewStateService>();
        }
 
        //Checks to see if the immediate parent WorkflowViewElement is of type "parentType".
        static bool IsParentOfType(ModelItem modelItem, Type parentType, EditingContext context)
        {
            Type parentDesignerType = GetParentDesignerType(modelItem, context);
            return parentType.IsAssignableFrom(parentDesignerType);
        }
 
        static Type GetParentDesignerType(ModelItem modelItem, EditingContext context)
        {
            ModelItem parent = GetParentModelItemWithView(modelItem, context, false);
            if (parent != null)
            {
                return GetViewService(context).GetDesignerType(parent.ItemType);
            }
            return null;
        }
 
        internal static void MeasureView(WorkflowViewElement view, bool measureAsCollapsed)
        {
            bool expandState = view.ExpandState;
            bool pinState = view.PinState;
         
            if (measureAsCollapsed)
            {
                view.ForceCollapse();
            }
 
            view.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
            view.UpdateLayout();
 
            if (view.ExpandState != expandState)
            {
                view.ExpandState = expandState;
            }
            if (view.PinState != pinState)
            {
                view.PinState = pinState;
            }
        }
 
        // the job of this method is to construct a DisplayName for ActivityBuilder
        // This name will be shown in breadcrumb bar.
        // if ActivityBuilder.Name = "workflowconsoleApp.Sequence1"
        // we want DisplayName = "Sequence1"
        internal static string GetActivityBuilderDisplayName(ModelItem modelItem)
        {
            Fx.Assert(modelItem != null, "modelItem != null");
            ModelItem nameModelItem = modelItem.Properties["Name"].Value;
            string name = (nameModelItem == null) ? null : (string)nameModelItem.GetCurrentValue();
 
            if (string.IsNullOrEmpty(name))
            {
                return string.Empty;
            }
 
            string displayName = string.Empty;
            int indexOfDot = name.LastIndexOf('.');
            if (indexOfDot > -1)
            {
                // if make sure there at least one character after .
                if (indexOfDot < name.Length - 1)
                {
                    displayName = name.Substring(indexOfDot + 1);
                }
            }
            else
            {
                displayName = name;
            }
 
            return displayName;
        }
 
        internal static GeneralTransform GetTransformToRoot(Visual visual)
        {
            Visual rootVisual = GetRootVisual(visual);
 
            return (rootVisual != null) ? visual.TransformToAncestor(rootVisual) : null;
        }
 
        private static Visual GetRootVisual(Visual visual)
        {
            Fx.Assert(visual != null, "visual should not be null");
 
            Visual root = null;
 
            PresentationSource source = PresentationSource.FromDependencyObject(visual);
            if (source != null)
            {
                root = source.RootVisual;
            }
 
            // PresentationSource will be null if the element is not in a window
            // Window w = new Window();
            // Button b = new Button();
            // w.Show();
            // w.Content = b;
            // PresentationSource.FromDependencyObject(b) will return an instance
            // w.Content = null;
            // PresentationSource.FromDependencyObject(b) will return null
            // The reason of tree walk is to make some effort to support 
            // the scenario where user wants to capture a visual that is 
            // not in a window. 
            if (root == null)
            {
                for (DependencyObject current = visual;
                    current != null; current = VisualTreeHelper.GetParent(current))
                {
                    // Maybe Visual is not enought in some case, but I don't get a sample 
                    // till now. If it happens, add LogicalTreeHelper.GetParent() to the 
                    // parent getting chain.
                    Visual currentVisual = current as Visual;
                    if (currentVisual != null)
                    {
                        root = currentVisual;
                    }
                }
            }
 
            return root;
        }
    }
}