Page 1 of 1

Properly showing/hiding a view in Unity with state changes

Posted: 01 Mar 2021, 17:54
by Rocko Bonaparte
I am having some goofy problems reusing my main menu view. The first problem I think was that I was recreating it each time I was trying to show it. So now I'm preserving the setup and just enabling and disabling it. However, there's a second layer to the problem where it looks like button enable states aren't propagating or something that's going to be a hard problem to show in detail and will take some effort to simplify and see what could be wrong. But for now, I was hoping somebody could check my work for showing and hiding a main menu:
    private NoesisXaml mainMenuXaml;
    private NoesisView mainMenuView;

    public void Show()
    {
        if (mainMenuView == null)
        {
            mainMenuView = GlobalState.Instance.MainCamera.gameObject.GetComponent<NoesisView>();
            mainMenuXaml = (NoesisXaml)UnityEngine.Resources.Load(MainMenuXamlPath, typeof(NoesisXaml));

            // I'm keeping this canary here in case something screws up with loading the main menu later. At least
            // this will point to the crux of the problem.
            if (mainMenuXaml == null)
            {
                Debug.LogError("Could not load the main menu XAML from " + MainMenuXamlPath);
            }

            mainMenuView.Xaml = mainMenuXaml;
            mainMenuView.LoadXaml(true);
        }
        else
        {
            mainMenuView.enabled = true;
        }
    }

    public void Hide()
    {
        mainMenuView.enabled = false;
    }

The button enable/disable logic is trickier because that goes between multiple files, involves the view model, and property change notifications. It works in a standalone test but doesn't in Unity. Is the a gotcha with affecting a viewmodel while the view is disabled or something?

Re: Properly showing/hiding a view in Unity with state changes

Posted: 02 Mar 2021, 08:09
by Rocko Bonaparte
I'm concluding I have that part down well enough. The bulk of my problem had to do with RelayCommand implementation, per viewtopic.php?f=3&t=2169&p=12273#p12273

Re: Properly showing/hiding a view in Unity with state changes

Posted: 04 Mar 2021, 07:32
by Rocko Bonaparte
I'm now stuck on a new problem. It looks like if I disable the view, it still gets events. So if I opt for a new game, this will disable the view and start the game. However, if I hit enter on the keyboard, it'll start a new game again despite having disabled the main menu. It's not showing that menu at all.

I also disabled keyboard, mouse, and gamepad, but that doesn't seem to do anything:
        mainMenuView.enabled = false;
        mainMenuView.EnableGamepad = false;
        mainMenuView.EnableMouse = false;
        mainMenuView.EnableKeyboard = false;
I considered it more of a hack regardless, but I'm still left wondering what I really should be doing.

Re: Properly showing/hiding a view in Unity with state changes

Posted: 04 Mar 2021, 11:19
by sfernandez
Hi Rocko,

I don't know if I'm following your setup, but if I understood correctly you are creating a new NoesisView component for each xaml you want to load, is that right?
What is the purpose of that? Do you need to render them to different cameras?

If you just want to load different xamls into the same scene I recommend using a single NoesisView component with a root xaml:
<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <!-- you can place here everything you need -->
</Grid>
Each view (MainMenu, Settings, HUD,...) can just be a new xaml that you load into that root Grid.

The loading can be done manually:
NoesisView view = GetComponent<NoesisView>();
var xaml = Noesis.GUI.LoadXaml("Assets/Path/To/MainMenu.xaml");
var grid = (Grid)view.Content;
grid.Children.Add(xaml);
Or it can be data driven using MVVM:
<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="clr-namespace:MyTests">
  <Grid.Resources>
    <DataTemplate DataType="{x:Type local:MainMenuVM}">
      <local:MainMenu/>
    </DataTemplate>
    <!-- and the rest of views -->
  </Grid.Resources>
  <ContentControl Content="{Binding ActiveView}"/>
</Grid>
public class ViewModel: INotifyPropertyChanged
{
  private object _activeView;
  public object ActiveView
  {
    get { return _activeView; }
    set
    {
      if (_activeView != value)
      {
        _activeView = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ActiveView");
    }
  }
  
  ...
}
Where that ViewModel is set in root xaml DataContext, and each view is a UserControl that loads its associated xaml.

Does any of this helps?