diff --git a/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs b/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs index c0e0915006..d4d53b091f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs @@ -219,18 +219,9 @@ namespace Avalonia.Markup.Xaml.Data { Contract.Requires(target != null); - if (relativeSource.AncestorType == null) - { - return new ExpressionObserver( - ControlLocator.Track(target, relativeSource.AncestorLevel - 1), - path); - } - else - { - return new ExpressionObserver( - ControlLocator.Track(target, relativeSource.AncestorType, relativeSource.AncestorLevel - 1), - path); - } + return new ExpressionObserver( + ControlLocator.Track(target, relativeSource.AncestorType != null ? relativeSource.Tree : TreeType.Logical, relativeSource.AncestorLevel - 1, relativeSource.AncestorType), + path); } private ExpressionObserver CreateSourceObserver( diff --git a/src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs b/src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs index f77df6853b..825d3b8ba5 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs @@ -87,5 +87,7 @@ namespace Avalonia.Markup.Xaml.Data /// Gets or sets a value that describes the type of relative source lookup. /// public RelativeSourceMode Mode { get; set; } + + public TreeType Tree { get; set; } = TreeType.Visual; } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs index 2896e9803d..58637d26c9 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs @@ -103,7 +103,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions } else if (path.StartsWith("$")) { - var relativeSource = new RelativeSource(); + var relativeSource = new RelativeSource + { + Tree = TreeType.Logical + }; result.RelativeSource = relativeSource; var dot = path.IndexOf('.'); string relativeSourceMode; diff --git a/src/Markup/Avalonia.Markup/ControlLocator.cs b/src/Markup/Avalonia.Markup/ControlLocator.cs index 41851ca288..e533c6922c 100644 --- a/src/Markup/Avalonia.Markup/ControlLocator.cs +++ b/src/Markup/Avalonia.Markup/ControlLocator.cs @@ -11,6 +11,21 @@ using Avalonia.VisualTree; namespace Avalonia.Markup { + /// + /// The type of tree via which to track a control. + /// + public enum TreeType + { + /// + /// The visual tree. + /// + Visual, + /// + /// The logical tree. + /// + Logical, + } + /// /// Locates controls relative to other controls. /// @@ -68,12 +83,44 @@ namespace Avalonia.Markup /// /// The control relative from which the other control should be found. /// - /// The type of the ancestor to find. + /// The tree via which to track the control. /// /// The level of ancestor control to look for. Use 0 for the first ancestor of the /// requested type. /// - public static IObservable Track(IControl relativeTo, Type ancestorType, int ancestorLevel) + /// The type of the ancestor to find. + public static IObservable Track(IControl relativeTo, TreeType tree, int ancestorLevel, Type ancestorType = null) + { + return TrackAttachmentToTree(relativeTo, tree).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; + } + } + else + { + return null; + } + }); + } + + private static IObservable TrackAttachmentToTree(IControl relativeTo, TreeType tree) + { + return tree == TreeType.Visual ? TrackAttachmentToVisualTree(relativeTo) : TrackAttachmentToLogicalTree(relativeTo); + } + + private static IObservable TrackAttachmentToVisualTree(IControl relativeTo) { var attached = Observable.FromEventPattern( x => relativeTo.AttachedToVisualTree += x, @@ -86,32 +133,11 @@ namespace Avalonia.Markup x => relativeTo.DetachedFromVisualTree -= x) .Select(x => false); - return attached.Merge(detached).Select(isAttachedToVisualTree => - { - if (isAttachedToVisualTree) - { - return relativeTo.GetVisualAncestors() - .Where(x => ancestorType.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo())) - .ElementAtOrDefault(ancestorLevel) as IControl; - } - else - { - return null; - } - }); + var attachmentStatus = attached.Merge(detached); + return attachmentStatus; } - /// - /// Tracks a typed logical ancestor control. - /// - /// - /// The control relative from which the other control should be found. - /// - /// - /// The level of ancestor control to look for. Use 0 for the first ancestor of the - /// requested type. - /// - public static IObservable Track(IControl relativeTo, int ancestorLevel) + private static IObservable TrackAttachmentToLogicalTree(IControl relativeTo) { var attached = Observable.FromEventPattern( x => relativeTo.AttachedToLogicalTree += x, @@ -124,18 +150,8 @@ namespace Avalonia.Markup x => relativeTo.DetachedFromLogicalTree += x) .Select(x => false); - return attached.Merge(detached).Select(isAttachedToLogicalTree => - { - if (isAttachedToLogicalTree) - { - return relativeTo.GetLogicalAncestors() - .ElementAtOrDefault(ancestorLevel) as IControl; - } - else - { - return null; - } - }); + var attachmentStatus = attached.Merge(detached); + return attachmentStatus; } } }