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);
- }
- }
- }
- }
|