diff --git a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs index 6dca479d38..f4d9808d38 100644 --- a/src/Avalonia.DesignerSupport/DesignWindowLoader.cs +++ b/src/Avalonia.DesignerSupport/DesignWindowLoader.cs @@ -18,7 +18,7 @@ namespace Avalonia.DesignerSupport Control control; using (PlatformManager.DesignerMode()) { - var loader = new AvaloniaXamlLoader(); + var loader = new AvaloniaXamlLoader() {IsDesignMode = true}; var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml)); diff --git a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs index 473b97b51a..c89be32057 100644 --- a/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs +++ b/src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs @@ -21,6 +21,12 @@ namespace Avalonia.Markup.Xaml { private readonly AvaloniaXamlSchemaContext _context = GetContext(); + public bool IsDesignMode + { + get => _context.IsDesignMode; + set => _context.IsDesignMode = value; + } + private static AvaloniaXamlSchemaContext GetContext() { var result = AvaloniaLocator.Current.GetService(); @@ -200,7 +206,7 @@ namespace Avalonia.Markup.Xaml internal static object LoadFromReader(XamlReader reader, AvaloniaXamlContext context = null, IAmbientProvider parentAmbientProvider = null) { var writer = AvaloniaXamlObjectWriter.Create( - reader.SchemaContext, + (AvaloniaXamlSchemaContext)reader.SchemaContext, context, parentAmbientProvider); @@ -234,4 +240,4 @@ namespace Avalonia.Markup.Xaml public static T Parse(string xaml, Assembly localAssembly = null) => (T)Parse(xaml, localAssembly); } -} \ No newline at end of file +} diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs index 240ca291a8..5d1a98f6f8 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs @@ -4,13 +4,27 @@ using Portable.Xaml.ComponentModel; using System.ComponentModel; using System.Collections.Generic; using System.Linq; +using System.Reflection; +using Avalonia.Controls; +using Portable.Xaml.Schema; namespace Avalonia.Markup.Xaml.PortableXaml { - public class AvaloniaXamlObjectWriter : XamlObjectWriter + class AvaloniaXamlObjectWriter : XamlObjectWriter { + private static Dictionary DesignDirectives = new Dictionary + { + ["DataContext"] = "DataContext", + ["DesignWidth"] = "Width", ["DesignHeight"] = "Height", ["PreviewWith"] = "PreviewWith" + } + .ToDictionary(p => new XamlDirective( + new[] {"http://schemas.microsoft.com/expression/blend/2008"}, p.Key, + XamlLanguage.Object, null, AllowedMemberLocations.Attribute), p => p.Value); + + private readonly AvaloniaXamlSchemaContext _schemaContext; + public static AvaloniaXamlObjectWriter Create( - XamlSchemaContext schemaContext, + AvaloniaXamlSchemaContext schemaContext, AvaloniaXamlContext context, IAmbientProvider parentAmbientProvider = null) { @@ -34,13 +48,14 @@ namespace Avalonia.Markup.Xaml.PortableXaml private AvaloniaNameScope _nameScope; private AvaloniaXamlObjectWriter( - XamlSchemaContext schemaContext, + AvaloniaXamlSchemaContext schemaContext, XamlObjectWriterSettings settings, AvaloniaNameScope nameScope, IAmbientProvider parentAmbientProvider) : base(schemaContext, settings, parentAmbientProvider) { _nameScope = nameScope; + _schemaContext = schemaContext; } protected override void Dispose(bool disposing) @@ -122,6 +137,20 @@ namespace Avalonia.Markup.Xaml.PortableXaml (value as Avalonia.ISupportInitialize)?.EndInit(); } + public override void WriteStartMember(XamlMember property) + { + foreach(var d in DesignDirectives) + if (property == d.Key && _schemaContext.IsDesignMode) + { + base.WriteStartMember(new XamlMember(d.Value, + typeof(Design).GetMethod("Get" + d.Value, BindingFlags.Static | BindingFlags.Public), + typeof(Design).GetMethod("Set" + d.Value, BindingFlags.Static | BindingFlags.Public), + SchemaContext)); + return; + } + base.WriteStartMember(property); + } + private class DelayedValuesHelper { private int _cnt; @@ -225,4 +254,4 @@ namespace Avalonia.Markup.Xaml.PortableXaml } } } -} \ 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 70402aa764..2d6e046f51 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs @@ -15,6 +15,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml { internal class AvaloniaXamlSchemaContext : XamlSchemaContext { + public bool IsDesignMode { get; set; } public static AvaloniaXamlSchemaContext Create(IRuntimeTypeProvider typeProvider = null) { return new AvaloniaXamlSchemaContext(typeProvider ?? new AvaloniaRuntimeTypeProvider()); @@ -280,5 +281,20 @@ namespace Avalonia.Markup.Xaml.PortableXaml return $"{MemberType}:{Type.Namespace}:{Type.Name}.{Member}"; } } + + + public override bool TryGetCompatibleXamlNamespace(string xamlNamespace, out string compatibleNamespace) + { + //Forces XamlXmlReader to not ignore our namespace in design mode if mc:Ignorable is set + if (IsDesignMode && + xamlNamespace == "http://schemas.microsoft.com/expression/blend/2008") + { + compatibleNamespace = xamlNamespace; + return true; + } + + return base.TryGetCompatibleXamlNamespace(xamlNamespace, out compatibleNamespace); + } + } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs index 6777171e41..2e67541c1f 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs @@ -924,6 +924,45 @@ do we need it?")] } } + [Fact] + public void Design_Mode_Properties_Should_Be_Ignored_At_Runtime_And_Set_In_Design_Mode() + { + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + { + var xaml = @" + + +"; + foreach (var designMode in new[] {true, false}) + { + var loader = new AvaloniaXamlLoader {IsDesignMode = designMode}; + var obj = (Window)loader.Load(xaml); + var context = Design.GetDataContext(obj); + var width = Design.GetWidth(obj); + var height = Design.GetHeight(obj); + if (designMode) + { + Assert.Equal("data-context", context); + Assert.Equal(123, width); + Assert.Equal(321, height); + } + else + { + Assert.False(obj.IsSet(Design.DataContextProperty)); + Assert.False(obj.IsSet(Design.WidthProperty)); + Assert.False(obj.IsSet(Design.HeightProperty)); + } + } + } + } + private class SelectedItemsViewModel : INotifyPropertyChanged { public string[] Items { get; set; } @@ -952,4 +991,4 @@ do we need it?")] public static string GetFoo(AvaloniaObject target) => (string)target.GetValue(FooProperty); } -} \ No newline at end of file +}