Browse Source

Move design-time handling of events...

...to `Avalonia.Markup.Xaml`. Also added tests: tests required a new instance of `AvaloniaXamlSchemaContext` for each `AvaloniaXamlLoader` as otherwise one test can affect other tests.
pull/2307/head
Steven Kirk 7 years ago
parent
commit
f85995ed7e
  1. 5
      src/Avalonia.DesignerSupport/DesignWindowLoader.cs
  2. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  3. 4
      src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs
  4. 31
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  5. 30
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaEventConverter.cs
  6. 2
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
  7. 66
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/EventTests.cs

5
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@ -12,11 +12,6 @@ namespace Avalonia.DesignerSupport
{ {
public class DesignWindowLoader public class DesignWindowLoader
{ {
static DesignWindowLoader()
{
AvaloniaTypeConverters.Register(typeof(EventInfo), typeof(DesignerEventConverter));
}
public static Window LoadDesignerWindow(string xaml, string assemblyPath, string xamlFileProjectPath) public static Window LoadDesignerWindow(string xaml, string assemblyPath, string xamlFileProjectPath)
{ {
Window window; Window window;

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -9,6 +9,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="AvaloniaXamlLoader.cs" /> <Compile Include="AvaloniaXamlLoader.cs" />
<Compile Include="Converters\AvaloniaUriTypeConverter.cs" /> <Compile Include="Converters\AvaloniaUriTypeConverter.cs" />
<Compile Include="Converters\AvaloniaEventConverter.cs" />
<Compile Include="Converters\FontFamilyTypeConverter.cs" /> <Compile Include="Converters\FontFamilyTypeConverter.cs" />
<Compile Include="Converters\MemberSelectorTypeConverter.cs" /> <Compile Include="Converters\MemberSelectorTypeConverter.cs" />
<Compile Include="Converters\NullableTypeConverter.cs" /> <Compile Include="Converters\NullableTypeConverter.cs" />

4
src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs

@ -11,6 +11,7 @@ using Avalonia.Controls.Templates;
namespace Avalonia.Markup.Xaml namespace Avalonia.Markup.Xaml
{ {
using System.Reflection;
using Avalonia.Media; using Avalonia.Media;
/// <summary> /// <summary>
@ -41,7 +42,8 @@ namespace Avalonia.Markup.Xaml
{ typeof(WindowIcon), typeof(IconTypeConverter) }, { typeof(WindowIcon), typeof(IconTypeConverter) },
{ typeof(CultureInfo), typeof(CultureInfoConverter) }, { typeof(CultureInfo), typeof(CultureInfoConverter) },
{ typeof(Uri), typeof(AvaloniaUriTypeConverter) }, { typeof(Uri), typeof(AvaloniaUriTypeConverter) },
{ typeof(FontFamily), typeof(FontFamilyTypeConverter) } { typeof(FontFamily), typeof(FontFamilyTypeConverter) },
{ typeof(EventInfo), typeof(AvaloniaEventConverter) },
}; };
internal static Type GetBuiltinTypeConverter(Type type) internal static Type GetBuiltinTypeConverter(Type type)

31
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -1,21 +1,20 @@
// 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 Avalonia.Controls;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.PortableXaml;
using Avalonia.Platform;
using Portable.Xaml;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
using System.Linq; using Avalonia.Controls;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.PortableXaml;
using Avalonia.Platform;
using Portable.Xaml;
namespace Avalonia.Markup.Xaml namespace Avalonia.Markup.Xaml
{ {
@ -24,7 +23,7 @@ namespace Avalonia.Markup.Xaml
/// </summary> /// </summary>
public class AvaloniaXamlLoader public class AvaloniaXamlLoader
{ {
private readonly AvaloniaXamlSchemaContext _context = GetContext(); private readonly AvaloniaXamlSchemaContext _context = AvaloniaXamlSchemaContext.Create();
public bool IsDesignMode public bool IsDesignMode
{ {
@ -32,22 +31,6 @@ namespace Avalonia.Markup.Xaml
set => _context.IsDesignMode = value; set => _context.IsDesignMode = value;
} }
private static AvaloniaXamlSchemaContext GetContext()
{
var result = AvaloniaLocator.Current.GetService<AvaloniaXamlSchemaContext>();
if (result == null)
{
result = AvaloniaXamlSchemaContext.Create();
AvaloniaLocator.CurrentMutable
.Bind<AvaloniaXamlSchemaContext>()
.ToConstant(result);
}
return result;
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class. /// Initializes a new instance of the <see cref="AvaloniaXamlLoader"/> class.
/// </summary> /// </summary>

30
src/Avalonia.DesignerSupport/DesignerEventConverter.cs → src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaEventConverter.cs

@ -4,11 +4,13 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.PortableXaml;
using Portable.Xaml; using Portable.Xaml;
namespace Avalonia.DesignerSupport namespace Avalonia.Markup.Xaml.Converters
{ {
internal class DesignerEventConverter : TypeConverter internal class AvaloniaEventConverter : TypeConverter
{ {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{ {
@ -72,13 +74,23 @@ namespace Avalonia.DesignerSupport
} }
} }
// We want to ignore missing events in the designer, so if event handler var contextProvider = (IXamlSchemaContextProvider)context.GetService(typeof(IXamlSchemaContextProvider));
// wasn't found create an empty delegate. var avaloniaContext = (AvaloniaXamlSchemaContext)contextProvider.SchemaContext;
var lambdaExpression = Expression.Lambda(
eventType, if (avaloniaContext.IsDesignMode)
Expression.Empty(), {
eventParameters.Select(x => Expression.Parameter(x.ParameterType))); // We want to ignore missing events in the designer, so if event handler
return lambdaExpression.Compile(); // wasn't found create an empty delegate.
var lambdaExpression = Expression.Lambda(
eventType,
Expression.Empty(),
eventParameters.Select(x => Expression.Parameter(x.ParameterType)));
return lambdaExpression.Compile();
}
else
{
throw new XamlObjectWriterException($"Referenced value method {text} in type {target.GetType()} indicated by event {eventType.FullName} was not found");
}
} }
} }
return base.ConvertFrom(context, culture, value); return base.ConvertFrom(context, culture, value);

2
src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github

@ -1 +1 @@
Subproject commit 5d2edfcc1b2988f80303b1b2f3dd2b7c3de53db7 Subproject commit ab5526173722b8988bc5ca3c03c8752ce89c0975

66
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/EventTests.cs

@ -0,0 +1,66 @@
// 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 Avalonia.Controls;
using Avalonia.Input;
using Portable.Xaml;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Xaml
{
public class EventTests
{
[Fact]
public void Event_Is_Attached()
{
var xaml = @"<Button xmlns='https://github.com/avaloniaui' Click='OnClick'/>";
var loader = new AvaloniaXamlLoader();
var target = new MyButton();
loader.Load(xaml, rootInstance: target);
RaiseClick(target);
Assert.True(target.Clicked);
}
[Fact]
public void Exception_Is_Thrown_If_Event_Not_Found()
{
var xaml = @"<Button xmlns='https://github.com/avaloniaui' Click='NotFound'/>";
var loader = new AvaloniaXamlLoader();
var target = new MyButton();
Assert.Throws<XamlObjectWriterException>(() => loader.Load(xaml, rootInstance: target));
}
[Fact]
public void Exception_Is_Not_Thrown_If_Event_Not_Found_In_Design_Mode()
{
var xaml = @"<Button xmlns='https://github.com/avaloniaui' Click='NotFound'/>";
var loader = new AvaloniaXamlLoader { IsDesignMode = true };
var target = new MyButton();
loader.Load(xaml, rootInstance: target);
}
private void RaiseClick(MyButton target)
{
target.RaiseEvent(new KeyEventArgs
{
RoutedEvent = Button.KeyDownEvent,
Key = Key.Enter,
});
}
class MyButton : Button
{
public bool Clicked { get; private set; }
public void OnClick(object sender, EventArgs e)
{
Clicked = true;
}
}
}
}
Loading…
Cancel
Save