Browse Source

WIP almost working fine, style resource binding and minor improvements

pull/916/head
donandren 10 years ago
committed by Andrey Kunchev
parent
commit
5aa306d4e8
  1. 1
      src/Avalonia.Base/Avalonia.Base.csproj
  2. 15
      src/Avalonia.Base/Metadata/AmbientAttribute.cs
  3. 2
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  4. 28
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs
  5. 5
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
  6. 4
      src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs
  7. 12
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
  8. 15
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs
  9. 14
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/TypeExtension.cs
  10. 29
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AttributeExtensions.cs
  11. 38
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaMemberAttributeProvider.cs
  12. 51
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs
  13. 183
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs
  14. 77
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
  15. 153
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
  16. 23
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs
  17. 68
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/XamlBinding.cs

1
src/Avalonia.Base/Avalonia.Base.csproj

@ -70,6 +70,7 @@
<Compile Include="Logging\LogArea.cs" /> <Compile Include="Logging\LogArea.cs" />
<Compile Include="Logging\LogEventLevel.cs" /> <Compile Include="Logging\LogEventLevel.cs" />
<Compile Include="Logging\Logger.cs" /> <Compile Include="Logging\Logger.cs" />
<Compile Include="Metadata\AmbientAttribute.cs" />
<Compile Include="Metadata\TemplateContent.cs" /> <Compile Include="Metadata\TemplateContent.cs" />
<Compile Include="Metadata\DependsOnAttribute.cs" /> <Compile Include="Metadata\DependsOnAttribute.cs" />
<Compile Include="Metadata\ContentAttribute.cs" /> <Compile Include="Metadata\ContentAttribute.cs" />

15
src/Avalonia.Base/Metadata/AmbientAttribute.cs

@ -0,0 +1,15 @@
// 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;
namespace Avalonia.Metadata
{
/// <summary>
/// Defines the ambient class/property
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, Inherited = true)]
public class AmbientAttribute : Attribute
{
}
}

2
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -52,6 +52,8 @@
</Compile> </Compile>
<Compile Include="AvaloniaXamlLoaderPortableXaml.cs" /> <Compile Include="AvaloniaXamlLoaderPortableXaml.cs" />
<Compile Include="AvaloniaXamlLoader.cs" /> <Compile Include="AvaloniaXamlLoader.cs" />
<Compile Include="PortableXaml\XamlBinding.cs" />
<Compile Include="PortableXaml\AttributeExtensions.cs" />
<Compile Include="PortableXaml\AvaloniaMemberAttributeProvider.cs" /> <Compile Include="PortableXaml\AvaloniaMemberAttributeProvider.cs" />
<Compile Include="PortableXaml\AvaloniaNameScope.cs" /> <Compile Include="PortableXaml\AvaloniaNameScope.cs" />
<Compile Include="PortableXaml\AvaloniaDefaultTypeConverters.cs" /> <Compile Include="PortableXaml\AvaloniaDefaultTypeConverters.cs" />

28
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs

@ -1,17 +1,17 @@
// Copyright (c) The Avalonia Project. All rights reserved. // 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. // 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.IO;
using System.Reflection;
using System.Text;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml.Context; using Avalonia.Markup.Xaml.Context;
using Avalonia.Markup.Xaml.Data; using Avalonia.Markup.Xaml.Data;
using Avalonia.Markup.Xaml.PortableXaml; using Avalonia.Markup.Xaml.PortableXaml;
using Avalonia.Platform; using Avalonia.Platform;
using Portable.Xaml; using Portable.Xaml;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
namespace Avalonia.Markup.Xaml namespace Avalonia.Markup.Xaml
{ {
@ -88,7 +88,14 @@ namespace Avalonia.Markup.Xaml
{ {
var initialize = rootInstance as ISupportInitialize; var initialize = rootInstance as ISupportInitialize;
initialize?.BeginInit(); initialize?.BeginInit();
return Load(stream, type, rootInstance, uri); try
{
return Load(stream, type, rootInstance, uri);
}
finally
{
initialize?.EndInit();
}
} }
} }
} }
@ -197,16 +204,17 @@ namespace Avalonia.Markup.Xaml
internal static object LoadFromReader(XamlReader reader, object instance) internal static object LoadFromReader(XamlReader reader, object instance)
{ {
var writer = AvaloniaXamlObjectWriter.Create(_context, instance); var writer = AvaloniaXamlObjectWriter.Create(reader.SchemaContext, instance);
XamlServices.Transform(reader, writer); XamlServices.Transform(reader, writer);
return writer.Result; return writer.Result;
} }
internal static object LoadFromReader(XamlReader reader) internal static object LoadFromReader(XamlReader reader)
{ {
return XamlServices.Load(reader); //return XamlServices.Load(reader);
return LoadFromReader(reader, null);
} }
/// <summary> /// <summary>

