A cross-platform UI framework for .NET
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.
 
 
 

125 lines
3.8 KiB

// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using System.Reactive.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Perspex.Data;
using Perspex.Markup.Data.Plugins;
namespace Perspex.Markup.Data
{
internal class PropertyAccessorNode : ExpressionNode
{
private IPropertyAccessor _accessor;
private IDisposable _subscription;
public PropertyAccessorNode(string propertyName)
{
PropertyName = propertyName;
}
public string PropertyName { get; }
public Type PropertyType => _accessor?.PropertyType;
public override bool SetValue(object value, BindingPriority priority)
{
if (Next != null)
{
return Next.SetValue(value, priority);
}
else
{
if (_accessor != null)
{
return _accessor.SetValue(value, priority);
}
return false;
}
}
protected override void SubscribeAndUpdate(WeakReference reference)
{
var instance = reference.Target;
if (instance != null && instance != PerspexProperty.UnsetValue)
{
var plugin = ExpressionObserver.PropertyAccessors.FirstOrDefault(x => x.Match(reference));
if (plugin != null)
{
_accessor = plugin.Start(reference, PropertyName, SetCurrentValue, _ => { });
if (_accessor != null)
{
SetCurrentValue(_accessor.Value);
return;
}
}
}
CurrentValue = UnsetReference;
}
protected override void Unsubscribe(object target)
{
_accessor?.Dispose();
_accessor = null;
}
private void SetCurrentValue(object value)
{
var observable = value as IObservable<object>;
var command = value as ICommand;
var task = value as Task;
bool set = false;
// HACK: ReactiveCommand is an IObservable but we want to bind to it, not its value.
// We may need to make this a more general solution.
if (observable != null && command == null)
{
CurrentValue = UnsetReference;
set = true;
_subscription = observable
.ObserveOn(SynchronizationContext.Current)
.Subscribe(x => CurrentValue = new WeakReference(x));
}
else if (task != null)
{
var resultProperty = task.GetType().GetTypeInfo().GetDeclaredProperty("Result");
if (resultProperty != null)
{
if (task.Status == TaskStatus.RanToCompletion)
{
CurrentValue = new WeakReference(resultProperty.GetValue(task));
set = true;
}
else
{
task.ContinueWith(
x => CurrentValue = new WeakReference(resultProperty.GetValue(task)),
TaskScheduler.FromCurrentSynchronizationContext())
.ConfigureAwait(false);
}
}
}
else
{
CurrentValue = new WeakReference(value);
set = true;
}
if (!set)
{
CurrentValue = UnsetReference;
}
}
}
}