csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
97 lines
3.2 KiB
97 lines
3.2 KiB
namespace Perspex.Xaml.DataBinding.ChangeTracking
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Reactive.Linq;
|
|
using System.Reflection;
|
|
using Glass;
|
|
|
|
public class ObservablePropertyBranch
|
|
{
|
|
private readonly object root;
|
|
private readonly PropertyPath propertyPath;
|
|
private PropertyMountPoint mountPoint;
|
|
|
|
public ObservablePropertyBranch(object root, PropertyPath propertyPath)
|
|
{
|
|
Guard.ThrowIfNull(root, nameof(root));
|
|
Guard.ThrowIfNull(propertyPath, nameof(propertyPath));
|
|
|
|
this.root = root;
|
|
this.propertyPath = propertyPath;
|
|
mountPoint = new PropertyMountPoint(root, propertyPath);
|
|
var subscriptions = GetInpcNodes();
|
|
Changed = CreateObservableFromNodes(subscriptions);
|
|
}
|
|
|
|
private IObservable<object> CreateObservableFromNodes(IEnumerable<InpcNode> subscriptions)
|
|
{
|
|
return subscriptions.Select(
|
|
subscription => Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
|
|
ev => subscription.Parent.PropertyChanged += ev,
|
|
handler => subscription.Parent.PropertyChanged -= handler)
|
|
.Do(_ => mountPoint = new PropertyMountPoint(root, propertyPath))
|
|
.Where(pattern => pattern.EventArgs.PropertyName == subscription.PropertyName))
|
|
.Merge();
|
|
}
|
|
|
|
private IEnumerable<InpcNode> GetInpcNodes()
|
|
{
|
|
return GetSubscriptionsRecursive(root, propertyPath, 0);
|
|
}
|
|
|
|
private IEnumerable<InpcNode> GetSubscriptionsRecursive(object current, PropertyPath propertyPath, int i)
|
|
{
|
|
var subscriptions = new List<InpcNode>();
|
|
var inpc = current as INotifyPropertyChanged;
|
|
|
|
if (inpc == null)
|
|
{
|
|
return subscriptions;
|
|
}
|
|
|
|
var nextPropertyName = propertyPath.Chunks[i];
|
|
subscriptions.Add(new InpcNode(inpc, nextPropertyName));
|
|
|
|
if (i < this.propertyPath.Chunks.Length)
|
|
{
|
|
var currentObjectTypeInfo = current.GetType().GetTypeInfo();
|
|
var nextProperty = currentObjectTypeInfo.GetDeclaredProperty(nextPropertyName);
|
|
var nextInstance = nextProperty.GetValue(current);
|
|
subscriptions.AddRange(GetSubscriptionsRecursive(nextInstance, propertyPath, i + 1));
|
|
}
|
|
|
|
return subscriptions;
|
|
}
|
|
|
|
public IObservable<object> Changed { get; }
|
|
|
|
public object Value
|
|
{
|
|
get
|
|
{
|
|
return mountPoint.Value;
|
|
}
|
|
set
|
|
{
|
|
mountPoint.Value = value;
|
|
}
|
|
}
|
|
|
|
public Type Type => mountPoint.ProperyType;
|
|
|
|
private class InpcNode
|
|
{
|
|
public InpcNode(INotifyPropertyChanged parent, string propertyName)
|
|
{
|
|
Parent = parent;
|
|
PropertyName = propertyName;
|
|
}
|
|
|
|
public INotifyPropertyChanged Parent { get; }
|
|
public string PropertyName { get; }
|
|
}
|
|
}
|
|
}
|