Browse Source

Refactor away dependencies between Avalonia.Markup and Avalonia.Controls. Now Avalonia.Markup only depends on Avalonia.Base.

Jeremy Koritzinsky 7 years ago
parent
commit
c2c581e524

+ 0 - 15
src/Avalonia.Controls/ControlExtensions.cs

@@ -66,21 +66,6 @@ namespace Avalonia.Controls
             return nameScope.Find<T>(name);
         }
 
-        /// <summary>
-        /// Finds the name scope for a control by searching up the logical tree.
-        /// </summary>
-        /// <param name="control">The control.</param>
-        /// <returns>The control's name scope, or null if not found.</returns>
-        public static INameScope FindNameScope(this IControl control)
-        {
-            Contract.Requires<ArgumentNullException>(control != null);
-
-            return control.GetSelfAndLogicalAncestors()
-                .OfType<Control>()
-                .Select(x => (x as INameScope) ?? NameScope.GetNameScope(x))
-                .FirstOrDefault(x => x != null);
-        }
-
         /// <summary>
         /// Adds or removes a pseudoclass depending on a boolean value.
         /// </summary>

+ 28 - 37
src/Markup/Avalonia.Markup/ControlLocator.cs → src/Avalonia.Styling/Controls/ControlLocator.cs

@@ -9,7 +9,7 @@ using Avalonia.Controls;
 using Avalonia.LogicalTree;
 using Avalonia.VisualTree;
 
-namespace Avalonia.Markup
+namespace Avalonia.Controls
 {
     /// <summary>
     /// The type of tree via which to track a control.
@@ -38,12 +38,12 @@ namespace Avalonia.Markup
         /// The control relative from which the other control should be found.
         /// </param>
         /// <param name="name">The name of the control to find.</param>
-        public static IObservable<IControl> Track(IControl relativeTo, string name)
+        public static IObservable<ILogical> Track(ILogical relativeTo, string name)
         {
             var attached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>(
                 x => relativeTo.AttachedToLogicalTree += x,
                 x => relativeTo.AttachedToLogicalTree -= x)
-                .Select(x => ((IControl)x.Sender).FindNameScope())
+                .Select(x => ((ILogical)x.Sender).FindNameScope())
                 .StartWith(relativeTo.FindNameScope());
 
             var detached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>(
@@ -60,53 +60,32 @@ namespace Avalonia.Markup
                         x => nameScope.Registered -= x)
                         .Where(x => x.EventArgs.Name == name)
                         .Select(x => x.EventArgs.Element)
-                        .OfType<IControl>();
+                        .OfType<ILogical>();
                     var unregistered = Observable.FromEventPattern<NameScopeEventArgs>(
                         x => nameScope.Unregistered += x,
                         x => nameScope.Unregistered -= x)
                         .Where(x => x.EventArgs.Name == name)
-                        .Select(_ => (IControl)null);
+                        .Select(_ => (ILogical)null);
                     return registered
-                        .StartWith(nameScope.Find<IControl>(name))
+                        .StartWith(nameScope.Find<ILogical>(name))
                         .Merge(unregistered);
                 }
                 else
                 {
-                    return Observable.Return<IControl>(null);
+                    return Observable.Return<ILogical>(null);
                 }
             }).Switch();
         }
 
-        /// <summary>
-        /// Tracks a typed visual ancestor control.
-        /// </summary>
-        /// <param name="relativeTo">
-        /// The control relative from which the other control should be found.
-        /// </param>
-        /// <param name="tree">The tree via which to track the control.</param>
-        /// <param name="ancestorLevel">
-        /// The level of ancestor control to look for. Use 0 for the first ancestor of the
-        /// requested type.
-        /// </param>
-        /// <param name="ancestorType">The type of the ancestor to find.</param>
-        public static IObservable<IControl> Track(IControl relativeTo, TreeType tree, int ancestorLevel, Type ancestorType = null)
+        public static IObservable<ILogical> Track(ILogical relativeTo, int ancestorLevel, Type ancestorType = null)
         {
-            return TrackAttachmentToTree(relativeTo, tree).Select(isAttachedToTree =>
+            return TrackAttachmentToTree(relativeTo).Select(isAttachedToTree =>
             {
                 if (isAttachedToTree)
                 {
-                    if (tree == TreeType.Visual)
-                    {
-                        return relativeTo.GetVisualAncestors()
-                            .Where(x => ancestorType?.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()) ?? true)
-                            .ElementAtOrDefault(ancestorLevel) as IControl; 
-                    }
-                    else
-                    {
-                        return relativeTo.GetLogicalAncestors()
-                            .Where(x => ancestorType?.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()) ?? true)
-                            .ElementAtOrDefault(ancestorLevel) as IControl;
-                    }
+                    return relativeTo.GetLogicalAncestors()
+                        .Where(x => ancestorType?.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()) ?? true)
+                        .ElementAtOrDefault(ancestorLevel);
                 }
                 else
                 {
@@ -115,12 +94,24 @@ namespace Avalonia.Markup
             });
         }
 
