|
|
|
@ -6,6 +6,7 @@ using System.Linq; |
|
|
|
using System.Reactive.Linq; |
|
|
|
using System.Reflection; |
|
|
|
using Avalonia.Controls; |
|
|
|
using Avalonia.Reactive; |
|
|
|
|
|
|
|
namespace Avalonia.LogicalTree |
|
|
|
{ |
|
|
|
@ -23,75 +24,122 @@ namespace Avalonia.LogicalTree |
|
|
|
/// <param name="name">The name of the control to find.</param>
|
|
|
|
public static IObservable<ILogical> Track(ILogical relativeTo, string name) |
|
|
|
{ |
|
|
|
var attached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>( |
|
|
|
x => relativeTo.AttachedToLogicalTree += x, |
|
|
|
x => relativeTo.AttachedToLogicalTree -= x) |
|
|
|
.Select(x => ((ILogical)x.Sender).FindNameScope()) |
|
|
|
.StartWith(relativeTo.FindNameScope()); |
|
|
|
|
|
|
|
var detached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>( |
|
|
|
x => relativeTo.DetachedFromLogicalTree += x, |
|
|
|
x => relativeTo.DetachedFromLogicalTree -= x) |
|
|
|
.Select(x => (INameScope)null); |
|
|
|
|
|
|
|
return attached.Merge(detached).Select(nameScope => |
|
|
|
return new ControlTracker(relativeTo, name); |
|
|
|
} |
|
|
|
|
|
|
|
public static IObservable<ILogical> Track(ILogical relativeTo, int ancestorLevel, Type ancestorType = null) |
|
|
|
{ |
|
|
|
return new ControlTracker(relativeTo, ancestorLevel, ancestorType); |
|
|
|
} |
|
|
|
|
|
|
|
private class ControlTracker : LightweightObservableBase<ILogical> |
|
|
|
{ |
|
|
|
private readonly ILogical _relativeTo; |
|
|
|
private readonly string _name; |
|
|
|
private readonly int _ancestorLevel; |
|
|
|
private readonly Type _ancestorType; |
|
|
|
INameScope _nameScope; |
|
|
|
ILogical _value; |
|
|
|
|
|
|
|
public ControlTracker(ILogical relativeTo, string name) |
|
|
|
{ |
|
|
|
_relativeTo = relativeTo; |
|
|
|
_name = name; |
|
|
|
} |
|
|
|
|
|
|
|
public ControlTracker(ILogical relativeTo, int ancestorLevel, Type ancestorType) |
|
|
|
{ |
|
|
|
if (nameScope != null) |
|
|
|
_relativeTo = relativeTo; |
|
|
|
_ancestorLevel = ancestorLevel; |
|
|
|
_ancestorType = ancestorType; |
|
|
|
} |
|
|
|
|
|
|
|
protected override void Initialize() |
|
|
|
{ |
|
|
|
Update(); |
|
|
|
_relativeTo.AttachedToLogicalTree += Attached; |
|
|
|
_relativeTo.DetachedFromLogicalTree += Detached; |
|
|
|
} |
|
|
|
|
|
|
|
protected override void Deinitialize() |
|
|
|
{ |
|
|
|
_relativeTo.AttachedToLogicalTree -= Attached; |
|
|
|
_relativeTo.DetachedFromLogicalTree -= Detached; |
|
|
|
|
|
|
|
if (_nameScope != null) |
|
|
|
{ |
|
|
|
var registered = Observable.FromEventPattern<NameScopeEventArgs>( |
|
|
|
x => nameScope.Registered += x, |
|
|
|
x => nameScope.Registered -= x) |
|
|
|
.Where(x => x.EventArgs.Name == name) |
|
|
|
.Select(x => x.EventArgs.Element) |
|
|
|
.OfType<ILogical>(); |
|
|
|
var unregistered = Observable.FromEventPattern<NameScopeEventArgs>( |
|
|
|
x => nameScope.Unregistered += x, |
|
|
|
x => nameScope.Unregistered -= x) |
|
|
|
.Where(x => x.EventArgs.Name == name) |
|
|
|
.Select(_ => (ILogical)null); |
|
|
|
return registered |
|
|
|
.StartWith(nameScope.Find<ILogical>(name)) |
|
|
|
.Merge(unregistered); |
|
|
|
_nameScope.Registered -= Registered; |
|
|
|
_nameScope.Unregistered -= Unregistered; |
|
|
|
} |
|
|
|
else |
|
|
|
|
|
|
|
_value = null; |
|
|
|
} |
|
|
|
|
|
|
|
protected override void Subscribed(IObserver<ILogical> observer, bool first) |
|
|
|
{ |
|
|
|
observer.OnNext(_value); |
|
|
|
} |
|
|
|
|
|
|
|
private void Attached(object sender, LogicalTreeAttachmentEventArgs e) |
|
|
|
{ |
|
|
|
Update(); |
|
|
|
PublishNext(_value); |
|
|
|
} |
|
|
|
|
|
|
|
private void Detached(object sender, LogicalTreeAttachmentEventArgs e) |
|
|
|
{ |
|
|
|
if (_nameScope != null) |
|
|
|
{ |
|
|
|
return Observable.Return<ILogical>(null); |
|
|
|
_nameScope.Registered -= Registered; |
|
|
|
_nameScope.Unregistered -= Unregistered; |
|
|
|
} |
|
|
|
}).Switch(); |
|
|
|
} |
|
|
|
|
|
|
|
public static IObservable<ILogical> Track(ILogical relativeTo, int ancestorLevel, Type ancestorType = null) |
|
|
|
{ |
|
|
|
return TrackAttachmentToTree(relativeTo).Select(isAttachedToTree => |
|
|
|
_value = null; |
|
|
|
PublishNext(null); |
|
|
|
} |
|
|
|
|
|
|
|
private void Registered(object sender, NameScopeEventArgs e) |
|
|
|
{ |
|
|
|
if (isAttachedToTree) |
|
|
|
if (e.Name == _name && e.Element is ILogical logical) |
|
|
|
{ |
|
|
|
return relativeTo.GetLogicalAncestors() |
|
|
|
.Where(x => ancestorType?.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()) ?? true) |
|
|
|
.ElementAtOrDefault(ancestorLevel); |
|
|
|
_value = logical; |
|
|
|
PublishNext(logical); |
|
|
|
} |
|
|
|
else |
|
|
|
} |
|
|
|
|
|
|
|
private void Unregistered(object sender, NameScopeEventArgs e) |
|
|
|
{ |
|
|
|
if (e.Name == _name) |
|
|
|
{ |
|
|
|
return null; |
|
|
|
_value = null; |
|
|
|
PublishNext(null); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private static IObservable<bool> TrackAttachmentToTree(ILogical relativeTo) |
|
|
|
{ |
|
|
|
var attached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>( |
|
|
|
x => relativeTo.AttachedToLogicalTree += x, |
|
|
|
x => relativeTo.AttachedToLogicalTree -= x) |
|
|
|
.Select(x => true) |
|
|
|
.StartWith(relativeTo.IsAttachedToLogicalTree); |
|
|
|
|
|
|
|
var detached = Observable.FromEventPattern<LogicalTreeAttachmentEventArgs>( |
|
|
|
x => relativeTo.DetachedFromLogicalTree += x, |
|
|
|
x => relativeTo.DetachedFromLogicalTree -= x) |
|
|
|
.Select(x => false); |
|
|
|
|
|
|
|
var attachmentStatus = attached.Merge(detached); |
|
|
|
return attachmentStatus; |
|
|
|
private void Update() |
|
|
|
{ |
|
|
|
if (_name != null) |
|
|
|
{ |
|
|
|
_nameScope = _relativeTo.FindNameScope(); |
|
|
|
|
|
|
|
if (_nameScope != null) |
|
|
|
{ |
|
|
|
_nameScope.Registered += Registered; |
|
|
|
_nameScope.Unregistered += Unregistered; |
|
|
|
_value = _nameScope.Find<ILogical>(_name); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
_value = null; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
_value = _relativeTo.GetLogicalAncestors() |
|
|
|
.Where(x => _ancestorType?.GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()) ?? true) |
|
|
|
.ElementAtOrDefault(_ancestorLevel); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|