5
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs

@ -33,10 +33,7 @@ namespace Avalonia.Markup.Xaml.Converters
if (typeName == null) if (typeName == null)
{ {
var amb = context.GetService<IAmbientProvider>(); var style = context.GetFirstAmbientValue<Style>();
var sc = context.GetService<IXamlSchemaContextProvider>().SchemaContext;
var xamlStyleType = sc.GetXamlType(typeof(Style));
var style = amb.GetFirstAmbientValue(xamlStyleType) as Style;
type = style?.Selector?.TargetType; type = style?.Selector?.TargetType;

4
src/Markup/Avalonia.Markup.Xaml/Data/StyleResourceBinding.cs

@ -40,10 +40,6 @@ namespace Avalonia.Markup.Xaml.Data
object anchor = null, object anchor = null,
bool enableDataValidation = false) bool enableDataValidation = false)
{ {
if (Name == "Red")
{
}
var host = (target as IControl) ?? (anchor as IControl); var host = (target as IControl) ?? (anchor as IControl);
var style = anchor as IStyle; var style = anchor as IStyle;
var resource = AvaloniaProperty.UnsetValue; var resource = AvaloniaProperty.UnsetValue;

12
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs

@ -7,12 +7,12 @@ using System;
namespace Avalonia.Markup.Xaml.MarkupExtensions namespace Avalonia.Markup.Xaml.MarkupExtensions
{ {
#if !OMNIXAML #if !OMNIXAML
using Portable.Xaml.Markup; using Portable.Xaml.Markup;
using PortableXaml;
[MarkupExtensionReturnType(typeof(Binding))] [MarkupExtensionReturnType(typeof(IBinding))]
public class BindingExtension : MarkupExtension public class BindingExtension : MarkupExtension
{ {
public BindingExtension() public BindingExtension()
@ -26,7 +26,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public override object ProvideValue(IServiceProvider serviceProvider) public override object ProvideValue(IServiceProvider serviceProvider)
{ {
return new Binding var b = new Binding
{ {
Converter = Converter, Converter = Converter,
ConverterParameter = ConverterParameter, ConverterParameter = ConverterParameter,
@ -35,7 +35,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
Mode = Mode, Mode = Mode,
Path = Path, Path = Path,
Priority = Priority, Priority = Priority,
RelativeSource = RelativeSource
}; };
return XamlBinding.FromMarkupExtensionContext(b, serviceProvider);
} }
public IValueConverter Converter { get; set; } public IValueConverter Converter { get; set; }
@ -54,7 +57,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public BindingPriority Priority { get; set; } = BindingPriority.LocalValue; public BindingPriority Priority { get; set; } = BindingPriority.LocalValue;
public object Source { get; set; } public object Source { get; set; }
public RelativeSource RelativeSource { get; set; }
} }
#else #else
using OmniXaml; using OmniXaml;

15
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleResourceExtension.cs

@ -1,21 +1,18 @@
// Copyright (c) The Avalonia Project. All rights reserved. // 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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reactive.Linq;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml.Data; using Avalonia.Markup.Xaml.Data;
using Avalonia.Styling; using System;
namespace Avalonia.Markup.Xaml.MarkupExtensions namespace Avalonia.Markup.Xaml.MarkupExtensions
{ {
using Avalonia.Data;
#if !OMNIXAML #if !OMNIXAML
using Portable.Xaml.Markup; using Portable.Xaml.Markup;
using PortableXaml;
[MarkupExtensionReturnType(typeof(StyleResourceBinding))] [MarkupExtensionReturnType(typeof(IBinding))]
public class StyleResourceExtension : MarkupExtension public class StyleResourceExtension : MarkupExtension
{ {
public StyleResourceExtension(string name) public StyleResourceExtension(string name)
@ -25,7 +22,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
public override object ProvideValue(IServiceProvider serviceProvider) public override object ProvideValue(IServiceProvider serviceProvider)
{ {
return new StyleResourceBinding(this.Name); return XamlBinding.FromMarkupExtensionContext(
new StyleResourceBinding(Name),
serviceProvider);
} }
[ConstructorArgument("name")] [ConstructorArgument("name")]

14
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/TypeExtension.cs

@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved. // 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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Markup.Xaml.MarkupExtensions namespace Avalonia.Markup.Xaml.MarkupExtensions
{ {
#if !OMNIXAML #if !OMNIXAML
@ -8,7 +10,19 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
//TODO: check do we need something more than std Portable.xaml type?? //TODO: check do we need something more than std Portable.xaml type??
public class TypeExtension : Portable.Xaml.Markup.TypeExtension public class TypeExtension : Portable.Xaml.Markup.TypeExtension
{ {
public TypeExtension()
{
}
public TypeExtension(string typeName) : base(typeName)
{
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return base.ProvideValue(serviceProvider);
}
} }
#else #else

29
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AttributeExtensions.cs

@ -0,0 +1,29 @@
using Avalonia.Markup.Xaml.Templates;
using avm = Avalonia.Metadata;
using pm = Portable.Xaml.Markup;
namespace Avalonia.Markup.Xaml.PortableXaml
{
internal static class AttributeExtensions
{
public static pm.XamlDeferLoadAttribute ToPortableXaml(this avm.TemplateContentAttribute attrib)
{
if (attrib == null)
{
return null;
}
return new pm.XamlDeferLoadAttribute(typeof(TemplateLoader), typeof(TemplateContent));
}
public static pm.AmbientAttribute ToPortableXaml(this avm.AmbientAttribute attrib)
{
if (attrib == null)
{
return null;
}
return new pm.AmbientAttribute();
}
}
}

38
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaMemberAttributeProvider.cs

@ -1,8 +1,7 @@
using System; using Portable.Xaml.ComponentModel;
using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Avalonia.Markup.Xaml.Templates;
using Portable.Xaml.ComponentModel;
using avm = Avalonia.Metadata; using avm = Avalonia.Metadata;
using pm = Portable.Xaml.Markup; using pm = Portable.Xaml.Markup;
@ -22,26 +21,27 @@ namespace Avalonia.Markup.Xaml.PortableXaml
public object[] GetCustomAttributes(Type attributeType, bool inherit) public object[] GetCustomAttributes(Type attributeType, bool inherit)
{ {
object[] result = null; Attribute result = null;
if (attributeType == typeof(pm.XamlDeferLoadAttribute)) if (attributeType == typeof(pm.XamlDeferLoadAttribute))
{ {
var attr = GetXamlDeferLoadAttribute(inherit); result = _info.GetCustomAttribute<avm.TemplateContentAttribute>(inherit)
.ToPortableXaml();
if (attr != null) }
{ else if (attributeType == typeof(pm.AmbientAttribute))
result = new object[] { attr }; {
} result = _info.GetCustomAttribute<avm.AmbientAttribute>(inherit)
.ToPortableXaml();
} }
if (result == null || result.Length == 0) if (result == null)
{ {
var attr = _info.GetCustomAttributes(attributeType, inherit); var attr = _info.GetCustomAttributes(attributeType, inherit);
return (attr as object[]) ?? attr.ToArray(); return (attr as object[]) ?? attr.ToArray();
} }
else else
{ {
return result; return new object[] { result };
} }
} }
@ -51,19 +51,5 @@ namespace Avalonia.Markup.Xaml.PortableXaml
} }
private readonly MemberInfo _info; private readonly MemberInfo _info;
private Attribute GetXamlDeferLoadAttribute(bool inherit)
{
var result = _info.GetCustomAttributes(typeof(avm.TemplateContentAttribute), inherit)
.Cast<avm.TemplateContentAttribute>()
.FirstOrDefault();
if (result == null)
{
return null;
}
return new pm.XamlDeferLoadAttribute(typeof(TemplateLoader), typeof(TemplateContent));
}
} }
} }

51
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaTypeAttributeProvider.cs

@ -1,11 +1,11 @@
// Copyright (c) The Perspex Project. All rights reserved. // 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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using Portable.Xaml.ComponentModel;
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Avalonia.Metadata; using avm = Avalonia.Metadata;
using Portable.Xaml.ComponentModel;
using pm = Portable.Xaml.Markup; using pm = Portable.Xaml.Markup;
namespace Avalonia.Markup.Xaml.PortableXaml namespace Avalonia.Markup.Xaml.PortableXaml
@ -24,47 +24,50 @@ namespace Avalonia.Markup.Xaml.PortableXaml
public object[] GetCustomAttributes(Type attributeType, bool inherit) public object[] GetCustomAttributes(Type attributeType, bool inherit)
{ {
Attribute result = null;
var ti = _type.GetTypeInfo();
if (attributeType == typeof(pm.ContentPropertyAttribute)) if (attributeType == typeof(pm.ContentPropertyAttribute))
{ {
var cp = GetContentPropertyAttribute(inherit); result = GetContentPropertyAttribute(inherit);
if (cp != null)
{
return new object[] { cp };
}
} }
else if (attributeType == typeof(pm.RuntimeNamePropertyAttribute)) else if (attributeType == typeof(pm.RuntimeNamePropertyAttribute))
{ {
if (_namedType.IsAssignableFrom(_type.GetTypeInfo())) if (_namedType.IsAssignableFrom(ti))
{ {
return new object[] result = new pm.RuntimeNamePropertyAttribute(nameof(INamed.Name));
{
new pm.RuntimeNamePropertyAttribute(nameof(INamed.Name))
};
} }
} }
else if (attributeType == typeof(TypeConverterAttribute)) else if (attributeType == typeof(TypeConverterAttribute))
{ {
var attribs = _type.GetTypeInfo().GetCustomAttributes(attributeType, inherit); result = ti.GetCustomAttribute(attributeType, inherit);
if (attribs?.Any() != true) if (result == null)
{ {
var convType = AvaloniaDefaultTypeConverters.GetTypeConverter(_type); var convType = AvaloniaDefaultTypeConverters.GetTypeConverter(_type);
if (convType != null) if (convType != null)
{ {
return new object[] result = new TypeConverterAttribute(convType);
{
new TypeConverterAttribute(convType)
};
} }
} }
}
return (attribs as object[]) ?? attribs.ToArray(); else if (attributeType == typeof(pm.AmbientAttribute))
{
result = ti.GetCustomAttribute<avm.AmbientAttribute>(inherit)
.ToPortableXaml();
} }
var attr = _type.GetTypeInfo().GetCustomAttributes(attributeType, inherit); if (result == null)
return (attr as object[]) ?? attr.ToArray(); {
var attr = ti.GetCustomAttributes(attributeType, inherit);
return (attr as object[]) ?? attr.ToArray();
}
else
{
return new object[] { result };
}
} }
public bool IsDefined(Type attributeType, bool inherit) public bool IsDefined(Type attributeType, bool inherit)
@ -83,7 +86,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml
while (type != null) while (type != null)
{ {
var properties = type.GetTypeInfo().DeclaredProperties var properties = type.GetTypeInfo().DeclaredProperties
.Where(x => x.GetCustomAttribute<ContentAttribute>() != null); .Where(x => x.GetCustomAttribute<avm.ContentAttribute>() != null);
string result = null; string result = null;
foreach (var property in properties) foreach (var property in properties)

183
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs

@ -1,4 +1,7 @@
using Portable.Xaml; using Avalonia.Data;
using Portable.Xaml;
using System.Collections.Generic;
using System.Linq;
namespace Avalonia.Markup.Xaml.PortableXaml namespace Avalonia.Markup.Xaml.PortableXaml
{ {
@ -37,12 +40,16 @@ namespace Avalonia.Markup.Xaml.PortableXaml
protected override void OnAfterProperties(object value) protected override void OnAfterProperties(object value)
{ {
_delayedValuesHelper.EndInit(value);
base.OnAfterProperties(value); base.OnAfterProperties(value);
//AfterEndInit is not called as it supports only //AfterEndInit is not called as it supports only
//Portable.Xaml.ComponentModel.ISupportInitialize //Portable.Xaml.ComponentModel.ISupportInitialize
//and we have Avalonia.ISupportInitialize so we need some hacks //and we have Avalonia.ISupportInitialize so we need some hacks
_endEditValue = value; HandleEndEdit(value);
_objects.Pop();
} }
protected override void OnBeforeProperties(object value) protected override void OnBeforeProperties(object value)
@ -50,26 +57,49 @@ namespace Avalonia.Markup.Xaml.PortableXaml
//OnAfterBeginInit is not called as it supports only //OnAfterBeginInit is not called as it supports only
//Portable.Xaml.ComponentModel.ISupportInitialize //Portable.Xaml.ComponentModel.ISupportInitialize
//and we have Avalonia.ISupportInitialize so we need some hacks //and we have Avalonia.ISupportInitialize so we need some hacks
HandleBeginInit(value); HandleBeginInit(value);
_delayedValuesHelper.BeginInit(value);
base.OnBeforeProperties(value); base.OnBeforeProperties(value);
}
public override void WriteEndObject() var target = Current;
{
base.WriteEndObject();
//AfterEndInit is not called as it supports only var member = _lastStartMember;
//Portable.Xaml.ComponentModel.ISupportInitialize _lastStartMember = null;
//and we have Avalonia.ISupportInitialize so we need some hacks
HandleEndEdit(_endEditValue); //if (target != null && value != null && member != null &&
_endEditValue = null; // member is PropertyXamlMember && !(value is MarkupExtension) &&
} // member.DeclaringType.ContentProperty?.Name == member.Name &&
// !member.IsReadOnly &&
// member.Invoker?.UnderlyingSetter != null)
//{
// //set default content before start properties
// try
// {
// if (!OnSetValue(target, member, value))
// {
// member.Invoker.SetValue(target, value);
// }
// }
// catch (Exception ex)
// {
// throw new XamlObjectWriterException($"Set value of member '{member}' threw an exception", ex);
// }
//}
private object _endEditValue; _objects.Push(value);
}
private AvaloniaNameScope _nameScope; private AvaloniaNameScope _nameScope;
private Stack<object> _objects = new Stack<object>();
private object Current => _objects.Count > 0 ? _objects.Peek() : null;
private XamlMember _lastStartMember = null;
private void HandleBeginInit(object value) private void HandleBeginInit(object value)
{ {
(value as Avalonia.ISupportInitialize)?.BeginInit(); (value as Avalonia.ISupportInitialize)?.BeginInit();
@ -82,7 +112,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml
private void HandleFinished() private void HandleFinished()
{ {
if(_nameScope != null && Result != null) if (_nameScope != null && Result != null)
{ {
_nameScope.RegisterOnNameScope(Result); _nameScope.RegisterOnNameScope(Result);
} }
@ -97,5 +127,130 @@ namespace Avalonia.Markup.Xaml.PortableXaml
base.Dispose(disposing); base.Dispose(disposing);
} }
public override void WriteStartMember(XamlMember property)
{
base.WriteStartMember(property);
_lastStartMember = property;
}
public override void WriteEndMember()
{
base.WriteEndMember();
_lastStartMember = null;
}
protected override bool OnSetValue(object target, XamlMember member, object value)
{
if (value is IBinding)
{
//delay bindings
_delayedValuesHelper.Add(new DelayedValue(target, member, value));
return true;
}
return base.OnSetValue(target, member, value);
}
private readonly DelayedValuesHelper _delayedValuesHelper = new DelayedValuesHelper();
private class DelayedValue
{
public DelayedValue(object target, XamlMember member, object value)
{
Target = target;
Member = member;
Value = value;
}
public object Target { get; }
public XamlMember Member { get; }
public object Value { get; }
}
private class DelayedValuesHelper
{
private HashSet<object> _targets = new HashSet<object>();
private IList<DelayedValue> _values = new List<DelayedValue>();
private int cnt;
public void BeginInit(object target)
{
++cnt;
AddTargetIfNeeded(target);
}
public void EndInit(object target)
{
--cnt;
if (cnt == 0)
{
EndInit();
}
//else
//{
// AddTargetIfNeeded(target);
//}
}
private void AddTargetIfNeeded(object target)
{
if (!_targets.Contains(target))
{
Add(new DelayedValue(target, null, null));
}
}
public void Add(DelayedValue value)
{
_values.Add(value);
var target = value.Target;
if (!_targets.Contains(value.Target))
{
_targets.Add(target);
(target as ISupportInitialize)?.BeginInit();
}
}
private void EndInit()
{
//TODO: revisit this
//apply delayed values and clear
//that's the last object let's set all delayed bindings
foreach (var dv in Values.Reverse().Where(v => v.Member != null))
{
dv.Member.Invoker.SetValue(dv.Target, dv.Value);
}
//TODO: check/add some order of end init
//currently we are sending end init in the order of
//objects creation
foreach (var v in Values.Reverse())
{
var target = v.Target;
if (_targets.Contains(target))
{
_targets.Remove(target);
(target as ISupportInitialize)?.EndInit();
}
}
_targets.Clear();
_values.Clear();
}
private IEnumerable<DelayedValue> Values => _values;
}
} }
} }

77
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs

@ -1,20 +1,20 @@
using System; using Avalonia.Data;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Avalonia.Markup.Xaml.Context; using Avalonia.Markup.Xaml.Context;
using Portable.Xaml; using Portable.Xaml;
using Portable.Xaml.ComponentModel; using Portable.Xaml.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using am = Avalonia.Metadata; using am = Avalonia.Metadata;
using Avalonia.Data;
using Avalonia.Markup.Xaml.Data;
namespace Avalonia.Markup.Xaml.PortableXaml namespace Avalonia.Markup.Xaml.PortableXaml
{ {
internal class AvaloniaXamlSchemaContext : XamlSchemaContext internal class AvaloniaXamlSchemaContext : XamlSchemaContext
{ {
public AvaloniaXamlSchemaContext(IRuntimeTypeProvider typeProvider) public AvaloniaXamlSchemaContext(IRuntimeTypeProvider typeProvider)
: base(typeProvider.ReferencedAssemblies) //better not set the references assemblies
//: base(typeProvider.ReferencedAssemblies)
{ {
_avaloniaTypeProvider = typeProvider; _avaloniaTypeProvider = typeProvider;
} }
@ -64,7 +64,6 @@ namespace Avalonia.Markup.Xaml.PortableXaml
if (type == null) if (type == null)
{ {
//let's try the simple types //let's try the simple types
//in Portable xaml like xmlns:sys='clr-namespace:System;assembly=mscorlib' //in Portable xaml like xmlns:sys='clr-namespace:System;assembly=mscorlib'
//and sys:Double is not resolved properly //and sys:Double is not resolved properly
@ -145,9 +144,9 @@ namespace Avalonia.Markup.Xaml.PortableXaml
private XamlType GetAvaloniaXamlType(Type type) private XamlType GetAvaloniaXamlType(Type type)
{ {
if (type == typeof(Binding)) if (typeof(IBinding).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
{ {
return new BindingXamlType(type, this); return BindingXamlType.Create(type, this);
} }
//TODO: do we need it ??? //TODO: do we need it ???
@ -161,7 +160,33 @@ namespace Avalonia.Markup.Xaml.PortableXaml
protected override XamlMember GetAttachableProperty(string attachablePropertyName, MethodInfo getter, MethodInfo setter) protected override XamlMember GetAttachableProperty(string attachablePropertyName, MethodInfo getter, MethodInfo setter)
{ {
return base.GetAttachableProperty(attachablePropertyName, getter, setter); var key = new Tuple<MemberInfo, MemberInfo>(getter, setter);
XamlMember result;
if (_cachedMembers.TryGetValue(key, out result))
{
return result;
}
var type = (getter ?? setter).DeclaringType;
var prop = AvaloniaPropertyRegistry.Instance.GetAttached(type)
.FirstOrDefault(v => v.Name == attachablePropertyName);
if (prop != null)
{
result = new AvaloniaAttachedPropertyXamlMember(
prop, attachablePropertyName,
getter, setter, this);
}
if (result == null)
{
result = base.GetAttachableProperty(attachablePropertyName, getter, setter);
}
return _cachedMembers[key] = result;
} }
protected override XamlMember GetProperty(PropertyInfo pi) protected override XamlMember GetProperty(PropertyInfo pi)
@ -169,28 +194,48 @@ namespace Avalonia.Markup.Xaml.PortableXaml
Type objType = pi.DeclaringType; Type objType = pi.DeclaringType;
string name = pi.Name; string name = pi.Name;
XamlMember result;
var key = new Tuple<MemberInfo, MemberInfo>(pi, null);
if (_cachedMembers.TryGetValue(key, out result))
{
return result;
}
var avProp = AvaloniaPropertyRegistry.Instance.FindRegistered(objType, name); var avProp = AvaloniaPropertyRegistry.Instance.FindRegistered(objType, name);
var assignBindingAttr = pi.GetCustomAttribute<AssignBindingAttribute>(); var assignBindingAttr = pi.GetCustomAttribute<AssignBindingAttribute>();
if (avProp != null) if (avProp != null)
{ {
return new AvaloniaPropertyXamlMember(avProp, pi, this) result = new AvaloniaPropertyXamlMember(avProp, pi, this)
{ {
AssignBinding = assignBindingAttr != null AssignBinding = assignBindingAttr != null
}; };
} }
var dependAttr = pi.GetCustomAttribute<am.DependsOnAttribute>(); if (result == null)
{
var dependAttr = pi.GetCustomAttribute<am.DependsOnAttribute>();
if (dependAttr != null)
{
result = new DependOnXamlMember(dependAttr.Name, pi, this);
}
}
if (dependAttr != null) if (result == null)
{ {
return new DependOnXamlMember(dependAttr.Name, pi, this); result = new PropertyXamlMember(pi, this);
} }
return base.GetProperty(pi); return _cachedMembers[key] = result;
} }
private Dictionary<Type, XamlType> _cachedTypes = new Dictionary<Type, XamlType>(); private Dictionary<Type, XamlType> _cachedTypes = new Dictionary<Type, XamlType>();
private Dictionary<Tuple<MemberInfo, MemberInfo>, XamlMember> _cachedMembers =
new Dictionary<Tuple<MemberInfo, MemberInfo>, XamlMember>();
} }
} }

153
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs

@ -1,10 +1,14 @@
using System; using Avalonia.Controls;
using System.Collections.Generic;
using System.Reflection;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Portable.Xaml; using Portable.Xaml;
using Portable.Xaml.Markup; using Portable.Xaml.ComponentModel;
using Portable.Xaml.Schema; using Portable.Xaml.Schema;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml.Serialization;
namespace Avalonia.Markup.Xaml.PortableXaml namespace Avalonia.Markup.Xaml.PortableXaml
{ {
@ -18,23 +22,95 @@ namespace Avalonia.Markup.Xaml.PortableXaml
public class BindingXamlType : AvaloniaXamlType public class BindingXamlType : AvaloniaXamlType
{ {
public BindingXamlType(Type underlyingType, XamlSchemaContext schemaContext) : public static BindingXamlType Create(Type type, XamlSchemaContext schemaContext)
{
if (type == typeof(Binding))
{
//in xmal we need to use the extension
type = typeof(BindingExtension);
}
return new BindingXamlType(type, schemaContext);
}
private static HashSet<Type> _notAssignable =
new HashSet<Type>()
{
typeof (IXmlSerializable)
};
private BindingXamlType(Type underlyingType, XamlSchemaContext schemaContext) :
base(underlyingType, schemaContext) base(underlyingType, schemaContext)
{ {
} }
public override bool CanAssignTo(XamlType xamlType) public override bool CanAssignTo(XamlType xamlType)
{ {
if (_notAssignable.Contains(xamlType.UnderlyingType))
{
return false;
}
return true; return true;
} }
protected override XamlMember LookupAliasedProperty(XamlDirective directive)
{
return base.LookupAliasedProperty(directive);
}
protected override bool LookupIsMarkupExtension()
{
return base.LookupIsMarkupExtension();
}
} }
public class AvaloniaPropertyXamlMember : XamlMember public class PropertyXamlMember : XamlMember
{
protected PropertyXamlMember(string attachablePropertyName,
MethodInfo getter, MethodInfo setter, XamlSchemaContext schemaContext)
: base(attachablePropertyName, getter, setter, schemaContext)
{
}
public PropertyXamlMember(
PropertyInfo propertyInfo,
XamlSchemaContext schemaContext) :
base(propertyInfo, schemaContext)
{
}
protected override MethodInfo LookupUnderlyingSetter()
{
//if we have content property a list
//we have some issues in portable.xaml
//but if the list is read only, this is solving the problem
//TODO: investigate is this good enough as solution ???
//We can add ReadOnyAttribute to cover this
if ((Type.IsCollection || Type.IsDictionary) &&
Name == DeclaringType.ContentProperty?.Name)
{
return null;
}
return base.LookupUnderlyingSetter();
}
}
public class AvaloniaPropertyXamlMember : PropertyXamlMember
{ {
public bool AssignBinding { get; set; } = false; public bool AssignBinding { get; set; } = false;
public AvaloniaProperty Property { get; } public AvaloniaProperty Property { get; }
protected AvaloniaPropertyXamlMember(AvaloniaProperty property,
string attachablePropertyName,
MethodInfo getter, MethodInfo setter, XamlSchemaContext schemaContext)
: base(attachablePropertyName, getter, setter, schemaContext)
{
Property = property;
}
public AvaloniaPropertyXamlMember(AvaloniaProperty property, public AvaloniaPropertyXamlMember(AvaloniaProperty property,
PropertyInfo propertyInfo, PropertyInfo propertyInfo,
XamlSchemaContext schemaContext) : XamlSchemaContext schemaContext) :
@ -48,6 +124,16 @@ namespace Avalonia.Markup.Xaml.PortableXaml
return new AvaloniaPropertyInvoker(this); return new AvaloniaPropertyInvoker(this);
} }
protected override bool LookupIsReadOnly()
{
if (Property.IsReadOnly)
{
return true;
}
return base.LookupIsReadOnly();
}
private class AvaloniaPropertyInvoker : XamlMemberInvoker private class AvaloniaPropertyInvoker : XamlMemberInvoker
{ {
public AvaloniaPropertyInvoker(XamlMember member) : base(member) public AvaloniaPropertyInvoker(XamlMember member) : base(member)
@ -59,9 +145,14 @@ namespace Avalonia.Markup.Xaml.PortableXaml
if (Property != null) if (Property != null)
{ {
var obj = ((IAvaloniaObject)instance); var obj = ((IAvaloniaObject)instance);
if (value is IBinding && !Member.AssignBinding) if (value is IBinding)
{ {
ApplyBinding(obj, (IBinding)value); if (!Member.AssignBinding)
ApplyBinding(obj, (IBinding)value);
else
obj.SetValue(Property, value is XamlBinding ?
(value as XamlBinding).Value :
value);
} }
else else
{ {
@ -86,11 +177,23 @@ namespace Avalonia.Markup.Xaml.PortableXaml
} }
} }
private void ApplyBinding(IAvaloniaObject obj, IBinding binding) public void ApplyBinding(IAvaloniaObject obj, IBinding binding)
{ {
//TODO: in Context.PropertyAccessor there is var control = obj as IControl;
//some quirk stuff check it later var property = Property;
obj.Bind(Property, binding); var xamlBinding = binding as XamlBinding;
//if (control != null && property != Control.DataContextProperty)
// DelayedBinding.Add(control, property, binding);
//else
if (xamlBinding != null)
obj.Bind(property, xamlBinding.Value, xamlBinding.Anchor?.Target);
else
obj.Bind(property, binding);
}
public void SetValue(ITypeDescriptorContext context, object instance, object value)
{
throw new NotImplementedException();
} }
private AvaloniaProperty Property => Member.Property; private AvaloniaProperty Property => Member.Property;
@ -100,7 +203,27 @@ namespace Avalonia.Markup.Xaml.PortableXaml
} }
} }
public class DependOnXamlMember : XamlMember public class AvaloniaAttachedPropertyXamlMember : AvaloniaPropertyXamlMember
{
private MethodInfo _setter;
public AvaloniaAttachedPropertyXamlMember(AvaloniaProperty property,
string attachablePropertyName,
MethodInfo getter, MethodInfo setter,
XamlSchemaContext schemaContext)
: base(property, attachablePropertyName, getter, setter, schemaContext)
{
_setter = setter;
}
protected override MethodInfo LookupUnderlyingSetter()
{
//TODO: investigate don't call base stack overflow
return _setter;
}
}
public class DependOnXamlMember : PropertyXamlMember
{ {
private string _dependOn; private string _dependOn;
@ -157,6 +280,10 @@ namespace Avalonia.Markup.Xaml.PortableXaml
value = ttConv.ConvertFromString(value as string); value = ttConv.ConvertFromString(value as string);
} }
} }
if (value is XamlBinding)
{
value = (value as XamlBinding).Value;
}
base.SetValue(instance, value); base.SetValue(instance, value);
} }

23
src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs

@ -1,5 +1,7 @@
using System; using System;
using Portable.Xaml.Markup; using Portable.Xaml.Markup;
using System.Linq;
using System.Collections.Generic;
namespace Portable.Xaml.ComponentModel namespace Portable.Xaml.ComponentModel
{ {
@ -33,5 +35,26 @@ namespace Portable.Xaml.ComponentModel
return tr?.Resolve(name); return tr?.Resolve(name);
} }
public static T GetFirstAmbientValue<T>(this ITypeDescriptorContext ctx) where T : class
{
var amb = ctx.GetService<IAmbientProvider>();
var sc = ctx.GetService<IXamlSchemaContextProvider>().SchemaContext;
return amb.GetFirstAmbientValue(sc.GetXamlType(typeof(T))) as T;
}
public static T GetLastOrDefaultAmbientValue<T>(this ITypeDescriptorContext ctx) where T : class
{
return ctx.GetAllambientValues<T>().LastOrDefault() as T;
}
public static IEnumerable<T> GetAllambientValues<T>(this ITypeDescriptorContext ctx) where T : class
{
var amb = ctx.GetService<IAmbientProvider>();
var sc = ctx.GetService<IXamlSchemaContextProvider>().SchemaContext;
return amb.GetAllAmbientValues(sc.GetXamlType(typeof(T))).OfType<T>();
}
} }
} }

