Browse Source

Fix last binding test. Two-way bindings via the indexer binding should not cause a PriorityBindingEntry to be created in the target. So instead use SetValue + GetObservable (similar to our higher level implementations in Avalonia.Markup).

pull/856/head
Jeremy Koritzinsky 9 years ago
parent
commit
da8267ade7
  1. 23
      src/Avalonia.Base/AvaloniaObject.cs
  2. 24
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  3. 16
      src/Avalonia.Base/Reactive/AnonymousSubject`1.cs
  4. 49
      src/Avalonia.Base/Reactive/AnonymousSubject`2.cs
  5. 26
      src/Avalonia.Base/Utilities/DelayedSetter.cs
  6. 1
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

23
src/Avalonia.Base/AvaloniaObject.cs

@ -590,28 +590,7 @@ namespace Avalonia
}
protected void SetAndRaise<T>(AvaloniaProperty<T> property, Action<T, Action<Action>> setterCallback, T value, Action<T> delayedSet)
{
Contract.Requires<ArgumentNullException>(setterCallback != null);
Contract.Requires<ArgumentNullException>(delayedSet != null);
if (!directDelayedSetter.IsNotifying(property))
{
setterCallback(value, notification =>
{
using (directDelayedSetter.MarkNotifying(property))
{
notification();
}
});
if (directDelayedSetter.HasPendingSet(property))
{
delayedSet((T)directDelayedSetter.GetFirstPendingSet(property));
}
}
else
{
directDelayedSetter.AddPendingSet(property, value);
}
}
=> directDelayedSetter.SetAndNotify(property, (val, notify) => setterCallback((T)val, notify), value, val => delayedSet((T)val));
/// <summary>
/// Tries to cast a value to a type, taking into account that the value may be a

24
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -138,17 +138,9 @@ namespace Avalonia
AvaloniaProperty property,
BindingPriority priority = BindingPriority.LocalValue)
{
// TODO: Subject.Create<T> is not yet in stable Rx : once it is, remove the
// AnonymousSubject classes and use Subject.Create<T>.
var output = new Subject<object>();
var result = new AnonymousSubject<object>(
Observer.Create<object>(
x => output.OnNext(x),
e => output.OnError(e),
() => output.OnCompleted()),
return Subject.Create<object>(
Observer.Create<object>(x => o.SetValue(property, x, priority)),
o.GetObservable(property));
o.Bind(property, output, priority);
return result;
}
/// <summary>
@ -169,17 +161,9 @@ namespace Avalonia
AvaloniaProperty<T> property,
BindingPriority priority = BindingPriority.LocalValue)
{
// TODO: Subject.Create<T> is not yet in stable Rx : once it is, remove the
// AnonymousSubject classes from this file and use Subject.Create<T>.
var output = new Subject<T>();
var result = new AnonymousSubject<T>(
Observer.Create<T>(
x => output.OnNext(x),
e => output.OnError(e),
() => output.OnCompleted()),
return Subject.Create<T>(
Observer.Create<T>(x => o.SetValue(property, x, priority)),
o.GetObservable(property));
o.Bind(property, output, priority);
return result;
}
/// <summary>

16
src/Avalonia.Base/Reactive/AnonymousSubject`1.cs

@ -1,16 +0,0 @@
// Copyright (c) The Avalonia 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 Avalonia.Reactive
{
public class AnonymousSubject<T> : AnonymousSubject<T, T>, ISubject<T>
{
public AnonymousSubject(IObserver<T> observer, IObservable<T> observable)
: base(observer, observable)
{
}
}
}

49
src/Avalonia.Base/Reactive/AnonymousSubject`2.cs

@ -1,49 +0,0 @@
// Copyright (c) The Avalonia 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 Avalonia.Reactive
{
public class AnonymousSubject<T, U> : ISubject<T, U>
{
private readonly IObserver<T> _observer;
private readonly IObservable<U> _observable;
public AnonymousSubject(IObserver<T> observer, IObservable<U> observable)
{
_observer = observer;
_observable = observable;
}
public void OnCompleted()
{
_observer.OnCompleted();
}
public void OnError(Exception error)
{
if (error == null)
throw new ArgumentNullException("error");
_observer.OnError(error);
}
public void OnNext(T value)
{
_observer.OnNext(value);
}
public IDisposable Subscribe(IObserver<U> observer)
{
if (observer == null)
throw new ArgumentNullException("observer");
//
// [OK] Use of unsafe Subscribe: non-pretentious wrapping of an observable sequence.
//
return _observable.Subscribe/*Unsafe*/(observer);
}
}
}

26
src/Avalonia.Base/Utilities/DelayedSetter.cs

@ -5,7 +5,7 @@ using System.Text;
namespace Avalonia.Utilities
{
public class DelayedSetter<T, TValue>
class DelayedSetter<T, TValue>
{
private class SettingStatus
{
@ -57,5 +57,29 @@ namespace Avalonia.Utilities
{
return setRecords[property].PendingValues.Dequeue();
}
public void SetAndNotify(T property, Action<TValue, Action<Action>> setterCallback, TValue value, Action<TValue> delayedSet)
{
Contract.Requires<ArgumentNullException>(setterCallback != null);
Contract.Requires<ArgumentNullException>(delayedSet != null);
if (!IsNotifying(property))
{
setterCallback(value, notification =>
{
using (MarkNotifying(property))
{
notification();
}
});
if (HasPendingSet(property))
{
delayedSet(GetFirstPendingSet(property));
}
}
else
{
AddPendingSet(property, value);
}
}
}
}

1
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

@ -14,6 +14,7 @@ using Avalonia.Markup.Xaml.Data;
using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.UnitTests;
using Avalonia.Diagnostics;
using Microsoft.Reactive.Testing;
using Moq;
using Xunit;

Loading…
Cancel
Save