Browse Source

Added PerspexProperty plugin to ExpressionObserver.

pull/297/head
Steven Kirk 11 years ago
parent
commit
3427b7655b
  1. 3
      src/Markup/Perspex.Markup/Data/ExpressionObserver.cs
  2. 12
      src/Markup/Perspex.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs
  3. 101
      src/Markup/Perspex.Markup/Data/Plugins/PerspexPropertyAccessorPlugin.cs
  4. 1
      src/Markup/Perspex.Markup/Perspex.Markup.csproj
  5. 5
      src/Perspex.Base/PerspexProperty.cs
  6. 45
      tests/Perspex.Markup.UnitTests/Binding/ExpressionObserverTests_PerspexProperty.cs
  7. 1
      tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj

3
src/Markup/Perspex.Markup/Data/ExpressionObserver.cs

@ -21,7 +21,8 @@ namespace Perspex.Markup.Data
public static readonly IList<IPropertyAccessorPlugin> PropertyAccessors =
new List<IPropertyAccessorPlugin>
{
new InpcPropertyAccessorPlugin()
new PerspexPropertyAccessorPlugin(),
new InpcPropertyAccessorPlugin(),
};
private Func<object> _root;

12
src/Markup/Perspex.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs

@ -54,18 +54,6 @@ namespace Perspex.Markup.Data.Plugins
}
}
private IObservable<object> GetObservable(
INotifyPropertyChanged inpc,
PropertyInfo property,
object o)
{
return Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
x => inpc.PropertyChanged += x,
x => inpc.PropertyChanged -= x)
.Where(e => e.EventArgs.PropertyName == property.Name)
.Select(_ => property.GetValue(o));
}
private class Accessor : IPropertyAccessor
{
private object _instance;

101
src/Markup/Perspex.Markup/Data/Plugins/PerspexPropertyAccessorPlugin.cs

@ -0,0 +1,101 @@
// 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.ComponentModel;
using System.Reactive.Linq;
using System.Reflection;
namespace Perspex.Markup.Data.Plugins
{
/// <summary>
/// Reads a property from a <see cref="PerspexObject"/>.
/// </summary>
public class PerspexPropertyAccessorPlugin : IPropertyAccessorPlugin
{
/// <summary>
/// Checks whether this plugin can handle accessing the properties of the specified object.
/// </summary>
/// <param name="instance">The object.</param>
/// <returns>True if the plugin can handle the object; otherwise false.</returns>
public bool Match(object instance)
{
Contract.Requires<ArgumentNullException>(instance != null);
return instance is PerspexObject;
}
/// <summary>
/// Starts monitoring the value of a property on an object.
/// </summary>
/// <param name="instance">The object.</param>
/// <param name="propertyName">The property name.</param>
/// <param name="changed">A function to call when the property changes.</param>
/// <returns>
/// An <see cref="IPropertyAccessor"/> interface through which future interactions with the
/// property will be made, or null if the property was not found.
/// </returns>
public IPropertyAccessor Start(object instance, string propertyName, Action<object> changed)
{
Contract.Requires<ArgumentNullException>(instance != null);
Contract.Requires<ArgumentNullException>(propertyName != null);
Contract.Requires<ArgumentNullException>(changed != null);
var o = (PerspexObject)instance;
var p = PerspexPropertyRegistry.Instance.FindRegistered(o, propertyName);
if (p != null)
{
return new Accessor(o, p, changed);
}
else
{
return null;
}
}
private class Accessor : IPropertyAccessor
{
private PerspexObject _instance;
private PerspexProperty _property;
private IDisposable _subscription;
public Accessor(PerspexObject instance, PerspexProperty property, Action<object> changed)
{
Contract.Requires<ArgumentNullException>(instance != null);
Contract.Requires<ArgumentNullException>(property != null);
_instance = instance;
_property = property;
_subscription = instance.GetObservable(property).Skip(1).Subscribe(changed);
}
public Type PropertyType
{
get { return _property.PropertyType; }
}
public object Value
{
get { return _instance.GetValue(_property); }
}
public void Dispose()
{
_subscription?.Dispose();
_subscription = null;
}
public bool SetValue(object value)
{
if (!_property.IsReadOnly)
{
_instance.SetValue(_property, value);
return true;
}
return false;
}
}
}
}

1
src/Markup/Perspex.Markup/Perspex.Markup.csproj

@ -42,6 +42,7 @@
<Compile Include="Data\ExpressionNodeBuilder.cs" />
<Compile Include="Data\ExpressionParseException.cs" />
<Compile Include="Data\ExpressionSubject.cs" />
<Compile Include="Data\Plugins\PerspexPropertyAccessorPlugin.cs" />
<Compile Include="Data\Plugins\InpcPropertyAccessorPlugin.cs" />
<Compile Include="Data\Plugins\IPropertyAccessor.cs" />
<Compile Include="Data\Plugins\IPropertyAccessorPlugin.cs" />

5
src/Perspex.Base/PerspexProperty.cs

@ -270,6 +270,11 @@ namespace Perspex
/// </summary>
public bool IsDirect { get; }
/// <summary>
/// Gets a value indicating whether this is a readonly property.
/// </summary>
public bool IsReadOnly => IsDirect && Setter == null;
/// <summary>
/// Gets an observable that is fired when this property is initialized on a
/// new <see cref="PerspexObject"/> instance.

45
tests/Perspex.Markup.UnitTests/Binding/ExpressionObserverTests_PerspexProperty.cs

@ -0,0 +1,45 @@
// 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.Collections.Generic;
using System.Reactive.Linq;
using Perspex.Markup.Data;
using Xunit;
namespace Perspex.Markup.UnitTests.Binding
{
public class ExpressionObserverTests_PerspexProperty
{
[Fact]
public async void Should_Get_Simple_Property_Value()
{
var data = new Class1();
var target = new ExpressionObserver(data, "Foo");
var result = await target.Take(1);
Assert.Equal("foo", result);
}
[Fact]
public void Should_Track_Simple_Property_Value()
{
var data = new Class1();
var target = new ExpressionObserver(data, "Foo");
var result = new List<object>();
var sub = target.Subscribe(x => result.Add(x));
data.SetValue(Class1.FooProperty, "bar");
Assert.Equal(new[] { "foo", "bar" }, result);
sub.Dispose();
}
private class Class1 : PerspexObject
{
public static readonly PerspexProperty<string> FooProperty =
PerspexProperty.Register<Class1, string>("Foo", defaultValue: "foo");
}
}
}

1
tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj

@ -73,6 +73,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Binding\ExpressionNodeBuilderTests_Errors.cs" />
<Compile Include="Binding\ExpressionObserverTests_PerspexProperty.cs" />
<Compile Include="Binding\ExpressionSubjectTests.cs" />
<Compile Include="Binding\ExpressionObserverTests_Observable.cs" />
<Compile Include="Binding\ExpressionObserverTests_SetValue.cs" />

Loading…
Cancel
Save