[Xaml[C]] Allow to connect to static event handler (#5119)
authorAkihiko Odaki <akihiko.odaki.4i@stu.hosei.ac.jp>
Fri, 3 May 2019 18:25:03 +0000 (03:25 +0900)
committerStephane Delcroix <stephane@delcroix.org>
Fri, 3 May 2019 18:25:03 +0000 (20:25 +0200)
Xamarin.Forms.Build.Tasks/SetPropertiesVisitor.cs
Xamarin.Forms.Xaml.UnitTests/EventsConnection.xaml
Xamarin.Forms.Xaml.UnitTests/EventsConnection.xaml.cs
Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs

index 59a4736..2e7338a 100644 (file)
@@ -937,14 +937,6 @@ namespace Xamarin.Forms.Build.Tasks
                        var value = ((ValueNode)valueNode).Value;
 
                        yield return Create(Ldloc, parent);
-                       if (context.Root is VariableDefinition)
-                               foreach (var instruction in (context.Root as VariableDefinition).LoadAs(adder.Parameters[0].ParameterType.ResolveGenericParameters(adder), module))
-                                       yield return instruction;
-                       else if (context.Root is FieldDefinition) {
-                               yield return Create(Ldarg_0);
-                               yield return Create(Ldfld, context.Root as FieldDefinition);
-                       } else 
-                               throw new InvalidProgramException();
                        var declaringType = context.Body.Method.DeclaringType;
                        while (declaringType.IsNested)
                                declaringType = declaringType.DeclaringType;
@@ -965,15 +957,29 @@ namespace Xamarin.Forms.Build.Tasks
                                                throw new XamlParseException($"Signature (parameter {i}) of EventHandler \"{context.Body.Method.DeclaringType.FullName}.{value}\" doesn't match the event type", iXmlLineInfo);
                        //TODO check generic parameters if any
 
+                       //FIXME: eventually get the right ctor instead fo the First() one, just in case another one could exists (not even sure it's possible).
+                       var ctor = module.ImportReference(eventinfo.EventType.ResolveCached().GetConstructors().First());
+                       ctor = ctor.ResolveGenericParameters(eventinfo.EventType, module);
+
+                       if (handler.IsStatic) {
+                               yield return Create(Ldnull);
+                       } else {
+                               if (context.Root is VariableDefinition)
+                                       foreach (var instruction in (context.Root as VariableDefinition).LoadAs(ctor.Parameters[0].ParameterType.ResolveGenericParameters(ctor), module))
+                                               yield return instruction;
+                               else if (context.Root is FieldDefinition) {
+                                       yield return Create(Ldarg_0);
+                                       yield return Create(Ldfld, context.Root as FieldDefinition);
+                               } else 
+                                       throw new InvalidProgramException();
+                       }
+
                        if (handler.IsVirtual) {
                                yield return Create(Ldarg_0);
                                yield return Create(Ldvirtftn, handler);
                        } else
                                yield return Create(Ldftn, handler);
 
-                       //FIXME: eventually get the right ctor instead fo the First() one, just in case another one could exists (not even sure it's possible).
-                       var ctor = module.ImportReference(eventinfo.EventType.ResolveCached().GetConstructors().First());
-                       ctor = ctor.ResolveGenericParameters(eventinfo.EventType, module);
                        yield return Create(Newobj, module.ImportReference(ctor));
                        //Check if the handler has the same signature as the ctor (it should)
                        yield return Create(Callvirt, module.ImportReference(adder));
index 7e48e1b..6fcba95 100644 (file)
@@ -14,5 +14,7 @@
                                                                Clicked="HandleClickedPrivateAsync" />
                <local:ElementWithEvent x:Name="elementWithVirtualHandler"
                                                                Clicked="HandleVirtualClicked" />
+               <local:ElementWithEvent x:Name="elementWithStaticHandler"
+                                                               Clicked="HandleStaticClicked" />
        </StackLayout>
 </local:BaseForEvents>
\ No newline at end of file
index e32788a..228923e 100644 (file)
@@ -77,6 +77,16 @@ namespace Xamarin.Forms.Xaml.UnitTests
                        baseForVirtualClicked++;
                }
 
+               protected static int staticClicked;
+
+               // This is necessary because the interpreter searches the class
+               // specified by x:Class for a static method.
+               // See: https://github.com/xamarin/Xamarin.Forms/issues/5100
+               static void HandleStaticClicked(object sender, EventArgs e)
+               {
+                       staticClicked++;
+               }
+
                [TestFixture]
                public class Tests
                {
@@ -131,6 +141,23 @@ namespace Xamarin.Forms.Xaml.UnitTests
                                Assert.AreEqual(0, layout.baseForVirtualClicked);
                                Assert.AreEqual(1, layout.overrideClicked);
                        }
+
+                       [TestCase(false)]
+                       [TestCase(true)]
+                       public void TestStaticHandler(bool useCompiledXaml)
+                       {
+                               try
+                               {
+                                       var layout = new SubForEvents(useCompiledXaml);
+                                       Assert.AreEqual(0, staticClicked);
+                                       layout.elementWithStaticHandler.SendClicked();
+                                       Assert.AreEqual(1, staticClicked);
+                               }
+                               finally
+                               {
+                                       staticClicked = 0;
+                               }
+                       }
                }
        }
 
@@ -151,5 +178,13 @@ namespace Xamarin.Forms.Xaml.UnitTests
 #pragma warning restore 1998
                {
                }
+
+               // This is necessary because the interpreter searches the subclass
+               // for a static method.
+               // See: https://github.com/xamarin/Xamarin.Forms/issues/5100
+               static void HandleStaticClicked(object sender, EventArgs e)
+               {
+                       staticClicked++;
+               }
        }
 }
\ No newline at end of file
index ca4d945..fa0a84f 100644 (file)
@@ -413,7 +413,7 @@ namespace Xamarin.Forms.Xaml
                        }
 
                        try {
-                               eventInfo.AddEventHandler(element, methodInfo.CreateDelegate(eventInfo.EventHandlerType, rootElement));
+                               eventInfo.AddEventHandler(element, methodInfo.CreateDelegate(eventInfo.EventHandlerType, methodInfo.IsStatic ? null : rootElement));
                                return true;
                        } catch (ArgumentException ae) {
                                exception = new XamlParseException($"Method {stringValue} does not have the correct signature", lineInfo, ae);