Browse Source
Now, two way bindings work as expected and setting a local value on a property with a binding sets the value temporarily until the binding changes value.pull/58/head
10 changed files with 559 additions and 289 deletions
@ -0,0 +1,17 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="IPerspexPropertyBinding.cs" company="Steven Kirk">
|
||||
|
// Copyright 2015 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Diagnostics |
||||
|
{ |
||||
|
public interface IPerspexPropertyBinding |
||||
|
{ |
||||
|
string Description { get; } |
||||
|
|
||||
|
int Priority { get; } |
||||
|
|
||||
|
object Value { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="PerspexPropertyBinding.cs" company="Steven Kirk">
|
||||
|
// Copyright 2015 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex.Diagnostics |
||||
|
{ |
||||
|
internal class PerspexPropertyBinding : IPerspexPropertyBinding |
||||
|
{ |
||||
|
public string Description { get; set; } |
||||
|
|
||||
|
public int Priority { get; set; } |
||||
|
|
||||
|
public object Value { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,97 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="PriorityValue.cs" company="Steven Kirk">
|
||||
|
// Copyright 2014 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
using Perspex.Diagnostics; |
||||
|
using System; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A registered binding in a <see cref="PriorityValue"/>.
|
||||
|
/// </summary>
|
||||
|
internal class PriorityBindingEntry : IDisposable |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The binding subscription.
|
||||
|
/// </summary>
|
||||
|
private IDisposable subscription; |
||||
|
|
||||
|
public PriorityBindingEntry(int index) |
||||
|
{ |
||||
|
this.Index = index; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a description of the binding.
|
||||
|
/// </summary>
|
||||
|
public string Description |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
public int Index |
||||
|
{ |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The current value of the binding.
|
||||
|
/// </summary>
|
||||
|
public object Value |
||||
|
{ |
||||
|
get; |
||||
|
private set; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Starts listening to the binding.
|
||||
|
/// </summary>
|
||||
|
/// <param name="binding">The binding.</param>
|
||||
|
/// <param name="changed">Called when the binding changes.</param>
|
||||
|
/// <param name="completed">Called when the binding completes.</param>
|
||||
|
public void Start( |
||||
|
IObservable<object> binding, |
||||
|
Action<PriorityBindingEntry> changed, |
||||
|
Action<PriorityBindingEntry> completed) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(binding != null); |
||||
|
Contract.Requires<ArgumentNullException>(changed != null); |
||||
|
Contract.Requires<ArgumentNullException>(completed != null); |
||||
|
|
||||
|
if (this.subscription != null) |
||||
|
{ |
||||
|
throw new Exception("PriorityValue.Entry.Start() called more than once."); |
||||
|
} |
||||
|
|
||||
|
this.Value = PerspexProperty.UnsetValue; |
||||
|
|
||||
|
if (binding is IDescription) |
||||
|
{ |
||||
|
this.Description = ((IDescription)binding).Description; |
||||
|
} |
||||
|
|
||||
|
this.subscription = binding.Subscribe( |
||||
|
value => |
||||
|
{ |
||||
|
this.Value = value; |
||||
|
changed(this); |
||||
|
}, |
||||
|
() => completed(this)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Ends the binding subscription.
|
||||
|
/// </summary>
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (this.subscription != null) |
||||
|
{ |
||||
|
this.subscription.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,121 @@ |
|||||
|
// -----------------------------------------------------------------------
|
||||
|
// <copyright file="PriorityValueTests.cs" company="Steven Kirk">
|
||||
|
// Copyright 2013 MIT Licence. See licence.md for more information.
|
||||
|
// </copyright>
|
||||
|
// -----------------------------------------------------------------------
|
||||
|
|
||||
|
namespace Perspex |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Reactive.Disposables; |
||||
|
|
||||
|
internal class PriorityLevel |
||||
|
{ |
||||
|
private Action<PriorityLevel> changed; |
||||
|
|
||||
|
private object directValue; |
||||
|
|
||||
|
private int nextIndex; |
||||
|
|
||||
|
public PriorityLevel( |
||||
|
int priority, |
||||
|
Action<PriorityLevel> changed) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(changed != null); |
||||
|
|
||||
|
this.changed = changed; |
||||
|
this.Priority = priority; |
||||
|
this.Value = this.directValue = PerspexProperty.UnsetValue; |
||||
|
this.ActiveBindingIndex = -1; |
||||
|
this.Bindings = new LinkedList<PriorityBindingEntry>(); |
||||
|
} |
||||
|
|
||||
|
public int Priority { get; } |
||||
|
|
||||
|
public object DirectValue |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this.directValue; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
this.Value = this.directValue = value; |
||||
|
this.changed(this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public object Value { get; private set; } |
||||
|
|
||||
|
public int ActiveBindingIndex { get; private set; } |
||||
|
|
||||
|
public LinkedList<PriorityBindingEntry> Bindings { get; } |
||||
|
|
||||
|
public IDisposable Add(IObservable<object> binding) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(binding != null); |
||||
|
|
||||
|
var entry = new PriorityBindingEntry(this.nextIndex++); |
||||
|
var node = this.Bindings.AddFirst(entry); |
||||
|
|
||||
|
entry.Start(binding, this.Changed, this.Completed); |
||||
|
|
||||
|
return Disposable.Create(() => |
||||
|
{ |
||||
|
this.Bindings.Remove(node); |
||||
|
|
||||
|
if (entry.Index >= this.ActiveBindingIndex) |
||||
|
{ |
||||
|
this.ActivateFirstBinding(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private void Changed(PriorityBindingEntry entry) |
||||
|
{ |
||||
|
if (entry.Index >= this.ActiveBindingIndex) |
||||
|
{ |
||||
|
if (entry.Value != PerspexProperty.UnsetValue) |
||||
|
{ |
||||
|
this.Value = entry.Value; |
||||
|
this.ActiveBindingIndex = entry.Index; |
||||
|
this.changed(this); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.ActivateFirstBinding(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void Completed(PriorityBindingEntry entry) |
||||
|
{ |
||||
|
this.Bindings.Remove(entry); |
||||
|
|
||||
|
if (entry.Index >= this.ActiveBindingIndex) |
||||
|
{ |
||||
|
this.ActivateFirstBinding(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ActivateFirstBinding() |
||||
|
{ |
||||
|
foreach (var binding in this.Bindings) |
||||
|
{ |
||||
|
if (binding.Value != PerspexProperty.UnsetValue) |
||||
|
{ |
||||
|
this.Value = binding.Value; |
||||
|
this.ActiveBindingIndex = binding.Index; |
||||
|
this.changed(this); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.Value = this.DirectValue; |
||||
|
this.ActiveBindingIndex = -1; |
||||
|
this.changed(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue