| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 | namespace Caliburn.Micro {    using System;    using System.Collections.Generic;    using System.Linq;#if WinRT    using System.ServiceModel;    using Windows.UI.Xaml;    using Windows.UI.Xaml.Controls;    using Windows.UI.Xaml.Controls.Primitives;    using Windows.UI.Xaml.Media;#else    using System.Windows;    using System.Windows.Controls;    using System.Windows.Media;    using System.Windows.Media.Media3D;    using Caliburn.Micro.Core;#endif    /// <summary>    /// Provides methods for searching a given scope for named elements.    /// </summary>    public static class BindingScope {        static readonly List<ChildResolver> ChildResolvers = new List<ChildResolver>();        static readonly Dictionary<Type, Object> NonResolvableChildTypes = new Dictionary<Type, Object>();        static BindingScope()        {            AddChildResolver<ContentControl>(e => new[] { e.Content as DependencyObject });            AddChildResolver<ItemsControl>(e => e.Items.OfType<DependencyObject>().ToArray() );#if !SILVERLIGHT && !WinRT            AddChildResolver<HeaderedContentControl>(e => new[] { e.Header as DependencyObject });            AddChildResolver<HeaderedItemsControl>(e => new[] { e.Header as DependencyObject });#endif#if WinRT            AddChildResolver<SemanticZoom>(e => new[] { e.ZoomedInView as DependencyObject, e.ZoomedOutView as DependencyObject });            AddChildResolver<ListViewBase>(e => new[] { e.Header as DependencyObject });#endif#if WinRT81            AddChildResolver<ListViewBase>(e => new[] { e.Footer as DependencyObject });            AddChildResolver<Hub>(ResolveHub);            AddChildResolver<HubSection>(e => new[] { e.Header as DependencyObject });            AddChildResolver<CommandBar>(ResolveCommandBar);            AddChildResolver<Button>(e => ResolveFlyoutBase(e.Flyout));            AddChildResolver<FrameworkElement>(e => ResolveFlyoutBase(FlyoutBase.GetAttachedFlyout(e)));#endif#if WINDOWS_UWP            AddChildResolver<SplitView>(e => new[] { e.Pane as DependencyObject, e.Content as DependencyObject });#endif        }        /// <summary>        /// Searches through the list of named elements looking for a case-insensitive match.        /// </summary>        /// <param name="elementsToSearch">The named elements to search through.</param>        /// <param name="name">The name to search for.</param>        /// <returns>The named element or null if not found.</returns>        public static FrameworkElement FindName(this IEnumerable<FrameworkElement> elementsToSearch, string name) {#if WinRT            return elementsToSearch.FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));#else            return elementsToSearch.FirstOrDefault(x => x.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));#endif        }        /// <summary>        /// Adds a child resolver.        /// </summary>        /// <param name="filter">The type filter.</param>        /// <param name="resolver">The resolver.</param>        public static ChildResolver AddChildResolver(Func<Type, bool> filter, Func<DependencyObject, IEnumerable<DependencyObject>> resolver) {            if (filter == null) {                throw new ArgumentNullException("filter");            }            if (resolver == null) {                throw new ArgumentNullException("resolver");            }            NonResolvableChildTypes.Clear();                        var childResolver = new ChildResolver(filter, resolver);            ChildResolvers.Add(childResolver);            return childResolver;        }        /// <summary>        /// Adds a child resolver.        /// </summary>        /// <param name="filter">The type filter.</param>        /// <param name="resolver">The resolver.</param>        public static ChildResolver AddChildResolver<T>(Func<T, IEnumerable<DependencyObject>> resolver) where T : DependencyObject        {            if (resolver == null) {                throw new ArgumentNullException("resolver");            }            NonResolvableChildTypes.Clear();            var childResolver = new ChildResolver<T>(resolver);            ChildResolvers.Add(childResolver);            return childResolver;        }        /// <summary>        /// Removes a child resolver.        /// </summary>        /// <param name="resolver">The resolver to remove.</param>        /// <returns>true, when the resolver was (found and) removed.</returns>        public static bool RemoveChildResolver(ChildResolver resolver) {            if (resolver == null) {                throw new ArgumentNullException("resolver");            }            return ChildResolvers.Remove(resolver);        }        /// <summary>        /// Gets all the <see cref="FrameworkElement"/> instances with names in the scope.        /// </summary>        /// <returns>Named <see cref="FrameworkElement"/> instances in the provided scope.</returns>        /// <remarks>Pass in a <see cref="DependencyObject"/> and receive a list of named <see cref="FrameworkElement"/> instances in the same scope.</remarks>        public static Func<DependencyObject, IEnumerable<FrameworkElement>> GetNamedElements = elementInScope => {            var routeHops = FindScopeNamingRoute(elementInScope);            return FindNamedDescendants(routeHops);        };        /// <summary>        /// Gets the parent of the given object in the Visual Tree.        /// </summary>        /// <returns>The parent of the given object in the Visual Tree</returns>        public static Func<DependencyObject, DependencyObject> GetVisualParent = e => VisualTreeHelper.GetParent(e);        /// <summary>        /// Finds a set of named <see cref="FrameworkElement"/> instances in each hop in a <see cref="ScopeNamingRoute"/>.        /// </summary>        /// <remarks>        /// Searches all the elements in the <see cref="ScopeNamingRoute"/> parameter as well as the visual children of         /// each of these elements, the <see cref="ContentControl.Content"/>, the <c>HeaderedContentControl.Header</c>,        /// the <see cref="ItemsControl.Items"/>, or the <c>HeaderedItemsControl.Header</c>, if any are found.        /// </remarks>        public static Func<ScopeNamingRoute, IEnumerable<FrameworkElement>> FindNamedDescendants = routeHops => {            if (routeHops == null) {                throw new ArgumentNullException("routeHops");            }            if (routeHops.Root == null) {                throw new ArgumentException(String.Format("Root is null on the given {0}", typeof (ScopeNamingRoute)));            }            var descendants = new List<FrameworkElement>();            var queue = new Queue<DependencyObject>();            queue.Enqueue(routeHops.Root);            while (queue.Count > 0) {                var current = queue.Dequeue();                var currentElement = current as FrameworkElement;                if (currentElement != null && !string.IsNullOrEmpty(currentElement.Name))                    descendants.Add(currentElement);                if (current is UserControl && !ReferenceEquals(current, routeHops.Root))                    continue;                DependencyObject hopTarget;                if (routeHops.TryGetHop(current, out hopTarget)) {                    queue.Enqueue(hopTarget);                    continue;                }#if NET                var childCount = (current is Visual || current is Visual3D)                    ? VisualTreeHelper.GetChildrenCount(current) : 0;#else                var childCount = (current is UIElement)                    ? VisualTreeHelper.GetChildrenCount(current) : 0;#endif                if (childCount > 0) {                    for (var i = 0; i < childCount; i++) {                        var childDo = VisualTreeHelper.GetChild(current, i);                        queue.Enqueue(childDo);                    }#if WinRT                    var page = current as Page;                    if (page != null) {                        if (page.BottomAppBar != null)                            queue.Enqueue(page.BottomAppBar);                        if (page.TopAppBar != null)                            queue.Enqueue(page.TopAppBar);                    }#endif                }                else {                    var currentType = current.GetType();                    if (!NonResolvableChildTypes.ContainsKey(currentType)) {                        var resolvers = ChildResolvers.Where(r => r.CanResolve(currentType)).ToArray();                        if (!resolvers.Any()) {                            NonResolvableChildTypes[currentType] = null;                        }                        else {                            resolvers                                .SelectMany(r => r.Resolve(current) ?? Enumerable.Empty<DependencyObject>())                                .Where(c => c != null)                                .Apply(queue.Enqueue);                        }                    }                }            }            return descendants;        };#if WinRT81        private static IEnumerable<DependencyObject> ResolveFlyoutBase(FlyoutBase flyoutBase) {            if (flyoutBase == null)                yield break;            var flyout = flyoutBase as Flyout;            if (flyout != null && flyout.Content != null)                yield return flyout.Content;            var menuFlyout = flyoutBase as MenuFlyout;            if (menuFlyout != null && menuFlyout.Items != null) {                foreach (var item in menuFlyout.Items) {                    foreach (var subItem in ResolveMenuFlyoutItems(item)) {                        yield return subItem;                    }                }            }        }        private static IEnumerable<DependencyObject> ResolveMenuFlyoutItems(MenuFlyoutItemBase item) {            yield return item;#if WINDOWS_UWP            var subItem = item as MenuFlyoutSubItem;            if (subItem != null && subItem.Items != null) {                foreach (var subSubItem in subItem.Items) {                    yield return subSubItem;                }            }#endif        }        private static IEnumerable<DependencyObject> ResolveCommandBar(CommandBar commandBar) {            foreach (var child in commandBar.PrimaryCommands.OfType<DependencyObject>()) {                yield return child;            }            foreach (var child in commandBar.SecondaryCommands.OfType<DependencyObject>())            {                yield return child;            }        }        private static IEnumerable<DependencyObject> ResolveHub(Hub hub) {            yield return hub.Header as DependencyObject;            foreach (var section in hub.Sections)                yield return section;        }#endif        /// <summary>        /// Finds a path of dependency objects which traces through visual anscestry until a root which is <see langword="null"/>,        /// a <see cref="UserControl"/>, a <c>Page</c> with a dependency object <c>Page.ContentProperty</c> value,         /// a dependency object with <see cref="View.IsScopeRootProperty"/> set to <see langword="true"/>. <see cref="ContentPresenter"/>        /// and <see cref="ItemsPresenter"/> are included in the resulting <see cref="ScopeNamingRoute"/> in order to track which item        /// in an items control we are scoped to.        /// </summary>        public static Func<DependencyObject, ScopeNamingRoute> FindScopeNamingRoute = elementInScope => {            var root = elementInScope;            var previous = elementInScope;            DependencyObject contentPresenter = null;            var routeHops = new ScopeNamingRoute();            while (true) {                if (root == null) {                    root = previous;                    break;                }                if (root is UserControl)                    break;#if !SILVERLIGHT                if (root is Page) {                    root = ((Page) root).Content as DependencyObject ?? root;                    break;                }#endif                if ((bool) root.GetValue(View.IsScopeRootProperty))                    break;#if WinRT                if (root is AppBar) {                    var frame = Window.Current.Content as Frame;                    var page = (frame != null) ? frame.Content as Page : null;                    if (page != null && (root == page.TopAppBar || root == page.BottomAppBar)) {                        root = page;                        break;                    }                }#endif                if (root is ContentPresenter)                    contentPresenter = root;                else if (root is ItemsPresenter && contentPresenter != null) {                    routeHops.AddHop(root, contentPresenter);                    contentPresenter = null;                }                previous = root;                root = GetVisualParent(previous);            }            routeHops.Root = root;            return routeHops;        };        /// <summary>        /// Maintains a connection in the visual tree of dependency objects in order to record a route through it.        /// </summary>        public class ScopeNamingRoute {            readonly Dictionary<DependencyObject, DependencyObject> path = new Dictionary<DependencyObject, DependencyObject>();            DependencyObject root;            /// <summary>            /// Gets or sets the starting point of the route.            /// </summary>            public DependencyObject Root {                get { return root; }                set {                    if (path.ContainsValue(value)) {                        throw new ArgumentException("Value is a target of some route hop; cannot be a root.");                    }                    root = value;                }            }            /// <summary>            /// Adds a segment to the route.            /// </summary>            /// <param name="from">The source dependency object.</param>            /// <param name="to">The target dependency object.</param>            public void AddHop(DependencyObject from, DependencyObject to) {                if (@from == null) {                    throw new ArgumentNullException("from");                }                if (to == null) {                    throw new ArgumentNullException("to");                }                if (path.Count > 0 &&                    !path.ContainsKey(from) &&                    !path.ContainsKey(to) &&                    !path.ContainsValue(from) &&                    !path.ContainsValue(from)) {                    throw new ArgumentException("Hop pair not part of existing route.");                }                if (path.ContainsKey(to)) {                    throw new ArgumentException("Cycle detected when adding hop.");                }                path[from] = to;            }            /// <summary>            /// Tries to get a target dependency object given a source.            /// </summary>            /// <param name="hopSource">The possible beginning of a route segment (hop).</param>            /// <param name="hopTarget">The target of a route segment (hop).</param>            /// <returns><see langword="true"/> if <paramref name="hopSource"/> had a target recorded; <see langword="false"/> otherwise.</returns>            public bool TryGetHop(DependencyObject hopSource, out DependencyObject hopTarget) {                return path.TryGetValue(hopSource, out hopTarget);            }        }    }}
 |