diff --git a/samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs b/samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs
index 2ab6c26e68..df80931367 100644
--- a/samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs
+++ b/samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs
@@ -21,7 +21,7 @@ namespace BindingDemo.ViewModels
}
else
{
- throw new ArgumentOutOfRangeException("Value must be less than 10.");
+ throw new ArgumentOutOfRangeException(nameof(value), "Value must be less than 10.");
}
}
}
diff --git a/samples/ControlCatalog/Pages/ContextMenuPage.xaml b/samples/ControlCatalog/Pages/ContextMenuPage.xaml
index 932f3292ff..8f147638b5 100644
--- a/samples/ControlCatalog/Pages/ContextMenuPage.xaml
+++ b/samples/ControlCatalog/Pages/ContextMenuPage.xaml
@@ -10,7 +10,8 @@
HorizontalAlignment="Center"
Spacing="16">
+ Margin="16"
+ Padding="48,48,48,48">
@@ -31,7 +32,24 @@
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs b/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
index dc73bef07a..96e8b49f89 100644
--- a/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
@@ -1,5 +1,6 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
+using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
@@ -8,6 +9,7 @@ namespace ControlCatalog.Pages
public ContextMenuPage()
{
this.InitializeComponent();
+ DataContext = new ContextMenuPageViewModel();
}
private void InitializeComponent()
diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml.cs b/samples/ControlCatalog/Pages/MenuPage.xaml.cs
index 5a07cae89f..0a77607719 100644
--- a/samples/ControlCatalog/Pages/MenuPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/MenuPage.xaml.cs
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
using System.Windows.Input;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
+using ControlCatalog.ViewModels;
using ReactiveUI;
namespace ControlCatalog.Pages
@@ -13,51 +14,7 @@ namespace ControlCatalog.Pages
public MenuPage()
{
this.InitializeComponent();
- var vm = new MenuPageViewModel();
-
- vm.MenuItems = new[]
- {
- new MenuItemViewModel
- {
- Header = "_File",
- Items = new[]
- {
- new MenuItemViewModel { Header = "_Open...", Command = vm.OpenCommand },
- new MenuItemViewModel { Header = "Save", Command = vm.SaveCommand },
- new MenuItemViewModel { Header = "-" },
- new MenuItemViewModel
- {
- Header = "Recent",
- Items = new[]
- {
- new MenuItemViewModel
- {
- Header = "File1.txt",
- Command = vm.OpenRecentCommand,
- CommandParameter = @"c:\foo\File1.txt"
- },
- new MenuItemViewModel
- {
- Header = "File2.txt",
- Command = vm.OpenRecentCommand,
- CommandParameter = @"c:\foo\File2.txt"
- },
- }
- },
- }
- },
- new MenuItemViewModel
- {
- Header = "_Edit",
- Items = new[]
- {
- new MenuItemViewModel { Header = "_Copy" },
- new MenuItemViewModel { Header = "_Paste" },
- }
- }
- };
-
- DataContext = vm;
+ DataContext = new MenuPageViewModel();
}
private void InitializeComponent()
@@ -65,51 +22,4 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this);
}
}
-
- public class MenuPageViewModel
- {
- public MenuPageViewModel()
- {
- OpenCommand = ReactiveCommand.CreateFromTask(Open);
- SaveCommand = ReactiveCommand.Create(Save);
- OpenRecentCommand = ReactiveCommand.Create(OpenRecent);
- }
-
- public IReadOnlyList MenuItems { get; set; }
- public ReactiveCommand OpenCommand { get; }
- public ReactiveCommand SaveCommand { get; }
- public ReactiveCommand OpenRecentCommand { get; }
-
- public async Task Open()
- {
- var dialog = new OpenFileDialog();
- var result = await dialog.ShowAsync(App.Current.MainWindow);
-
- if (result != null)
- {
- foreach (var path in result)
- {
- System.Diagnostics.Debug.WriteLine($"Opened: {path}");
- }
- }
- }
-
- public void Save()
- {
- System.Diagnostics.Debug.WriteLine("Save");
- }
-
- public void OpenRecent(string path)
- {
- System.Diagnostics.Debug.WriteLine($"Open recent: {path}");
- }
- }
-
- public class MenuItemViewModel
- {
- public string Header { get; set; }
- public ICommand Command { get; set; }
- public object CommandParameter { get; set; }
- public IList Items { get; set; }
- }
}
diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml
index 625b344b8c..fea55bcb07 100644
--- a/samples/ControlCatalog/SideBar.xaml
+++ b/samples/ControlCatalog/SideBar.xaml
@@ -1,6 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
+
diff --git a/src/Avalonia.Visuals/Media/DrawingContext.cs b/src/Avalonia.Visuals/Media/DrawingContext.cs
index e7d6df5a93..fd593db991 100644
--- a/src/Avalonia.Visuals/Media/DrawingContext.cs
+++ b/src/Avalonia.Visuals/Media/DrawingContext.cs
@@ -109,6 +109,8 @@ namespace Avalonia.Media
/// The geometry.
public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry)
{
+ Contract.Requires(geometry != null);
+
if (brush != null || PenIsVisible(pen))
{
PlatformImpl.DrawGeometry(brush, pen, geometry.PlatformImpl);
diff --git a/src/Avalonia.Visuals/Media/GeometryDrawing.cs b/src/Avalonia.Visuals/Media/GeometryDrawing.cs
index a26a5341c8..ac0cc1c17d 100644
--- a/src/Avalonia.Visuals/Media/GeometryDrawing.cs
+++ b/src/Avalonia.Visuals/Media/GeometryDrawing.cs
@@ -31,7 +31,10 @@
public override void Draw(DrawingContext context)
{
- context.DrawGeometry(Brush, Pen, Geometry);
+ if (Geometry != null)
+ {
+ context.DrawGeometry(Brush, Pen, Geometry);
+ }
}
public override Rect GetBounds()
@@ -41,4 +44,4 @@
return Geometry?.GetRenderBounds(pen) ?? new Rect();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
index 46d264e518..2d01b117d9 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
@@ -89,9 +89,14 @@ namespace Avalonia.Rendering.SceneGraph
///
public override bool HitTest(Point p)
{
- p *= Transform.Invert();
- return (Brush != null && Geometry.FillContains(p)) ||
- (Pen != null && Geometry.StrokeContains(Pen, p));
+ if (Transform.HasInverse)
+ {
+ p *= Transform.Invert();
+ return (Brush != null && Geometry.FillContains(p)) ||
+ (Pen != null && Geometry.StrokeContains(Pen, p));
+ }
+
+ return false;
}
}
}
diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs
index d2b4c97a05..cc7da96ee8 100644
--- a/src/Avalonia.Visuals/Visual.cs
+++ b/src/Avalonia.Visuals/Visual.cs
@@ -552,7 +552,7 @@ namespace Avalonia
{
if (c == null)
{
- throw new ArgumentNullException("Cannot add null to VisualChildren.");
+ throw new ArgumentNullException(nameof(c), "Cannot add null to VisualChildren.");
}
if (c.VisualParent != null)
diff --git a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
index 44d887241c..878689442d 100644
--- a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
+++ b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
@@ -84,7 +84,7 @@ namespace Avalonia.Gtk3
public RenderOp(GtkWidget widget, ManagedCairoSurface surface, double factor, int width, int height)
{
_widget = widget;
- _surface = surface ?? throw new ArgumentNullException();
+ _surface = surface ?? throw new ArgumentNullException(nameof(surface));
_factor = factor;
_width = width;
_height = height;
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index 38d207a31d..3c1ef84c54 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -9,6 +9,7 @@
+
diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs
index c30822aacb..be92b6a8d7 100644
--- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaTypeConverters.cs
@@ -11,6 +11,7 @@ using Avalonia.Controls.Templates;
namespace Avalonia.Markup.Xaml
{
+ using System.Reflection;
using Avalonia.Media;
///
@@ -41,7 +42,8 @@ namespace Avalonia.Markup.Xaml
{ typeof(WindowIcon), typeof(IconTypeConverter) },
{ typeof(CultureInfo), typeof(CultureInfoConverter) },
{ typeof(Uri), typeof(AvaloniaUriTypeConverter) },
- { typeof(FontFamily), typeof(FontFamilyTypeConverter) }
+ { typeof(FontFamily), typeof(FontFamilyTypeConverter) },
+ { typeof(EventInfo), typeof(AvaloniaEventConverter) },
};
internal static Type GetBuiltinTypeConverter(Type type)
diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
index b99864b050..a1f8bf6cf6 100644
--- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
@@ -1,21 +1,20 @@
// 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 Avalonia.Controls;
-using Avalonia.Markup.Data;
-using Avalonia.Markup.Xaml.PortableXaml;
-using Avalonia.Platform;
-using Portable.Xaml;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
+using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
-using System.Runtime.Serialization.Json;
using System.Text;
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
{
@@ -24,29 +23,7 @@ namespace Avalonia.Markup.Xaml
///
public class AvaloniaXamlLoader
{
- private readonly AvaloniaXamlSchemaContext _context = GetContext();
-
- public bool IsDesignMode
- {
- get => _context.IsDesignMode;
- set => _context.IsDesignMode = value;
- }
-
- private static AvaloniaXamlSchemaContext GetContext()
- {
- var result = AvaloniaLocator.Current.GetService();
-
- if (result == null)
- {
- result = AvaloniaXamlSchemaContext.Create();
-
- AvaloniaLocator.CurrentMutable
- .Bind()
- .ToConstant(result);
- }
-
- return result;
- }
+ public bool IsDesignMode { get; set; }
///
/// Initializes a new instance of the class.
@@ -188,7 +165,8 @@ namespace Avalonia.Markup.Xaml
LocalAssembly = localAssembly
};
- var reader = new XamlXmlReader(stream, _context, readerSettings);
+ var context = IsDesignMode ? AvaloniaXamlSchemaContext.DesignInstance : AvaloniaXamlSchemaContext.Instance;
+ var reader = new XamlXmlReader(stream, context, readerSettings);
object result = LoadFromReader(
reader,
diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaEventConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaEventConverter.cs
new file mode 100644
index 0000000000..665e71bfea
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaEventConverter.cs
@@ -0,0 +1,99 @@
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml.PortableXaml;
+using Portable.Xaml;
+
+namespace Avalonia.Markup.Xaml.Converters
+{
+ internal class AvaloniaEventConverter : TypeConverter
+ {
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+ {
+ return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
+ }
+
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ var text = value as string;
+ if (text != null)
+ {
+ var rootObjectProvider = context.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider;
+ var destinationTypeProvider = context.GetService(typeof(IDestinationTypeProvider)) as IDestinationTypeProvider;
+ if (rootObjectProvider != null && destinationTypeProvider != null)
+ {
+ var target = rootObjectProvider.RootObject;
+ var eventType = destinationTypeProvider.GetDestinationType();
+ var eventParameters = eventType.GetRuntimeMethods().First(r => r.Name == "Invoke").GetParameters();
+ // go in reverse to match System.Xaml behaviour
+ var methods = target.GetType().GetRuntimeMethods().Reverse();
+
+ // find based on exact match parameter types first
+ foreach (var method in methods)
+ {
+ if (method.Name != text)
+ continue;
+ var parameters = method.GetParameters();
+ if (eventParameters.Length != parameters.Length)
+ continue;
+ if (parameters.Length == 0)
+ return method.CreateDelegate(eventType, target);
+
+ for (int i = 0; i < parameters.Length; i++)
+ {
+ var param = parameters[i];
+ var eventParam = eventParameters[i];
+ if (param.ParameterType != eventParam.ParameterType)
+ break;
+ if (i == parameters.Length - 1)
+ return method.CreateDelegate(eventType, target);
+ }
+ }
+
+ // EnhancedXaml: Find method with compatible base class parameters
+ foreach (var method in methods)
+ {
+ if (method.Name != text)
+ continue;
+ var parameters = method.GetParameters();
+ if (parameters.Length == 0 || eventParameters.Length != parameters.Length)
+ continue;
+
+ for (int i = 0; i < parameters.Length; i++)
+ {
+ var param = parameters[i];
+ var eventParam = eventParameters[i];
+ if (!param.ParameterType.GetTypeInfo().IsAssignableFrom(eventParam.ParameterType.GetTypeInfo()))
+ break;
+ if (i == parameters.Length - 1)
+ return method.CreateDelegate(eventType, target);
+ }
+ }
+
+ var contextProvider = (IXamlSchemaContextProvider)context.GetService(typeof(IXamlSchemaContextProvider));
+ var avaloniaContext = (AvaloniaXamlSchemaContext)contextProvider.SchemaContext;
+
+ if (avaloniaContext.IsDesignMode)
+ {
+ // We want to ignore missing events in the designer, so if event handler
+ // 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);
+ }
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaMemberAttributeProvider.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaMemberAttributeProvider.cs
index e9f6ba6945..529cbab938 100644
--- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaMemberAttributeProvider.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaMemberAttributeProvider.cs
@@ -49,6 +49,18 @@ namespace Avalonia.Markup.Xaml.PortableXaml
//Portable.Xaml is not searching for Type Converter
result = new TypeConverterAttribute(typeof(SetterValueTypeConverter));
}
+ else if (attributeType == typeof(TypeConverterAttribute) && _info is EventInfo)
+ {
+ // If a type converter for `EventInfo` is registered, then use that to convert
+ // event handler values. This is used by the designer to override the lookup
+ // for event handlers with a null handler.
+ var eventConverter = AvaloniaTypeConverters.GetTypeConverter(typeof(EventInfo));
+
+ if (eventConverter != null)
+ {
+ result = new TypeConverterAttribute(eventConverter);
+ }
+ }
if (result == null)
{
@@ -68,4 +80,4 @@ namespace Avalonia.Markup.Xaml.PortableXaml
private readonly MemberInfo _info;
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
index 9a493a85c0..326db884a6 100644
--- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
@@ -1,21 +1,48 @@
-using Avalonia.Data;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Avalonia.Data;
using Avalonia.Markup.Xaml.Context;
-using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Markup.Xaml.Styling;
using Portable.Xaml;
-using Portable.Xaml.ComponentModel;
-using System.ComponentModel;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
namespace Avalonia.Markup.Xaml.PortableXaml
{
internal class AvaloniaXamlSchemaContext : XamlSchemaContext
{
- public bool IsDesignMode { get; set; }
+ private static AvaloniaXamlSchemaContext s_instance;
+ private static AvaloniaXamlSchemaContext s_designInstance;
+
+ public static AvaloniaXamlSchemaContext Instance
+ {
+ get
+ {
+ if (s_instance == null)
+ {
+ s_instance = Create();
+ }
+
+ return s_instance;
+ }
+ }
+
+ public static AvaloniaXamlSchemaContext DesignInstance
+ {
+ get
+ {
+ if (s_designInstance == null)
+ {
+ s_designInstance = Create();
+ s_designInstance.IsDesignMode = true;
+ }
+
+ return s_designInstance;
+ }
+ }
+
+ public bool IsDesignMode { get; private set; }
public static AvaloniaXamlSchemaContext Create(IRuntimeTypeProvider typeProvider = null)
{
return new AvaloniaXamlSchemaContext(typeProvider ?? new AvaloniaRuntimeTypeProvider());
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
index 2194223cb7..10cf716912 100644
--- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlType.cs
@@ -385,4 +385,4 @@ namespace Avalonia.Markup.Xaml.PortableXaml
{
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Markup/Avalonia.Markup/Data/RelativeSource.cs b/src/Markup/Avalonia.Markup/Data/RelativeSource.cs
index 369bd8112c..ac974e3bda 100644
--- a/src/Markup/Avalonia.Markup/Data/RelativeSource.cs
+++ b/src/Markup/Avalonia.Markup/Data/RelativeSource.cs
@@ -85,9 +85,9 @@ namespace Avalonia.Data
get { return _ancestorLevel; }
set
{
- if (_ancestorLevel <= 0)
+ if (value <= 0)
{
- throw new ArgumentOutOfRangeException("AncestorLevel may not be set to less than 1.");
+ throw new ArgumentOutOfRangeException(nameof(value), "AncestorLevel may not be set to less than 1.");
}
_ancestorLevel = value;
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs
index 8d1216e1dc..8137ac3f48 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs
+++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorParser.cs
@@ -50,10 +50,10 @@ namespace Avalonia.Markup.Parsers
{
case SelectorGrammar.OfTypeSyntax ofType:
- result = result.OfType(_typeResolver(ofType.Xmlns, ofType.TypeName));
+ result = result.OfType(Resolve(ofType.Xmlns, ofType.TypeName));
break;
case SelectorGrammar.IsSyntax @is:
- result = result.Is(_typeResolver(@is.Xmlns, @is.TypeName));
+ result = result.Is(Resolve(@is.Xmlns, @is.TypeName));
break;
case SelectorGrammar.ClassSyntax @class:
result = result.Class(@class.Class);
@@ -113,5 +113,18 @@ namespace Avalonia.Markup.Parsers
return result;
}
+
+ private Type Resolve(string xmlns, string typeName)
+ {
+ var result = _typeResolver(xmlns, typeName);
+
+ if (result == null)
+ {
+ var type = string.IsNullOrWhiteSpace(xmlns) ? typeName : xmlns + ':' + typeName;
+ throw new InvalidOperationException($"Could not resolve type '{type}'");
+ }
+
+ return result;
+ }
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
index e17279013d..87b235dce7 100644
--- a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Up_Opens_MenuItem_With_SubMenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var item = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var e = new KeyEventArgs { Key = Key.Up, Source = item };
@@ -27,7 +27,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Down_Opens_MenuItem_With_SubMenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var item = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var e = new KeyEventArgs { Key = Key.Down, Source = item };
@@ -41,7 +41,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Right_Selects_Next_MenuItem()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of(x => x.MoveSelection(NavigationDirection.Right, true) == true);
var item = Mock.Of(x => x.IsTopLevel == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Right, Source = item };
@@ -55,7 +55,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Left_Selects_Previous_MenuItem()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of(x => x.MoveSelection(NavigationDirection.Left, true) == true);
var item = Mock.Of(x => x.IsTopLevel == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Left, Source = item };
@@ -69,7 +69,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Enter_On_Item_With_No_SubMenu_Causes_Click()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var item = Mock.Of(x => x.IsTopLevel == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Enter, Source = item };
@@ -84,7 +84,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Enter_On_Item_With_SubMenu_Opens_SubMenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var item = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Enter, Source = item };
@@ -99,7 +99,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Escape_Closes_Parent_Menu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var item = Mock.Of(x => x.IsTopLevel == true && x.Parent == menu);
var e = new KeyEventArgs { Key = Key.Escape, Source = item };
@@ -113,7 +113,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerEnter_Opens_Item_When_Old_Item_Is_Open()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock();
var item = Mock.Of(x =>
x.IsSubMenuOpen == true &&
@@ -141,7 +141,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerLeave_Deselects_Item_When_Menu_Not_Open()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock();
var item = Mock.Of(x => x.IsTopLevel == true && x.Parent == menu.Object);
var e = new PointerEventArgs { RoutedEvent = MenuItem.PointerLeaveItemEvent, Source = item };
@@ -156,7 +156,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerLeave_Doesnt_Deselect_Item_When_Menu_Open()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock();
var item = Mock.Of(x => x.IsTopLevel == true && x.Parent == menu.Object);
var e = new PointerEventArgs { RoutedEvent = MenuItem.PointerLeaveItemEvent, Source = item };
@@ -175,7 +175,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Up_Selects_Previous_MenuItem()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of(x => x.Parent == parentItem);
var e = new KeyEventArgs { Key = Key.Up, Source = item };
@@ -189,7 +189,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Down_Selects_Next_MenuItem()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of(x => x.Parent == parentItem);
var e = new KeyEventArgs { Key = Key.Down, Source = item };
@@ -203,7 +203,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Left_Closes_Parent_SubMenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of(x => x.HasSubMenu == true && x.IsSubMenuOpen == true);
var item = Mock.Of(x => x.Parent == parentItem);
var e = new KeyEventArgs { Key = Key.Left, Source = item };
@@ -218,7 +218,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Right_With_SubMenu_Items_Opens_SubMenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of(x => x.Parent == parentItem && x.HasSubMenu == true);
var e = new KeyEventArgs { Key = Key.Right, Source = item };
@@ -233,7 +233,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Right_On_TopLevel_Child_Navigates_TopLevel_Selection()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = new Mock();
var parentItem = Mock.Of(x =>
x.IsSubMenuOpen == true &&
@@ -263,7 +263,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Enter_On_Item_With_No_SubMenu_Causes_Click()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem);
@@ -279,7 +279,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Enter_On_Item_With_SubMenu_Opens_SubMenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of(x => x.Parent == parentItem && x.HasSubMenu == true);
var e = new KeyEventArgs { Key = Key.Enter, Source = item };
@@ -294,7 +294,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void Escape_Closes_Parent_MenuItem()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true);
var item = Mock.Of(x => x.Parent == parentItem);
var e = new KeyEventArgs { Key = Key.Escape, Source = item };
@@ -309,7 +309,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerEnter_Selects_Item()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem);
@@ -325,7 +325,7 @@ namespace Avalonia.Controls.UnitTests.Platform
public void PointerEnter_Opens_Submenu_After_Delay()
{
var timer = new TestTimer();
- var target = new DefaultMenuInteractionHandler(null, timer.RunOnce);
+ var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem && x.HasSubMenu == true);
@@ -344,7 +344,7 @@ namespace Avalonia.Controls.UnitTests.Platform
public void PointerEnter_Closes_Sibling_Submenu_After_Delay()
{
var timer = new TestTimer();
- var target = new DefaultMenuInteractionHandler(null, timer.RunOnce);
+ var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem);
@@ -365,7 +365,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerLeave_Deselects_Item()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem);
@@ -381,7 +381,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerLeave_Doesnt_Deselect_Sibling()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem);
@@ -398,7 +398,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerLeave_Doesnt_Deselect_Item_If_Pointer_Over_Submenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem && x.HasSubMenu == true && x.IsPointerOverSubMenu == true);
@@ -413,7 +413,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerReleased_On_Item_With_No_SubMenu_Causes_Click()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem);
@@ -430,7 +430,7 @@ namespace Avalonia.Controls.UnitTests.Platform
public void Selection_Is_Correct_When_Pointer_Temporarily_Exits_Item_To_Select_SubItem()
{
var timer = new TestTimer();
- var target = new DefaultMenuInteractionHandler(null, timer.RunOnce);
+ var target = new DefaultMenuInteractionHandler(false, null, timer.RunOnce);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem && x.HasSubMenu == true);
@@ -467,7 +467,7 @@ namespace Avalonia.Controls.UnitTests.Platform
[Fact]
public void PointerPressed_On_Item_With_SubMenu_Causes_Opens_Submenu()
{
- var target = new DefaultMenuInteractionHandler();
+ var target = new DefaultMenuInteractionHandler(false);
var menu = Mock.Of();
var parentItem = Mock.Of(x => x.IsTopLevel == true && x.HasSubMenu == true && x.Parent == menu);
var item = Mock.Of(x => x.Parent == parentItem && x.HasSubMenu == true);
@@ -481,6 +481,23 @@ namespace Avalonia.Controls.UnitTests.Platform
}
}
+ public class ContextMenu
+ {
+ [Fact]
+ public void Down_Selects_Selects_First_MenuItem_When_No_Selection()
+ {
+ var target = new DefaultMenuInteractionHandler(true);
+ var contextMenu = Mock.Of(x => x.MoveSelection(NavigationDirection.Down, true) == true);
+ var e = new KeyEventArgs { Key = Key.Down, Source = contextMenu };
+
+ target.Attach(contextMenu);
+ target.KeyDown(contextMenu, e);
+
+ Mock.Get(contextMenu).Verify(x => x.MoveSelection(NavigationDirection.Down, true));
+ Assert.True(e.Handled);
+ }
+ }
+
private class TestTimer
{
private Action _action;
diff --git a/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs b/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs
new file mode 100644
index 0000000000..05224c2495
--- /dev/null
+++ b/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs
@@ -0,0 +1,16 @@
+using Avalonia.Controls.Shapes;
+using Xunit;
+
+namespace Avalonia.Controls.UnitTests.Shapes
+{
+ public class PathTests
+ {
+ [Fact]
+ public void Path_With_Null_Data_Does_Not_Throw_On_Measure()
+ {
+ var target = new Path();
+
+ target.Measure(Size.Infinity);
+ }
+ }
+}
diff --git a/tests/Avalonia.DesignerSupport.Tests/RemoteProtocolTests.cs b/tests/Avalonia.DesignerSupport.Tests/RemoteProtocolTests.cs
index e5a477cc32..4264559ab6 100644
--- a/tests/Avalonia.DesignerSupport.Tests/RemoteProtocolTests.cs
+++ b/tests/Avalonia.DesignerSupport.Tests/RemoteProtocolTests.cs
@@ -9,6 +9,7 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Remote.Protocol;
+using Avalonia.Remote.Protocol.Designer;
using Avalonia.Remote.Protocol.Viewport;
using Xunit;
@@ -109,6 +110,16 @@ namespace Avalonia.DesignerSupport.Tests
return Guid.NewGuid().ToString();
if (t == typeof(Guid))
return Guid.NewGuid();
+ if (t == typeof(Exception))
+ return new Exception("Here");
+ if (t == typeof(ExceptionDetails))
+ return new ExceptionDetails
+ {
+ ExceptionType = "Exception",
+ LineNumber = 5,
+ LinePosition = 6,
+ Message = "Here",
+ };
throw new Exception($"Doesn't know how to fabricate a random value for {t}, path {pathInfo}");
}
diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorParserTests.cs b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorParserTests.cs
index dba4573473..1b1a96a7e2 100644
--- a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorParserTests.cs
+++ b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorParserTests.cs
@@ -1,8 +1,6 @@
using System;
using Avalonia.Controls;
-using Avalonia.Controls.Primitives;
using Avalonia.Markup.Parsers;
-using Avalonia.Styling;
using Xunit;
namespace Avalonia.Markup.UnitTests.Parsers
@@ -15,5 +13,19 @@ namespace Avalonia.Markup.UnitTests.Parsers
var target = new SelectorParser((ns, type) => typeof(TextBlock));
var result = target.Parse("TextBlock[IsPointerOver=True]");
}
+
+ [Fact]
+ public void Throws_If_OfType_Type_Not_Found()
+ {
+ var target = new SelectorParser((ns, type) => null);
+ Assert.Throws(() => target.Parse("NotFound"));
+ }
+
+ [Fact]
+ public void Throws_If_Is_Type_Not_Found()
+ {
+ var target = new SelectorParser((ns, type) => null);
+ Assert.Throws(() => target.Parse(":is(NotFound)"));
+ }
}
}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/EventTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/EventTests.cs
new file mode 100644
index 0000000000..6ea3015138
--- /dev/null
+++ b/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 = @"";
+ 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 = @"";
+ var loader = new AvaloniaXamlLoader();
+ var target = new MyButton();
+
+ Assert.Throws(() => loader.Load(xaml, rootInstance: target));
+ }
+
+ [Fact]
+ public void Exception_Is_Not_Thrown_If_Event_Not_Found_In_Design_Mode()
+ {
+ var xaml = @"";
+ 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;
+ }
+ }
+ }
+}
diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs
index 2060cc7170..6a1f08a384 100644
--- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs
@@ -61,6 +61,19 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
Assert.Equal(1, bitmap.RefCount);
}
+ [Fact]
+ public void HitTest_On_Geometry_Node_With_Zero_Transform_Does_Not_Throw()
+ {
+ var geometry = Mock.Of();
+ var geometryNode = new GeometryNode(
+ new Matrix(),
+ Brushes.Black,
+ null,
+ geometry);
+
+ geometryNode.HitTest(new Point());
+ }
+
private class TestDrawOperation : DrawOperation
{
public TestDrawOperation(Rect bounds, Matrix transform, Pen pen)