Browse Source
Instead of an ISubject<object> as this was wasteful when a OneTime or OneWay binding was required.pull/464/head
17 changed files with 308 additions and 157 deletions
@ -0,0 +1,71 @@ |
|||||
|
// 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.Disposables; |
||||
|
using System.Reactive.Linq; |
||||
|
|
||||
|
namespace Perspex.Data |
||||
|
{ |
||||
|
public static class BindingOperations |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Applies an <see cref="InstancedBinding"/> a property on an <see cref="IPerspexObject"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="target">The target object.</param>
|
||||
|
/// <param name="property">The property to bind.</param>
|
||||
|
/// <param name="binding">The instanced binding.</param>
|
||||
|
/// <param name="anchor">
|
||||
|
/// An optional anchor from which to locate required context. When binding to objects that
|
||||
|
/// are not in the logical tree, certain types of binding need an anchor into the tree in
|
||||
|
/// order to locate named controls or resources. The <paramref name="anchor"/> parameter
|
||||
|
/// can be used to provice this context.
|
||||
|
/// </param>
|
||||
|
/// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
|
||||
|
public static IDisposable Apply( |
||||
|
IPerspexObject target, |
||||
|
PerspexProperty property, |
||||
|
InstancedBinding binding, |
||||
|
object anchor) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(target != null); |
||||
|
Contract.Requires<ArgumentNullException>(property != null); |
||||
|
Contract.Requires<ArgumentNullException>(binding != null); |
||||
|
|
||||
|
var mode = binding.Mode; |
||||
|
|
||||
|
if (mode == BindingMode.Default) |
||||
|
{ |
||||
|
mode = property.GetMetadata(target.GetType()).DefaultBindingMode; |
||||
|
} |
||||
|
|
||||
|
switch (mode) |
||||
|
{ |
||||
|
case BindingMode.Default: |
||||
|
case BindingMode.OneWay: |
||||
|
return target.Bind(property, binding.Observable ?? binding.Subject, binding.Priority); |
||||
|
case BindingMode.TwoWay: |
||||
|
return new CompositeDisposable( |
||||
|
target.Bind(property, binding.Subject, binding.Priority), |
||||
|
target.GetObservable(property).Subscribe(binding.Subject)); |
||||
|
case BindingMode.OneTime: |
||||
|
var source = binding.Subject ?? binding.Observable; |
||||
|
|
||||
|
if (source != null) |
||||
|
{ |
||||
|
return source.Take(1).Subscribe(x => target.SetValue(property, x, binding.Priority)); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
target.SetValue(property, binding.Value, binding.Priority); |
||||
|
return Disposable.Empty; |
||||
|
} |
||||
|
case BindingMode.OneWayToSource: |
||||
|
return target.GetObservable(property).Subscribe(binding.Subject); |
||||
|
default: |
||||
|
throw new ArgumentException("Invalid binding mode."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,108 @@ |
|||||
|
// 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.Reactive.Subjects; |
||||
|
|
||||
|
namespace Perspex.Data |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Holds the result of calling <see cref="IBinding.Initiate"/>.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Whereas an <see cref="IBinding"/> holds a description of a binding such as "Bind to the X
|
||||
|
/// property on a control's DataContext"; this class represents a binding that has been
|
||||
|
/// *instanced* by calling <see cref="IBinding.Initiate(IPerspexObject, PerspexProperty, object)"/>
|
||||
|
/// on a target object.
|
||||
|
///
|
||||
|
/// When a binding is initiated, it can return one of 3 possible sources for the binding:
|
||||
|
/// - An <see cref="ISubject{Object}"/> which can be used for any type of binding.
|
||||
|
/// - An <see cref="IObservable{Object}"/> which can be used for all types of bindings except
|
||||
|
/// <see cref="BindingMode.OneWayToSource"/> and <see cref="BindingMode.TwoWay"/>.
|
||||
|
/// - A plain object, which can only represent a <see cref="BindingMode.OneTime"/> binding.
|
||||
|
/// </remarks>
|
||||
|
public class InstancedBinding |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="InstancedBinding"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">
|
||||
|
/// The value used for the <see cref="BindingMode.OneTime"/> binding.
|
||||
|
/// </param>
|
||||
|
/// <param name="priority">The binding priority.</param>
|
||||
|
public InstancedBinding(object value, BindingPriority priority = BindingPriority.LocalValue) |
||||
|
{ |
||||
|
Mode = BindingMode.OneTime; |
||||
|
Priority = priority; |
||||
|
Value = value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="InstancedBinding"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="observable">The observable for a one-way binding.</param>
|
||||
|
/// <param name="mode">The binding mode.</param>
|
||||
|
/// <param name="priority">The binding priority.</param>
|
||||
|
public InstancedBinding( |
||||
|
IObservable<object> observable, |
||||
|
BindingMode mode = BindingMode.OneWay, |
||||
|
BindingPriority priority = BindingPriority.LocalValue) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(observable != null); |
||||
|
|
||||
|
if (mode == BindingMode.OneWayToSource || mode == BindingMode.TwoWay) |
||||
|
{ |
||||
|
throw new ArgumentException( |
||||
|
"Invalid BindingResult mode: OneWayToSource and TwoWay bindings" + |
||||
|
"require a Subject."); |
||||
|
} |
||||
|
|
||||
|
Mode = mode; |
||||
|
Priority = priority; |
||||
|
Observable = observable; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="InstancedBinding"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="subject">The subject for a two-way binding.</param>
|
||||
|
/// <param name="mode">The binding mode.</param>
|
||||
|
/// <param name="priority">The binding priority.</param>
|
||||
|
public InstancedBinding( |
||||
|
ISubject<object> subject, |
||||
|
BindingMode mode = BindingMode.OneWay, |
||||
|
BindingPriority priority = BindingPriority.LocalValue) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(subject != null); |
||||
|
|
||||
|
Mode = mode; |
||||
|
Priority = priority; |
||||
|
Subject = subject; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the binding mode with which the binding was initiated.
|
||||
|
/// </summary>
|
||||
|
public BindingMode Mode { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the binding priority.
|
||||
|
/// </summary>
|
||||
|
public BindingPriority Priority { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the value used for a <see cref="BindingMode.OneTime"/> binding.
|
||||
|
/// </summary>
|
||||
|
public object Value { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the observable for a one-way binding.
|
||||
|
/// </summary>
|
||||
|
public IObservable<object> Observable { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the subject for a two-way binding.
|
||||
|
/// </summary>
|
||||
|
public ISubject<object> Subject { get; } |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue