Detroit Sample: how do I reset ViewModels on state change? Where should C++ VMs come from?
I've implemented my menu screens by following the Detroit example; with VisualStateManager.VisualStateGroups -> and one VisualState per menu screen, which hide/unhide the different views (which can have their own ViewModel), ala
The issue is that one of these has a ViewModel with state in it, and I'd like that state to be reset on each return. (In this case, it's the pre-game setup, i.e. a game a lobby, with per-session settings that shouldn't persist if you back out to the main menu and then come back to the lobby screen). How should I go about that in this setup?
-----------------------
Second question; if I'm trying to control the menu screens in Xaml using these visual states, how do/should I create and manage my ViewModel C++ classes? I suppose I can have the xaml/Noesis create them when the view is instantiated, but what if those VMs need to communicate with the rest of the game? Right now, for my current VMs, they're all created from C++ manually with constructors that give them references to various engine systems, but this would be an inversion of that so I'm a bit lost on that part of the concept. To be fair, the existing VMs are essentially; are we in the menu? Create Menu VM. Are we entering gameplay? Create Game VM. Whereas this menu switching is more complicated! But I am enjoying the VisualState switching concept from that sample, these issues aside :)
Thanks.
Code: Select all
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="States">
<VisualState x:Name="Home">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="HomeScreen">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="StartScreen">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="SettingsScreen">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Start">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="HomeScreen">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="StartScreen">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="SettingsScreen">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Settings">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="HomeScreen">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="StartScreen">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="SettingsScreen">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
-----------------------
Second question; if I'm trying to control the menu screens in Xaml using these visual states, how do/should I create and manage my ViewModel C++ classes? I suppose I can have the xaml/Noesis create them when the view is instantiated, but what if those VMs need to communicate with the rest of the game? Right now, for my current VMs, they're all created from C++ manually with constructors that give them references to various engine systems, but this would be an inversion of that so I'm a bit lost on that part of the concept. To be fair, the existing VMs are essentially; are we in the menu? Create Menu VM. Are we entering gameplay? Create Game VM. Whereas this menu switching is more complicated! But I am enjoying the VisualState switching concept from that sample, these issues aside :)
Thanks.
Re: Detroit Sample: how do I reset ViewModels on state change? Where should C++ VMs come from?
I think I should clarify the above late-night message, specifically question 2.
When I know we're loading into the menu, I manually create my MenuViewModel and assign it to a "CurrentViewModel" property on a MainViewModel class. That main view model is just to hold whatever the "main" view model is at that point, and whichever view model is set has its xaml loaded.
MainWindow.xaml
Similarly, when we know the gameplay is loaded, we create a GameViewModel and set it as the "CurrentViewModel" above, replacing the menu view model. This all works well, but it revolves around us having a "main" view model, and then we create other view models within those as needed (e.g. a viewmodel for each unit that is created in the game, which exist on the GameViewModel directly), and we bind to those from various views.
If I let Noesis create my view models for me as per the original post, how do I update its properties etc since I don't have any pointers/references to it? They exist out in space and I have no direct connection to them. Conversely, if I revert to manually creating the view models am I not taking back over control from the viewstate switching? I'm trying to avoid too much state handling in C++.
My game lobby screen view xaml's start - can see it creating the view model.
When I know we're loading into the menu, I manually create my MenuViewModel and assign it to a "CurrentViewModel" property on a MainViewModel class. That main view model is just to hold whatever the "main" view model is at that point, and whichever view model is set has its xaml loaded.
MainWindow.xaml
Code: Select all
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:rtg"
xmlns:local-views="clr-namespace:rtg.Views" Background="Transparent">
<Grid.Resources>
<DataTemplate DataType="{x:Type MenuViewModel}">
<local-views:MenuView/>
</DataTemplate>
<DataTemplate DataType="{x:Type GameViewModel}">
<local-views:GameView/>
</DataTemplate>
</Grid.Resources>
<ContentControl Content="{Binding Path=CurrentViewModel}"></ContentControl>
</Grid>If I let Noesis create my view models for me as per the original post, how do I update its properties etc since I don't have any pointers/references to it? They exist out in space and I have no direct connection to them. Conversely, if I revert to manually creating the view models am I not taking back over control from the viewstate switching? I'm trying to avoid too much state handling in C++.
My game lobby screen view xaml's start - can see it creating the view model.
Code: Select all
<UserControl x:Class="rtg.Views.MenuGameSetupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:rtg.Views" xmlns:viewmodels="clr-namespace:rtg.ViewModels"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:views="clr-namespace:rtg.Views"
mc:Ignorable="d"
d:DesignHeight="1440" d:DesignWidth="2440"
x:Name="MenuGameSetupView">
<UserControl.DataContext>
<viewmodels:MenuGameSetupViewModel></viewmodels:MenuGameSetupViewModel>
</UserControl.DataContext>
Re: Detroit Sample: how do I reset ViewModels on state change? Where should C++ VMs come from?
Sorry for the triple post - but I think I found a way to do this.
In the main MenuView.xaml, I create the sub-views for each menu screen like so
And on the MenuViewModel, I create and reference a MenuGameSetupViewModel and expose as a property. This way, I control it and can link it to engine systems etc. Now in these sub-views I can use the bound view model as the DataContext.
---------
As to question 1; resetting the view model;
In the code-behind, we need to hook the visibility changed event
We create a function in the high-level menu view model to clear this sub view model.
And we update the earlier xaml to hook the sub-view's visibility change callback to this game session recreation
---------------------
Do these approaches make sense? Any better ideas / feedback? :)
In the main MenuView.xaml, I create the sub-views for each menu screen like so
Code: Select all
<local:MenuGameSetupView x:Name="StartScreen" Visibility="Collapsed" DataContext="{Binding MenuGameSetupViewModel}"/>
---------
As to question 1; resetting the view model;
In the code-behind, we need to hook the visibility changed event
Code: Select all
public partial class MenuGameSetupView : UserControl
{
public MenuGameSetupView()
{
InitializeComponent();
IsVisibleChanged += MenuGameSetupView_IsVisibleChanged;
}
private void MenuGameSetupView_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (RouteOnVisibilityChanged != null)
{
RouteOnVisibilityChanged.Execute(sender);
}
}
public static readonly DependencyProperty RouteOnVisibilityChangedProperty =
DependencyProperty.Register(
"RouteOnVisibilityChanged",
typeof(ICommand),
typeof(MenuGameSetupView),
new UIPropertyMetadata(null));
public ICommand RouteOnVisibilityChanged
{
get { return (ICommand)GetValue(RouteOnVisibilityChangedProperty); }
set { SetValue(RouteOnVisibilityChangedProperty, value); }
}
}
Code: Select all
public MenuViewModel()
{
NewGameSessionRequired = new DelegateCommand(o => OnNewGameSessionRequired(o));
GameSetupViewModel = new MenuGameSetupViewModel();
ServerBrowserViewModel = new ServerBrowserViewModel();
}
private void OnNewGameSessionRequired(object sender)
{
GameSetupViewModel = new MenuGameSetupViewModel();
}
//...
Code: Select all
<local:MenuGameSetupView x:Name="StartScreen" Visibility="Collapsed" RouteOnVisibilityChanged="{Binding Path=DataContext.NewGameSessionRequired, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type views:MenuView}}}" DataContext="{Binding GameSetupViewModel}"/>
Do these approaches make sense? Any better ideas / feedback? :)
-
-
sfernandez
Site Admin
- Posts: 3363
- Joined:
Re: Detroit Sample: how do I reset ViewModels on state change? Where should C++ VMs come from?
It looks fine to me. The general idea when using MVVM is that you can have a hierarchy of ViewModels that you can manage, and then assign them to the different parts of your UI. So a specific component only interacts with its own ViewModel, while a parent view model can decide if it needs to recreate it or not.
Who is online
Users browsing this forum: Google [Bot] and 9 guests