unoplatform / uno

Build Mobile, Desktop and WebAssembly apps with C# and XAML. Today. Open source and professionally supported.
https://platform.uno
Apache License 2.0
8.78k stars 706 forks source link

`LayoutUpdated` event is not fired properly #7002

Open dr1rrb opened 3 years ago

dr1rrb commented 3 years ago

Current behavior

The LayoutUpdated event is fired only on elements that has been [re-]layouted

Expected behavior

More info: https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.frameworkelement.layoutupdated?view=winrt-20348#remarks

How to reproduce it (as minimally and precisely as possible)

In a blank app, in the MainPage constructor, add

new Border().LayoutUpdated += (snd, e) => "".ToString(); 

The event handler is never invoked while it's on UWP!

Workaround

Works on UWP/WinUI

Yes

Environment

Uno.UI / Uno.UI.WebAssembly / Uno.UI.Skia

NuGet package version(s)

3.10.0-dev.632

Affected platforms

iOS, Android, WebAssembly, WebAssembly renderers for Xamarin.Forms, macOS, Skia (WPF), Skia (GTK on Linux/macOS/Windows), Skia (Tizen)

IDE

No response

IDE version

No response

Relevant plugins

No response

Anything else we need to know?

Didn't really validated on all platforms ... just an assumption based of current implementation.

jeromelaban commented 3 years ago

Related to https://github.com/unoplatform/uno/issues/3519

Youssef1313 commented 8 months ago

Wrote two runtime tests for this to prepare after #14936.

        [TestMethod]
        [RunsOnUIThread]
        public async Task When_LayoutUpdated()
        {
            string s = "";
            var button = new Button();
            button.LayoutUpdated += (sender, args) =>
            {
                Assert.IsNull(sender);
                Assert.IsNull(args);
                s += "button1 ";
            };
            button.LayoutUpdated += (sender, args) =>
            {
                Assert.IsNull(sender);
                Assert.IsNull(args);
                s += "button2 ";
            };

            var border = new Border
            {
                Width = 100,
                Height = 100,
                Background = new SolidColorBrush(Colors.Red),
            };

            border.LayoutUpdated += (sender, args) =>
            {
                Assert.IsNull(sender);
                Assert.IsNull(args);
                s += "border1 ";
            };

            border.LayoutUpdated += (sender, args) =>
            {
                Assert.IsNull(sender);
                Assert.IsNull(args);
                s += "border2 ";
            };

            await UITestHelper.Load(border);

            // On WinUI, running the test randomly produces one of these outputs.
            if (s is not ("button1 button2 border1 border2 " or "border1 border2 button1 button2 "))
            {
                Assert.Fail($"Test failed. Actual: {s}");
            }
        }

        [TestMethod]
        [RunsOnUIThread]
        public void When_LayoutUpdated_Element_Can_Get_GCed()
        {
            WeakReference GetButtonWeakReference()
            {
                var button = new Button();
                button.LayoutUpdated += static (_, _) => { };
                return new WeakReference(button);
            }

            var weakRef = GetButtonWeakReference();
            GC.Collect();
            Assert.IsFalse(weakRef.IsAlive);
        }
Youssef1313 commented 3 months ago

Done for Skia and Wasm in #16761