6 changed files with 233 additions and 43 deletions
@ -0,0 +1,35 @@ |
|||
// 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; |
|||
|
|||
namespace Perspex.Markup.Data.Plugins |
|||
{ |
|||
/// <summary>
|
|||
/// Defines an accessor to a property on an object returned by a
|
|||
/// <see cref="IPropertyAccessorPlugin"/>
|
|||
/// </summary>
|
|||
public interface IPropertyAccessor : IDisposable |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the type of the property.
|
|||
/// </summary>
|
|||
Type PropertyType { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the current value of the property.
|
|||
/// </summary>
|
|||
object Value { get; } |
|||
|
|||
/// <summary>
|
|||
/// Sets the property value.
|
|||
/// </summary>
|
|||
/// <param name="value">
|
|||
/// The value to set. Guaranteed to be of a valid type for the property.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// True if the property was set; false if the property could not be set.
|
|||
/// </returns>
|
|||
bool SetValue(object value); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// 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; |
|||
|
|||
namespace Perspex.Markup.Data.Plugins |
|||
{ |
|||
/// <summary>
|
|||
/// Defines how a member is read, written and observed by a
|
|||
/// <see cref="ExpressionObserver"/>.
|
|||
/// </summary>
|
|||
public interface 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>
|
|||
bool Match(object instance); |
|||
|
|||
/// <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>
|
|||
IPropertyAccessor Start(object instance, string propertyName, Action<object> changed); |
|||
} |
|||
} |
|||
@ -0,0 +1,132 @@ |
|||
// 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 standard C# object that optionally supports the
|
|||
/// <see cref="INotifyPropertyChanged"/> interface.
|
|||
/// </summary>
|
|||
public class InpcPropertyAccessorPlugin : 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 true; |
|||
} |
|||
|
|||
/// <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 p = instance.GetType().GetRuntimeProperty(propertyName); |
|||
|
|||
if (p != null) |
|||
{ |
|||
return new Accessor(instance, p, changed); |
|||
} |
|||
else |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
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; |
|||
private PropertyInfo _property; |
|||
private Action<object> _changed; |
|||
|
|||
public Accessor(object instance, PropertyInfo property, Action<object> changed) |
|||
{ |
|||
Contract.Requires<ArgumentNullException>(instance != null); |
|||
Contract.Requires<ArgumentNullException>(property != null); |
|||
|
|||
_instance = instance; |
|||
_property = property; |
|||
_changed = changed; |
|||
|
|||
var inpc = instance as INotifyPropertyChanged; |
|||
|
|||
if (inpc != null) |
|||
{ |
|||
inpc.PropertyChanged += PropertyChanged; |
|||
} |
|||
} |
|||
|
|||
public Type PropertyType |
|||
{ |
|||
get { return _property.PropertyType; } |
|||
} |
|||
|
|||
public object Value |
|||
{ |
|||
get { return _property.GetValue(_instance); } |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
var inpc = _instance as INotifyPropertyChanged; |
|||
|
|||
if (inpc != null) |
|||
{ |
|||
inpc.PropertyChanged -= PropertyChanged; |
|||
} |
|||
} |
|||
|
|||
public bool SetValue(object value) |
|||
{ |
|||
if (_property.CanWrite) |
|||
{ |
|||
_property.SetValue(_instance, value); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private void PropertyChanged(object sender, PropertyChangedEventArgs e) |
|||
{ |
|||
if (e.PropertyName == _property.Name) |
|||
{ |
|||
_changed(Value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue