Browse Source

Don't use MemberValuePlugins for setting values.

They don't have access to TopDownValueContext, and we're going to need it.
pull/413/head
Steven Kirk 11 years ago
parent
commit
e3bd8d03dd
  1. 4
      src/Markup/Perspex.Markup.Xaml/Context/PerspexAttachableXamlMember.cs
  2. 5
      src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMember.cs
  3. 149
      src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs
  4. 129
      src/Markup/Perspex.Markup.Xaml/Context/PropertyAccessor.cs
  5. 2
      src/Markup/Perspex.Markup.Xaml/OmniXAML
  6. 2
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

4
src/Markup/Perspex.Markup.Xaml/Context/PerspexAttachableXamlMember.cs

@ -20,9 +20,9 @@ namespace Perspex.Markup.Xaml.Context
{
}
protected override IMemberValuePlugin LookupXamlMemberValueConnector()
public override void SetValue(object instance, object value)
{
return new PerspexXamlMemberValuePlugin(this);
PropertyAccessor.SetValue(instance, this, value);
}
public override string ToString()

5
src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMember.cs

@ -4,6 +4,7 @@
using Perspex.Markup.Xaml.Data;
using OmniXaml;
using OmniXaml.Typing;
using System;
namespace Perspex.Markup.Xaml.Context
{
@ -17,9 +18,9 @@ namespace Perspex.Markup.Xaml.Context
{
}
protected override IMemberValuePlugin LookupXamlMemberValueConnector()
public override void SetValue(object instance, object value)
{
return new PerspexXamlMemberValuePlugin(this);
PropertyAccessor.SetValue(instance, this, value);
}
public override string ToString()

149
src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs

@ -1,149 +0,0 @@
// 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.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Glass;
using OmniXaml.ObjectAssembler;
using OmniXaml.Typing;
using Perspex.Controls;
using Perspex.Data;
using Perspex.Markup.Xaml.Data;
using Perspex.Styling;
namespace Perspex.Markup.Xaml.Context
{
public class PerspexXamlMemberValuePlugin : MemberValuePlugin
{
private readonly MutableMember _xamlMember;
public PerspexXamlMemberValuePlugin(MutableMember xamlMember)
: base(xamlMember)
{
_xamlMember = xamlMember;
}
// ReSharper disable once MemberCanBePrivate.Global
public PerspexProperty PerspexProperty
{
get
{
var underlyingType = _xamlMember.DeclaringType.UnderlyingType;
var name = _xamlMember.Name + "Property";
var value = ReflectionExtensions.GetValueOfStaticField(underlyingType, name);
return value as PerspexProperty;
}
}
public override void SetValue(object instance, object value)
{
if (value is IBinding)
{
HandleBinding(instance, (IBinding)value);
}
else if (IsPerspexProperty)
{
HandlePerspexProperty(instance, value);
}
else if (instance is Setter && _xamlMember.Name == "Value")
{
var setter = (Setter)instance;
var targetType = setter.Property.PropertyType;
var valuePipeline = new ValuePipeline(_xamlMember.TypeRepository, null);
var xamlType = _xamlMember.TypeRepository.GetByType(targetType);
base.SetValue(instance, valuePipeline.ConvertValueIfNecessary(value, xamlType));
}
else
{
base.SetValue(instance, value);
}
}
private void HandlePerspexProperty(object instance, object value)
{
var pp = PerspexProperty;
var po = (PerspexObject)instance;
po.SetValue(pp, value);
}
private void HandleBinding(object instance, IBinding binding)
{
if (!(AssignBinding(instance, binding) || ApplyBinding(instance, binding)))
{
throw new InvalidOperationException(
$"Cannot assign to '{_xamlMember.Name}' on '{instance.GetType()}");
}
}
private bool AssignBinding(object instance, IBinding binding)
{
var property = instance.GetType()
.GetRuntimeProperties()
.FirstOrDefault(x => x.Name == _xamlMember.Name);
if (property?.GetCustomAttribute<AssignBindingAttribute>() != null)
{
property.SetValue(instance, binding);
return true;
}
return false;
}
private bool ApplyBinding(object instance, IBinding binding)
{
var targetControl = instance as IControl;
var attached = _xamlMember as PerspexAttachableXamlMember;
if (targetControl == null)
{
return false;
}
PerspexProperty property;
string propertyName;
if (attached == null)
{
propertyName = _xamlMember.Name;
property = PerspexPropertyRegistry.Instance.GetRegistered((PerspexObject)targetControl)
.FirstOrDefault(x => x.Name == propertyName);
}
else
{
// Ensure the OwnerType's static ctor has been run.
RuntimeHelpers.RunClassConstructor(attached.DeclaringType.UnderlyingType.TypeHandle);
propertyName = attached.DeclaringType.UnderlyingType.Name + '.' + _xamlMember.Name;
property = PerspexPropertyRegistry.Instance.GetRegistered((PerspexObject)targetControl)
.Where(x => x.IsAttached && x.OwnerType == attached.DeclaringType.UnderlyingType)
.FirstOrDefault(x => x.Name == _xamlMember.Name);
}
if (property == null)
{
return false;
}
targetControl.Bind(property, binding);
return true;
}
private bool ValueRequiresSpecialHandling(object value)
{
return value is Data.Binding || IsPerspexProperty;
}
private bool IsPerspexProperty => PerspexProperty != null;
public override string ToString()
{
return $"{{Perspex Value Connector for member {_xamlMember}}}";
}
}
}

129
src/Markup/Perspex.Markup.Xaml/Context/PropertyAccessor.cs

@ -0,0 +1,129 @@
// 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.Reflection;
using System.Runtime.CompilerServices;
using OmniXaml.ObjectAssembler;
using OmniXaml.Typing;
using Perspex.Data;
using Perspex.Styling;
namespace Perspex.Markup.Xaml.Context
{
internal static class PropertyAccessor
{
public static void SetValue(object instance, MutableMember member, object value)
{
var perspexProperty = FindPerspexProperty(instance, member);
if (value is IBinding)
{
SetBinding(instance, member, perspexProperty, (IBinding)value);
}
else if (perspexProperty != null)
{
((PerspexObject)instance).SetValue(perspexProperty, value);
}
else if (instance is Setter && member.Name == "Value")
{
// TODO: Make this more generic somehow.
var setter = (Setter)instance;
var targetType = setter.Property.PropertyType;
var valuePipeline = new ValuePipeline(member.TypeRepository, null);
var xamlType = member.TypeRepository.GetByType(targetType);
SetClrProperty(instance, member, valuePipeline.ConvertValueIfNecessary(value, xamlType));
}
else
{
SetClrProperty(instance, member, value);
}
}
private static PerspexProperty FindPerspexProperty(object instance, MutableMember member)
{
var target = instance as IPerspexObject;
var attached = member as PerspexAttachableXamlMember;
if (target == null)
{
return null;
}
PerspexProperty property;
string propertyName;
if (attached == null)
{
propertyName = member.Name;
property = PerspexPropertyRegistry.Instance.GetRegistered((PerspexObject)target)
.FirstOrDefault(x => x.Name == propertyName);
}
else
{
// Ensure the OwnerType's static ctor has been run.
RuntimeHelpers.RunClassConstructor(attached.DeclaringType.UnderlyingType.TypeHandle);
propertyName = attached.DeclaringType.UnderlyingType.Name + '.' + member.Name;
property = PerspexPropertyRegistry.Instance.GetRegistered((PerspexObject)target)
.Where(x => x.IsAttached && x.OwnerType == attached.DeclaringType.UnderlyingType)
.FirstOrDefault(x => x.Name == member.Name);
}
return property;
}
private static void SetBinding(
object instance,
MutableMember member,
PerspexProperty property,
IBinding binding)
{
if (!(AssignBinding(instance, member, binding) || ApplyBinding(instance, property, binding)))
{
throw new InvalidOperationException(
$"Cannot assign to '{member.Name}' on '{instance.GetType()}");
}
}
private static void SetClrProperty(object instance, MutableMember member, object value)
{
if (member.IsAttachable)
{
member.Setter.Invoke(null, new[] { instance, value });
}
else
{
member.Setter.Invoke(instance, new[] { value });
}
}
private static bool AssignBinding(object instance, MutableMember member, IBinding binding)
{
var property = instance.GetType()
.GetRuntimeProperties()
.FirstOrDefault(x => x.Name == member.Name);
if (property?.GetCustomAttribute<AssignBindingAttribute>() != null)
{
property.SetValue(instance, binding);
return true;
}
return false;
}
private static bool ApplyBinding(object instance, PerspexProperty property, IBinding binding)
{
if (property != null)
{
((IPerspexObject)instance).Bind(property, binding);
return true;
}
return false;
}
}
}

2
src/Markup/Perspex.Markup.Xaml/OmniXAML

@ -1 +1 @@
Subproject commit 5ed61489b4db4578680004cd6ddbe811cdd91f8f
Subproject commit a0b7a7f7a415409497539f69e39deb457f7ca629

2
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@ -51,6 +51,7 @@
<Compile Include="Context\PerspexXamlMember.cs" />
<Compile Include="Context\PerspexXamlMemberValuePlugin.cs" />
<Compile Include="Context\PerspexXamlType.cs" />
<Compile Include="Context\PropertyAccessor.cs" />
<Compile Include="Converters\BitmapTypeConverter.cs" />
<Compile Include="Converters\BrushTypeConverter.cs" />
<Compile Include="Converters\ClassesTypeConverter.cs" />
@ -274,6 +275,7 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="ClassDiagram1.cd" />
<None Include="OmniXAML\Source\Glass\ChangeTracking\packages.config" />
<None Include="packages.config" />
</ItemGroup>

Loading…
Cancel
Save