Page 1 of 1

[Unity] Loaded event issues

Posted: 09 Jul 2015, 20:35
by MrHayato
Hi,

I've been pulling my hair out trying to get this to work, but I can't seem to...

The idea:
- A scrollviewer contains a stackpanel
- The stackpanel contains a bunch of items in it.
- I want to know where each item is, relative to the scrollviewer.
- A control contains buttons, with one button per item in the scrollviewer.
- Clicking the buttons will scroll the scrollviewer to the position of the item

So I created an attached property to be attached to any scrollviewer (including a custom one we've built)

I've created a gist with all the attempts I've tried to get this working: https://gist.github.com/MrHayato/64402a3d1ca1139fcd6e

The attached property can be seen in this gist as TrackerA and looks pretty straightforward. However, the debug line only writes out (0,0) as the position for each of the children (even though they're all visible, no bindings).

I then added child.UpdateLayout() to each of the children, thinking perhaps it needs to be measured and arranged. You can see this at TrackerB.cs (line 29). This was a little better, as I was getting non-zero values. However, it seems the event fires twice, and then throws an exception:
Exception: System.ApplicationException: System.ApplicationException: System.ApplicationException: System.ApplicationException: System.ApplicationException: System.ApplicationException: System.ApplicationException: System.ApplicationException: System.ApplicationException: System.ApplicationException: System.ApplicationException: System.ApplicationException: System.ApplicationException: System.ApplicationException: Element is already loaded
  at Noesis.UIElement.UpdateLayout () [0x00015] in D:\Dev\iQmetrix\XQ.Shelf\Un
Noesis.Error.Check () (at Assets/Plugins/NoesisGUI/Scripts/Core/NoesisError.cs:21)
Noesis.UIRenderer.Noesis_UpdateRenderer (Int32 rendererId, Double timeInSeconds) (at Assets/Plugins/NoesisGUI/Scripts/Core/NoesisUIRendererImports.cs:145)
Noesis.UIRenderer.Update (Double timeInSeconds, AntialiasingMode aaMode, TessellationMode tessMode, TessellationQuality tessQuality, RendererFlags flags, Boolean enableMouse, Boolean enableTouch, Boolean enablePostProcess, Boolean flipVertically) (at Assets/Plugins/NoesisGUI/Scripts/Core/NoesisUIRenderer.cs:129)
NoesisGUIPanel.LateUpdate () (at Assets/Plugins/NoesisGUI/Scripts/NoesisGUIPanel.cs:203)
So maybe loaded is getting fired too often? Not sure why, since it should only fire once. So, I'll make sure it is only fired once (TrackerC line 15). However, we are back to the first result, where everything reports 0,0.

What gives? Why is Loaded firing more than once if I call UpdateLayout on its children? And why aren't the correct sizes being reported back without it? I thought the loaded event would fire after it's loaded.

Re: [Unity] Loaded event issues

Posted: 09 Jul 2015, 21:46
by sfernandez
Hi,

The problem is in our side, because Loaded event is raised when element is added to the UI tree, and not after first layout is done as it occurs in WPF. We have a ticket open to fix this: https://trello.com/c/Plh08ybM

Right now, the best approach (to behave similar to WPF) is to hook to the SizeChanged event and check when was the first time the event was fired. In your code will look like this:
    public class NavigationTracker : DependencyObject
    {
        private static readonly IDictionary<NavigationAnchor, NavigationPoints> _navigationLookup =
            new Dictionary<NavigationAnchor, NavigationPoints>();
 
        private static void OnNavigationViewChanged(DependencyObject dependencyObject,
            DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var scrollViewer = dependencyObject as ScrollViewer;
 
            if (scrollViewer == null)
            {
                throw new Exception("NavigationTracker can only be applied on ScrollViewers.");
            }
 
            //Only initialize once..
            var initialized = false;
            scrollViewer.SizeChanged += (sender, args) =>
            {
                if (!initialized)
                {
                    Initialize(scrollViewer);
                    initialized = true;
                }
            };
        }
 
        private static void Initialize(ScrollViewer scrollViewer)
        {
            var panel = scrollViewer.Content as Panel;
 
            if (panel == null)
            {
                throw new Exception("Can only track panels in the scrollviewer");
            }
 
            foreach (var child in panel.Children.Cast<FrameworkElement>())
            {
                var matrix = child.TransformToAncestor(scrollViewer);
                var position = new Point(matrix[3].X, matrix[3].Y);
                Debug.Log(position);
            }
        }
 
        // NOTE: Value type needs to match DependencyProperty type!!! (it was string in your code)
        public static void SetNavigationView(UIElement element, NavigationView value)
        {
            element.SetValue(NavigationViewProperty, value);
        }
 
        public static NavigationView GetNavigationView(UIElement element)
        {
            return (NavigationView)element.GetValue(NavigationViewProperty);
        }
 
        public static DependencyProperty NavigationViewProperty =
            DependencyProperty.RegisterAttached("NavigationView", typeof (NavigationView), typeof (NavigationTracker), new PropertyMetadata(null, OnNavigationViewChanged));
    }
The Loaded event shouldn't be fired twice, even if you call UpdateLayout(). This seems like a bug, I'll take note.

Re: [Unity] Loaded event issues

Posted: 09 Jul 2015, 22:00
by MrHayato
Perfect. This did the trick.

Do you know what the ETA on the Loaded fix would be? I have a feeling we'll encounter this more times later.

Thanks again!

Re: [Unity] Loaded event issues

Posted: 10 Jul 2015, 10:53
by sfernandez
We want to use the work on this ticket to implement the Initialized event too, and get rid of the OnPostInit() function that deviates from WPF API. As this can break customer code we planned to include it in 1.3 release.

We will study the possibility to modify the Loaded event only so it can be included in an upcoming release of 1.2 version.

Re: [Unity] Loaded event issues

Posted: 17 Jul 2015, 13:39
by sfernandez
We finally decided to fix the behavior of Loaded event (now will be fired after element gets ready for render) for the next 1.2.4 release. We also included the missing FrameworkElement.Initialized event.

Re: [Unity] Loaded event issues

Posted: 11 Aug 2015, 20:09
by jsantos
Fixed in v1.2.4