From c8363ddeb715efafb41e6f8eebf31b8d61bd0433 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 31 Jan 2019 11:05:19 +0100 Subject: [PATCH 01/23] Supply more detailed exception details. So we can display better errors in a designer. --- .../Remote/RemoteDesignerEntryPoint.cs | 12 +++++++++++- src/Avalonia.Remote.Protocol/DesignMessages.cs | 8 ++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs index 09196e4fb7..67a93f3c9c 100644 --- a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs +++ b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs @@ -8,6 +8,7 @@ using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Designer; using Avalonia.Remote.Protocol.Viewport; using Avalonia.Threading; +using Portable.Xaml; namespace Avalonia.DesignerSupport.Remote { @@ -204,9 +205,18 @@ namespace Avalonia.DesignerSupport.Remote } catch (Exception e) { + var xamlException = e as XamlException; + s_transport.Send(new UpdateXamlResultMessage { - Error = e.ToString() + Error = e.ToString(), + Exception = new ExceptionDetails + { + ExceptionType = e.GetType().FullName, + Message = e.Message.ToString(), + LineNumber = xamlException?.LineNumber, + LinePosition = xamlException?.LinePosition, + } }); } } diff --git a/src/Avalonia.Remote.Protocol/DesignMessages.cs b/src/Avalonia.Remote.Protocol/DesignMessages.cs index f70bcef6b3..5ff16c574d 100644 --- a/src/Avalonia.Remote.Protocol/DesignMessages.cs +++ b/src/Avalonia.Remote.Protocol/DesignMessages.cs @@ -15,6 +15,7 @@ namespace Avalonia.Remote.Protocol.Designer { public string Error { get; set; } public string Handle { get; set; } + public ExceptionDetails Exception { get; set; } } [AvaloniaRemoteMessageGuid("854887CF-2694-4EB6-B499-7461B6FB96C7")] @@ -23,4 +24,11 @@ namespace Avalonia.Remote.Protocol.Designer public string SessionId { get; set; } } + public class ExceptionDetails + { + public string ExceptionType { get; set; } + public string Message { get; set; } + public int? LineNumber { get; set; } + public int? LinePosition { get; set; } + } } From 8caa0d2924aa91b4cab6b1e0673d02e4c8c863f5 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 31 Jan 2019 11:06:10 +0100 Subject: [PATCH 02/23] Strong name sign Avalonia.Remote.Protocol. It needs to be signed to be used in VS. --- .../Avalonia.Remote.Protocol.csproj | 2 ++ src/Avalonia.Remote.Protocol/Key.snk | Bin 0 -> 596 bytes 2 files changed, 2 insertions(+) create mode 100644 src/Avalonia.Remote.Protocol/Key.snk diff --git a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj index 871c9cd995..6684772bfe 100644 --- a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj +++ b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj @@ -2,6 +2,8 @@ netstandard2.0 AVALONIA_REMOTE_PROTOCOL;$(DefineConstants) + true + Key.snk diff --git a/src/Avalonia.Remote.Protocol/Key.snk b/src/Avalonia.Remote.Protocol/Key.snk new file mode 100644 index 0000000000000000000000000000000000000000..d6cbc36f52ccda288283effe3294eac82a5c7ef7 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097%7Y|vta8^3KR$Smx;K{fFS_SHi*Psw3 zJkx=j;*YrY^86pNz5vg5O~4U_MzBsH{`RYL9%T;+mhYMfmF$a-hma@g6C=)}fn}{@ zI6+_-6SrY={k8i>Zro$$X_F^INltv6z|DqkHoVCrb@d@)Ynl ziDFw_@9Kk?>cNGZ zkCmu~lKW60E{OV*o5~ku8U4_BVgA~yhp_&l8GDAo5|W{$&Z?`@AZ@_1&~jq@i>hZp zfr$zNo<&wCV%9Y16^ult`xUXBU{dwnHju#G_+DC2%24bQ6(nW*Ru#AksAm} zVrGJ%!vumsmQv4#B#c13373GWA)F8F&Jcl|4?#CU|8T13H{?NW1^lSD_)<`0%4AFb zT{Zy{*o=br4QtNUt*gZX42(6>YX3sMl5R$?_iRW)5tpm!JF-;s%CmTExp4_Y))=1A z5!R5C0%+Tm4TlE_P);~#r&{t`3qauN`OdjpTZIT$iHJqcKU?P+59wR{R01ve4D1B~ zFg7f2wt{GtEMYmt)ZZ)c>-o!;2RLaY_uLW_ zs%cQ6S|W66#6C;m>k<*}q>-#musb}vAeE695xEBO7zyW1c9{!H`NE?Kb$f6CtyJ66 iSF5$`(ApW(ItQ!B9xp{O{xF;%!Gd-<-CHQkxaEbh4H%~Y literal 0 HcmV?d00001 From 718c01f206c43214238f9cdf96979a733dc78f6c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 31 Jan 2019 11:05:19 +0100 Subject: [PATCH 03/23] Supply more detailed exception details. So we can display better errors in a designer. --- .../Remote/RemoteDesignerEntryPoint.cs | 12 +++++++++++- src/Avalonia.Remote.Protocol/DesignMessages.cs | 8 ++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs index 09196e4fb7..67a93f3c9c 100644 --- a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs +++ b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs @@ -8,6 +8,7 @@ using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Designer; using Avalonia.Remote.Protocol.Viewport; using Avalonia.Threading; +using Portable.Xaml; namespace Avalonia.DesignerSupport.Remote { @@ -204,9 +205,18 @@ namespace Avalonia.DesignerSupport.Remote } catch (Exception e) { + var xamlException = e as XamlException; + s_transport.Send(new UpdateXamlResultMessage { - Error = e.ToString() + Error = e.ToString(), + Exception = new ExceptionDetails + { + ExceptionType = e.GetType().FullName, + Message = e.Message.ToString(), + LineNumber = xamlException?.LineNumber, + LinePosition = xamlException?.LinePosition, + } }); } } diff --git a/src/Avalonia.Remote.Protocol/DesignMessages.cs b/src/Avalonia.Remote.Protocol/DesignMessages.cs index f70bcef6b3..5ff16c574d 100644 --- a/src/Avalonia.Remote.Protocol/DesignMessages.cs +++ b/src/Avalonia.Remote.Protocol/DesignMessages.cs @@ -15,6 +15,7 @@ namespace Avalonia.Remote.Protocol.Designer { public string Error { get; set; } public string Handle { get; set; } + public ExceptionDetails Exception { get; set; } } [AvaloniaRemoteMessageGuid("854887CF-2694-4EB6-B499-7461B6FB96C7")] @@ -23,4 +24,11 @@ namespace Avalonia.Remote.Protocol.Designer public string SessionId { get; set; } } + public class ExceptionDetails + { + public string ExceptionType { get; set; } + public string Message { get; set; } + public int? LineNumber { get; set; } + public int? LinePosition { get; set; } + } } From f32832878e9729a50dbaba071440722aa93a411c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 31 Jan 2019 11:06:10 +0100 Subject: [PATCH 04/23] Strong name sign Avalonia.Remote.Protocol. It needs to be signed to be used in VS. --- .../Avalonia.Remote.Protocol.csproj | 2 ++ src/Avalonia.Remote.Protocol/Key.snk | Bin 0 -> 596 bytes 2 files changed, 2 insertions(+) create mode 100644 src/Avalonia.Remote.Protocol/Key.snk diff --git a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj index 871c9cd995..6684772bfe 100644 --- a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj +++ b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj @@ -2,6 +2,8 @@ netstandard2.0 AVALONIA_REMOTE_PROTOCOL;$(DefineConstants) + true + Key.snk diff --git a/src/Avalonia.Remote.Protocol/Key.snk b/src/Avalonia.Remote.Protocol/Key.snk new file mode 100644 index 0000000000000000000000000000000000000000..d6cbc36f52ccda288283effe3294eac82a5c7ef7 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097%7Y|vta8^3KR$Smx;K{fFS_SHi*Psw3 zJkx=j;*YrY^86pNz5vg5O~4U_MzBsH{`RYL9%T;+mhYMfmF$a-hma@g6C=)}fn}{@ zI6+_-6SrY={k8i>Zro$$X_F^INltv6z|DqkHoVCrb@d@)Ynl ziDFw_@9Kk?>cNGZ zkCmu~lKW60E{OV*o5~ku8U4_BVgA~yhp_&l8GDAo5|W{$&Z?`@AZ@_1&~jq@i>hZp zfr$zNo<&wCV%9Y16^ult`xUXBU{dwnHju#G_+DC2%24bQ6(nW*Ru#AksAm} zVrGJ%!vumsmQv4#B#c13373GWA)F8F&Jcl|4?#CU|8T13H{?NW1^lSD_)<`0%4AFb zT{Zy{*o=br4QtNUt*gZX42(6>YX3sMl5R$?_iRW)5tpm!JF-;s%CmTExp4_Y))=1A z5!R5C0%+Tm4TlE_P);~#r&{t`3qauN`OdjpTZIT$iHJqcKU?P+59wR{R01ve4D1B~ zFg7f2wt{GtEMYmt)ZZ)c>-o!;2RLaY_uLW_ zs%cQ6S|W66#6C;m>k<*}q>-#musb}vAeE695xEBO7zyW1c9{!H`NE?Kb$f6CtyJ66 iSF5$`(ApW(ItQ!B9xp{O{xF;%!Gd-<-CHQkxaEbh4H%~Y literal 0 HcmV?d00001 From ba316b89333cf0ebffcdd940f0facd3756510990 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 1 Feb 2019 14:51:44 +0100 Subject: [PATCH 05/23] Updated Portable.Xaml. --- .../Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github index ab55261737..b9f886b93a 160000 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github @@ -1 +1 @@ -Subproject commit ab5526173722b8988bc5ca3c03c8752ce89c0975 +Subproject commit b9f886b93ab28dd69722e72ef8cb6c33889b3749 From 521c9a38414ca4a9e80c9619f23bf1da7460f083 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 1 Feb 2019 18:17:02 +0100 Subject: [PATCH 06/23] Updated Portable.Xaml. To get better line/column info for errors. --- .../Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github index b9f886b93a..452ced4782 160000 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github @@ -1 +1 @@ -Subproject commit b9f886b93ab28dd69722e72ef8cb6c33889b3749 +Subproject commit 452ced47823d963c1e2d0ad809d0327c9e9b5247 From 03e84dea7bf86d5133a9ed79891c24899e0d771a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 13 Feb 2019 13:11:53 +0100 Subject: [PATCH 07/23] Ignore missing events in the designer. Do this by registering a special type converter for EventInfo, and add in the plumbing for Portable.Xaml to use that type converter instead of its own (internal) `EventConverter`. --- .../DesignWindowLoader.cs | 5 ++ .../DesignerEventConverter.cs | 87 +++++++++++++++++++ .../AvaloniaMemberAttributeProvider.cs | 14 ++- .../PortableXaml/AvaloniaXamlType.cs | 2 +- 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 src/Avalonia.DesignerSupport/DesignerEventConverter.cs diff --git a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs index 8fee31408f..2df171295f 100644 --- a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs +++ b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs @@ -12,6 +12,11 @@ namespace Avalonia.DesignerSupport { public class DesignWindowLoader { + static DesignWindowLoader() + { + AvaloniaTypeConverters.Register(typeof(EventInfo), typeof(DesignerEventConverter)); + } + public static Window LoadDesignerWindow(string xaml, string assemblyPath, string xamlFileProjectPath) { Window window; diff --git a/src/Avalonia.DesignerSupport/DesignerEventConverter.cs b/src/Avalonia.DesignerSupport/DesignerEventConverter.cs new file mode 100644 index 0000000000..b420448698 --- /dev/null +++ b/src/Avalonia.DesignerSupport/DesignerEventConverter.cs @@ -0,0 +1,87 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Portable.Xaml; + +namespace Avalonia.DesignerSupport +{ + internal class DesignerEventConverter : 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); + } + } + + // 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(); + } + } + 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/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 +} From 9ed23bbf89158792812965b9f39afeab71314ef9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 13 Feb 2019 16:33:27 +0100 Subject: [PATCH 08/23] Handle null modifiers collection. --- .../Remote/Server/RemoteServerTopLevelImpl.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index e1767fca36..028a78aea4 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -61,6 +61,11 @@ namespace Avalonia.Controls.Remote.Server { var result = InputModifiers.None; + if (modifiers == null) + { + return result; + } + foreach(var modifier in modifiers) { switch (modifier) From 2b34a2789213796454d62a7706dc55f3ddb37a27 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 13 Feb 2019 19:36:33 +0100 Subject: [PATCH 09/23] Return a frame even for an empty control. If the user has a control with no content then we've still got a valid control so return a frame with size 0. --- .../Remote/Server/RemoteServerTopLevelImpl.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index 028a78aea4..3fa0b108ec 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -270,11 +270,15 @@ namespace Avalonia.Controls.Remote.Server var bpp = fmt == ProtocolPixelFormat.Rgb565 ? 2 : 4; var data = new byte[width * height * bpp]; var handle = GCHandle.Alloc(data, GCHandleType.Pinned); + try { - _framebuffer = new LockedFramebuffer(handle.AddrOfPinnedObject(), new PixelSize(width, height), width * bpp, _dpi, (PixelFormat)fmt, - null); - Paint?.Invoke(new Rect(0, 0, width, height)); + if (width > 0 && height > 0) + { + _framebuffer = new LockedFramebuffer(handle.AddrOfPinnedObject(), new PixelSize(width, height), width * bpp, _dpi, (PixelFormat)fmt, + null); + Paint?.Invoke(new Rect(0, 0, width, height)); + } } finally { @@ -306,8 +310,7 @@ namespace Avalonia.Controls.Remote.Server return; } - if (ClientSize.Width < 1 || ClientSize.Height < 1) - return; + var format = ProtocolPixelFormat.Rgba8888; foreach(var fmt in _supportedFormats) if (fmt <= ProtocolPixelFormat.MaxValue) From 81510372b4a8d445e083b83ac988b8fb772acfbf Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 13 Feb 2019 21:01:26 +0100 Subject: [PATCH 10/23] Don't invalidate a disposed TopLevelImpl. --- .../Embedding/Offscreen/OffscreenTopLevelImpl.cs | 4 +++- .../Remote/Server/RemoteServerTopLevelImpl.cs | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs index d328e1ee88..9c53dc0c10 100644 --- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs @@ -11,11 +11,13 @@ namespace Avalonia.Controls.Embedding.Offscreen { private double _scaling = 1; private Size _clientSize; + public IInputRoot InputRoot { get; private set; } + public bool IsDisposed { get; private set; } public virtual void Dispose() { - //No-op + IsDisposed = true; } public IRenderer CreateRenderer(IRenderRoot root) => new ImmediateRenderer(root); diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index 3fa0b108ec..6293cbfbfd 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -331,8 +331,11 @@ namespace Avalonia.Controls.Remote.Server public override void Invalidate(Rect rect) { - _invalidated = true; - Dispatcher.UIThread.Post(RenderIfNeeded); + if (!IsDisposed) + { + _invalidated = true; + Dispatcher.UIThread.Post(RenderIfNeeded); + } } public override IMouseDevice MouseDevice { get; } = new MouseDevice(); From e3e42e8ae08fb91c8f7a28a3ea858eff709639ef Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 14 Feb 2019 22:24:45 +0100 Subject: [PATCH 11/23] Fix bad XAML in DesignWindowLoader message. --- src/Avalonia.DesignerSupport/DesignWindowLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs index 2df171295f..68feb2edb5 100644 --- a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs +++ b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs @@ -56,8 +56,8 @@ namespace Avalonia.DesignerSupport { new TextBlock {Text = "Styles can't be previewed without Design.PreviewWith. Add"}, new TextBlock {Text = ""}, - new TextBlock {Text = " "}, - new TextBlock {Text = ""}, + new TextBlock {Text = " "}, + new TextBlock {Text = ""}, new TextBlock {Text = "before setters in your first Style"} } }; From 8d9c23446a8d4e7cde8416c913104d41e282508a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 14 Feb 2019 23:35:36 +0100 Subject: [PATCH 12/23] Make Styles inherit from AvaloniaObject. This way we can add attached properties (such as `Design.PreviewWith`) to `Styles`. --- src/Avalonia.Styling/Styling/Styles.cs | 79 +++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Styling/Styling/Styles.cs index 51499b737a..288cf35d08 100644 --- a/src/Avalonia.Styling/Styling/Styles.cs +++ b/src/Avalonia.Styling/Styling/Styles.cs @@ -2,7 +2,9 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Collections; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using Avalonia.Collections; using Avalonia.Controls; @@ -12,16 +14,17 @@ namespace Avalonia.Styling /// /// A style that consists of a number of child styles. /// - public class Styles : AvaloniaList, IStyle, ISetStyleParent + public class Styles : AvaloniaObject, IAvaloniaList, IStyle, ISetStyleParent { private IResourceNode _parent; private IResourceDictionary _resources; + private AvaloniaList _styles = new AvaloniaList(); private Dictionary> _cache; public Styles() { - ResetBehavior = ResetBehavior.Remove; - this.ForEachItem( + _styles.ResetBehavior = ResetBehavior.Remove; + _styles.ForEachItem( x => { if (x.ResourceParent == null && x is ISetStyleParent setParent) @@ -57,9 +60,18 @@ namespace Avalonia.Styling () => { }); } + public event NotifyCollectionChangedEventHandler CollectionChanged + { + add => _styles.CollectionChanged += value; + remove => _styles.CollectionChanged -= value; + } + /// public event EventHandler ResourcesChanged; + /// + public int Count => _styles.Count; + /// public bool HasResources => _resources?.Count > 0 || this.Any(x => x.HasResources); @@ -94,6 +106,19 @@ namespace Avalonia.Styling /// IResourceNode IResourceNode.ResourceParent => _parent; + /// + bool ICollection.IsReadOnly => false; + + /// + IStyle IReadOnlyList.this[int index] => _styles[index]; + + /// + public IStyle this[int index] + { + get => _styles[index]; + set => _styles[index] = value; + } + /// /// Attaches the style to a control if the style's selector matches. /// @@ -172,6 +197,54 @@ namespace Avalonia.Styling return false; } + /// + public void AddRange(IEnumerable items) => _styles.AddRange(items); + + /// + public void InsertRange(int index, IEnumerable items) => _styles.InsertRange(index, items); + + /// + public void Move(int oldIndex, int newIndex) => _styles.Move(oldIndex, newIndex); + + /// + public void MoveRange(int oldIndex, int count, int newIndex) => _styles.MoveRange(oldIndex, count, newIndex); + + /// + public void RemoveAll(IEnumerable items) => _styles.RemoveAll(items); + + /// + public void RemoveRange(int index, int count) => _styles.RemoveRange(index, count); + + /// + public int IndexOf(IStyle item) => _styles.IndexOf(item); + + /// + public void Insert(int index, IStyle item) => _styles.Insert(index, item); + + /// + public void RemoveAt(int index) => _styles.RemoveAt(index); + + /// + public void Add(IStyle item) => _styles.Add(item); + + /// + public void Clear() => _styles.Clear(); + + /// + public bool Contains(IStyle item) => _styles.Contains(item); + + /// + public void CopyTo(IStyle[] array, int arrayIndex) => _styles.CopyTo(array, arrayIndex); + + /// + public bool Remove(IStyle item) => _styles.Remove(item); + + /// + public IEnumerator GetEnumerator() => _styles.GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => _styles.GetEnumerator(); + /// void ISetStyleParent.SetParent(IResourceNode parent) { From c438f9301e8d8a53269d485cdb05f551457afef7 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Feb 2019 00:13:57 +0100 Subject: [PATCH 13/23] Make DesignWindowLoader expect PreviewWith on Styles. --- samples/ControlCatalog/SideBar.xaml | 8 ++++++++ src/Avalonia.Controls/Design.cs | 6 +++--- src/Avalonia.DesignerSupport/DesignWindowLoader.cs | 8 ++++---- 3 files changed, 15 insertions(+), 7 deletions(-) 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 @@ + + + + + + + +