Browse Source

Support for mc:Ignorable + d:DataContext

Nikita Tsukanov 7 years ago
parent
commit
04228b6d03

+ 1 - 1
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));
 
 

+ 8 - 2
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<AvaloniaXamlSchemaContext>();
@@ -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<T>(string xaml, Assembly localAssembly = null)
             => (T)Parse(xaml, localAssembly);
     }
-}
+}

+ 33 - 4
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<XamlDirective, string> DesignDirectives = new Dictionary<string, string>
+            {
+                ["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
             }
         }
     }
-}
+}

+ 17 - 1
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);
+        }
+
     }
-}
+}

+ 40 - 1
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 = @"
+<Window xmlns='https://github.com/avaloniaui' 
+    xmlns:d='http://schemas.microsoft.com/expression/blend/2008'
+    xmlns:mc='http://schemas.openxmlformats.org/markup-compatibility/2006'
+    mc:Ignorable='d'
+    d:DataContext='data-context'
+    d:DesignWidth='123'
+    d:DesignHeight='321'
+>
+
+</Window>";
+                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);
 
     }
-}
+}