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