68
src/Markup/Avalonia.Markup.Xaml/PortableXaml/XamlBinding.cs

@ -0,0 +1,68 @@
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Styling;
using Portable.Xaml.ComponentModel;
using Portable.Xaml.Markup;
using System;
namespace Avalonia.Markup.Xaml.PortableXaml
{
internal class XamlBinding : IBinding
{
public static IBinding FromMarkupExtensionContext(
IBinding binding,
IServiceProvider serviceProvider)
{
var context = (ITypeDescriptorContext)serviceProvider;
var pvt = context.GetService<IProvideValueTarget>();
if (pvt.TargetObject is IControl) return binding;
object anchor = GetDefaultAnchor(context);
if (anchor == null) return binding;
return new XamlBinding(binding, anchor);
}
private static object GetDefaultAnchor(ITypeDescriptorContext context)
{
object anchor = null;
//// The target is not a control, so we need to find an anchor that will let us look
//// up named controls and style resources. First look for the closest IControl in
//// the TopDownValueContext.
//anchor = context.TopDownValueContext.StoredInstances
// .Select(x => x.Instance)
// .OfType<IControl>()
// .LastOrDefault();
anchor = context.GetFirstAmbientValue<IControl>();
//// If a control was not found, then try to find the highest-level style as the XAML
//// file could be a XAML file containing only styles.
// anchor = context.TopDownValueContext.StoredInstances
// .Select(x => x.Instance)
// .OfType<IStyle>()
// .FirstOrDefault();
return anchor ?? context.GetLastOrDefaultAmbientValue<IStyle>();
}
private XamlBinding(IBinding binding, object anchor)
{
Value = binding;
Anchor = new WeakReference(anchor);
}
public WeakReference Anchor { get; }
public IBinding Value { get; }
public InstancedBinding Initiate(IAvaloniaObject target, AvaloniaProperty targetProperty, object anchor = null, bool enableDataValidation = false)
{
return Value.Initiate(target, targetProperty,
anchor ?? Anchor.Target, enableDataValidation);
}
}
}
Loading…
Cancel
Save