Browse Source

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

pull/1594/head
Jeremy Koritzinsky 8 years ago
parent
commit
c2c581e524
  1. 15
      src/Avalonia.Controls/ControlExtensions.cs
  2. 65
      src/Avalonia.Styling/Controls/ControlLocator.cs
  3. 13
      src/Avalonia.Styling/Controls/NameScopeExtensions.cs
  4. 23
      src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs
  5. 6
      src/Markup/Avalonia.Markup/Avalonia.Markup.csproj

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>

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

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

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" />

Loading…
Cancel
Save