Page 1 of 1

Changing elements in UserControls

Posted: 12 Dec 2014, 03:05
by Orann
If a usercontrol is binding it's internal variables in the method OnPostInit (as is recommended in your documentation), when and where is the best practice for calling methods on that UserControl?

To elaborate:

I have a usercontrol:
<UserControl
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
  x:Class="FullscreenPopupContainer"
  UseLayoutRounding="True" d:DesignWidth="725.167" d:DesignHeight="339.667"
    Margin="1, 4, 0, 0">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="5*"/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="5*"/>
            <RowDefinition />
        </Grid.RowDefinitions>
        <Button Name="BackgroundCloserButton" Opacity="0" Grid.Row="99" Grid.ColumnSpan="99" />
        <Rectangle Fill="Black" Opacity="0.6" Grid.ColumnSpan="99" Grid.RowSpan="99" />
        
        <Grid Grid.Column="1" Grid.Row="1" >
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="28" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <Grid Grid.Row="0">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="60" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>

                    
                    <Grid Grid.Column="1" Grid.Row="0" Background="#FF4E7BF5" />
                    

                    <Path  Data="F0 M0,20 L20,00 100,0 100,100 00,100"
						        StrokeThickness="0" Stroke="White" Fill="#FF4E7BF5"/>
                    
                    <Label Grid.Column="1" Grid.Row="0" Content="Title" Name="TitleLabel" Margin="-30, 2, 0, 0"/>


                    <Border Visibility="Collapsed" BorderThickness="0" Grid.Row="0"
						          CornerRadius="20,0,0,0" Background="#FF4E7BF5"/>

                </Grid>
                <Grid Grid.Row="1">
                    <Border BorderThickness="2, 2, 2, 2" BorderBrush="#FF4E7BF5" />
                    <Grid x:Name="ControlSpace" Margin="2"/>

                </Grid>
            </Grid>
        </Grid>

    </Grid>
</UserControl>
And it's code behind, for unity:
using UnityEngine;
using System.Collections;
using Noesis.UserControls;
using Noesis;
using System;

[Noesis.Extended]
[Noesis.UserControlSource("Assets/GUI/Containers/FullscreenPopupContainer.xaml")]
public class FullscreenPopupContainer : Noesis.UserControl {

    Label titleLabel;
    Grid contentPanel;

    public void Start()
    {

    }

	// Use this for initialization
    public void OnPostInit()
    {
        titleLabel = FindName<Label>("TitleLabel");
        contentPanel = FindName<Grid>("ControlSpace");
        Debug.Log("Post init called");
    }
	
    public void SetLabel(string label)
    {
        titleLabel.SetContent(label);
    }

    public void AddControl(UserControl contentControl)
    {
        contentPanel.GetChildren().Add(contentControl);
    }

}
I want to be able to create a popup over my main window to show a UserControl, with the following method:
public void AddPopup(UserControl control, string title)
    {
        FullscreenPopupContainer popupContainer = NoesisGUISystem.LoadXaml<FullscreenPopupContainer>("Assets/GUI/Containers/FullscreenPopupContainer.xaml");

        popupContainer.Loaded += delegate
        {
            if (control != null) {
                popupContainer.AddControl(control);
            }
            popupContainer.SetLabel(title);
        };
        
        
        contentGrid.GetChildren().Add(popupContainer);
    }
The problem here is that the Loaded event is fired *before* OnPostInit() - I get a null pointer exception setting the label or content as they have not been initialized yet.

It seems that there's no event which is fired just after OnPosInit() is called on the UserControl, giving no native non-databinding way to add content to your control which is created externally.

What am I missing?

It's possible, of course, to add my own event and call it at the end of OnPostInit(), but that doesn't seem right for larger scale systems and feels against the grain of Noesis's intended direction.

Is there a better solution to this problem?

Re: Changing elements in UserControls

Posted: 12 Dec 2014, 23:54
by sfernandez
Hi,

We have to improve this scenario because the Init/Load behavior does not correspond to WPF, and people gets very confused.

In your example, and with the current implementation of NoesisGUI, you need to wait after control is added to the tree to ensure that OnPostInit was called. So your code should be modified as follows to make it work:
    public void AddPopup(UserControl control, string title)
    {
        // Here you can also create the popup calling just the new operator:
        //FullscreenPopupContainer popupContainer = new FullscreenPopupContainer();
        FullscreenPopupContainer popupContainer = NoesisGUISystem.LoadXaml<FullscreenPopupContainer>("Assets/GUI/Containers/FullscreenPopupContainer.xaml");

        contentGrid.GetChildren().Add(popupContainer); // Loaded is called first, then OnPostInit

        if (control != null) {
            popupContainer.AddControl(control);
        }
        popupContainer.SetLabel(title);
    }
Hope this helps.

Regards,
- Sergio