-        private static IObservable<bool> TrackAttachmentToTree(IControl relativeTo, TreeType tree)
+        public static IObservable<IVisual> Track(IVisual relativeTo, int ancestorLevel, Type ancestorType = null)
         {
-            return tree == TreeType.Visual ? TrackAttachmentToVisualTree(relativeTo) : TrackAttachmentToLogicalTree(relativeTo);
+            return TrackAttachmentToTree(relativeTo).Select(isAttachedToTree =>
+            {
+                if (isAttachedToTree)
+                {
+                    return relativeTo.GetVisualAncestors()
+                        .Where(x => ancestorType?.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()) ?? true)
+                        .ElementAtOrDefault(ancestorLevel);
+                }
+                else
+                {
+                    return null;
+                }
+            });
         }
 
-        private static IObservable<bool> TrackAttachmentToVisualTree(IControl relativeTo)
+        private static IObservable<bool> TrackAttachmentToTree(IVisual relativeTo)
         {
             var attached = Observable.FromEventPattern<VisualTreeAttachmentEventArgs>(
                 x => relativeTo.AttachedToVisualTree += x,
@@ -137,7 +128,7 @@ namespace Avalonia.Markup
             return attachmentStatus;
         }
 
-        private static IObservable<bool> TrackAttachmentToLogicalTree(IControl relativeTo)
+        private static IObservable<bool> TrackAttachmentToTree(ILogical relativeTo)
         {
             var attached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>(
                 x => relativeTo.AttachedToLogicalTree += x,

+ 13 - 0
src/Avalonia.Styling/Controls/NameScopeExtensions.cs

@@ -3,6 +3,9 @@
 
 using System;
 using System.Collections.Generic;
+using System.Linq;
+using Avalonia.LogicalTree;
+using Avalonia.VisualTree;
 
 namespace Avalonia.Controls
 {
@@ -64,5 +67,15 @@ namespace Avalonia.Controls
 
             return (T)result;
         }
+
+        public static INameScope FindNameScope(this ILogical control)
+        {
+            Contract.Requires<ArgumentNullException>(control != null);
+
+            return control.GetSelfAndLogicalAncestors()
+                .OfType<Visual>()
+                .Select(x => (x as INameScope) ?? NameScope.GetNameScope(x))
+                .FirstOrDefault(x => x != null);
+        }
     }
 }

+ 22 - 1
src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs

@@ -8,6 +8,7 @@ using System.Reactive.Linq;
 using System.Reflection;
 using Avalonia.Controls;
 using Avalonia.Data;
+using Avalonia.LogicalTree;
 using Avalonia.Markup.Data;
 using Avalonia.VisualTree;
 
@@ -234,8 +235,28 @@ namespace Avalonia.Markup.Xaml.Data
         {
             Contract.Requires<ArgumentNullException>(target != null);
 
+            IObservable<object> controlLocator;
+
+            switch (relativeSource.Tree)
+            {
+                case TreeType.Logical:
+                    controlLocator = ControlLocator.Track(
+                        (ILogical)target,
+                        relativeSource.AncestorLevel - 1,
+                        relativeSource.AncestorType);
+                    break;
+                case TreeType.Visual:
+                    controlLocator = ControlLocator.Track(
+                        (IVisual)target,
+                        relativeSource.AncestorLevel - 1,
+                        relativeSource.AncestorType);
+                    break;
+                default:
+                    throw new InvalidOperationException("Invalid tree to traverse.");
+            }
+
             return new ExpressionObserver(
-                ControlLocator.Track(target, relativeSource.Tree, relativeSource.AncestorLevel - 1, relativeSource.AncestorType),
+                controlLocator,
                 path,
                 enableDataValidation);
         }

+ 0 - 6
src/Markup/Avalonia.Markup/Avalonia.Markup.csproj

@@ -4,12 +4,6 @@
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
-    <ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj" />
-    <ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj" />
-    <ProjectReference Include="..\..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
-    <ProjectReference Include="..\..\Avalonia.Layout\Avalonia.Layout.csproj" />
-    <ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
-    <ProjectReference Include="..\..\Avalonia.Styling\Avalonia.Styling.csproj" />
   </ItemGroup>
   <Import Project="..\..\..\build\Markup.props" />
   <Import Project="..\..\..\build\Rx.props" />