Skip to content

Instantly share code, notes, and snippets.

@markusbkk
Last active August 11, 2024 12:19
Show Gist options
  • Select an option

  • Save markusbkk/2b72f616f6e0f6b77af818d2d82e2954 to your computer and use it in GitHub Desktop.

Select an option

Save markusbkk/2b72f616f6e0f6b77af818d2d82e2954 to your computer and use it in GitHub Desktop.
NSCUMM View Model Binding Problem
using NScumm.Core;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.UI.Xaml.Controls;
using Windows.ApplicationModel.Resources;
using Windows.UI.Core;
using System;
using Windows.UI.Popups;
using System.Threading.Tasks;
using Windows.UI.Xaml.Media;
using Windows.UI;
using Windows.Storage;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml;
using NScumm.MonoGame.ViewModels;
using NScumm.MonoGame.Converters;
namespace NScumm.MonoGame.Pages
{
public sealed partial class MainMenu : ContentDialog
{
public MenuViewModel ViewModel
{
get { return DataContext as MenuViewModel; }
set { DataContext = value; }
}
private ResourceLoader _loader;
internal ScummGame Game { get; set; }
private IEngine Engine
{
get
{
var engine = Game.Services.GetService<IEngine>();
return engine;
}
}
public MainMenu()
{
InitializeComponent();
_loader = ResourceLoader.GetForViewIndependentUse();
}
private void OnResume(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
Hide();
}
private void OnBack(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
Title = _loader.GetString("MainMenu_Title");
MainStackPanel.Visibility = Windows.UI.Xaml.Visibility.Visible;
LoadStackPanel.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
SaveStackPanel.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
private async void OnLoad(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
Title = _loader.GetString("MainMenu_LoadTitle");
LoadGameList.Items.Clear();
var savegames = await GetSavegames();
savegames.ForEach(LoadGameList.Items.Add);
MainStackPanel.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
LoadStackPanel.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
private async void OnSave(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
Title = _loader.GetString("MainMenu_SaveTitle");
SaveGameList.Items.Clear();
var savegames = await GetSavegames();
savegames.ForEach(SaveGameList.Items.Add);
ListViewItem NewEntry = new ListViewItem();
NewEntry.Content = _loader.GetString("New Entry");
NewEntry.Background = new SolidColorBrush(Colors.DodgerBlue);
NewEntry.Foreground = new SolidColorBrush(Colors.White);
SaveGameList.Items.Add(NewEntry);
MainStackPanel.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
SaveStackPanel.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
private void OnExit(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
ExitGame_Click();
Hide();
}
private async void ExitGame_Click()
{
try
{
var messageDialog = new MessageDialog("Do you want to exit?");
messageDialog.Commands.Add(new UICommand("Exit", new UICommandInvokedHandler(this.CommandInvokedHandler)));
messageDialog.Commands.Add(new UICommand("Dismiss"));
await messageDialog.ShowAsync();
}
catch (Exception ex)
{
}
}
private void CommandInvokedHandler(IUICommand command)
{
Engine.IsPaused = false;
Engine.HasToQuit = true;
App.isGameStarted = false;
}
private void OnLoadGame(object sender, ItemClickEventArgs e)
{
//string ObjectType = e.ClickedItem.GetType().Name;
var index = ((ListView)sender).Items.IndexOf(e.ClickedItem);
LoadGame(index);
Hide();
}
private void OnSaveGame(object sender, ItemClickEventArgs e)
{
var index = ((ListView)sender).Items.IndexOf(e.ClickedItem);
if(index == -1)
{
index = ((ListView)sender).Items.Count - 1;
}
SaveGame(index);
Hide();
}
private async Task<List<string>> GetSavegames()
{
var local = Windows.Storage.ApplicationData.Current.LocalFolder;
var spec = await local.CreateFolderAsync(Game.Settings.Game.Id, Windows.Storage.CreationCollisionOption.OpenIfExists);
if (spec != null)
{
var dir = spec.Path;
var pattern = string.Format("{0}*.sav", Game.Settings.Game.Id);
return ServiceLocator.FileStorage.EnumerateFiles(dir, pattern).Select(Path.GetFileNameWithoutExtension).ToList();
}
return new List<string>();
}
private async Task<string> GetSaveGamePath(int index)
{
var local = Windows.Storage.ApplicationData.Current.LocalFolder;
var spec = await local.CreateFolderAsync(Game.Settings.Game.Id, Windows.Storage.CreationCollisionOption.OpenIfExists);
if (spec != null)
{
var indexResolve = "00";
if(index > 9)
{
indexResolve = "0";
}else if (index > 99)
{
indexResolve = "";
}
var dir = spec.Path;
var filename = Path.Combine(dir, string.Format("{0} ({1}{2}).sav", Game.Settings.Game.Id, indexResolve ,(index + 1)));
return filename;
}
return "";
}
private async void LoadGame(int index)
{
try
{
var filename = await GetSaveGamePath(index);
if (filename.Length > 0)
{
Engine.Load(filename);
GamePage.ShowTileHandler.Invoke(new string[] { "Load State", "State loaded successfully", $"Name: { Path.GetFileNameWithoutExtension(filename) }"}, EventArgs.Empty);
}
}catch(Exception ex)
{
ShowDialog(ex);
}
}
private async void SaveGame(int index)
{
try
{
var filename = await GetSaveGamePath(index);
if (filename.Length > 0)
{
Engine.Save(filename);
GamePage.ShowTileHandler.Invoke(new string[] { "Save State", "Save state done", $"Name: { Path.GetFileNameWithoutExtension(filename) }" }, EventArgs.Empty);
}
}catch(Exception ex)
{
ShowDialog(ex);
}
}
private async void ShowDialog(Exception message, bool log = false)
{
try
{
if (log)
{
var logFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("log.txt", CreationCollisionOption.OpenIfExists);
await FileIO.AppendTextAsync(logFile, message.Message);
}
}
catch (Exception ex)
{
}
var messageDialog = new MessageDialog(message.Message);
messageDialog.Commands.Add(new UICommand(
"Close"));
await messageDialog.ShowAsync();
}
private async void ShowDialog(string message, bool log = false)
{
try
{
if (log)
{
var logFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("log.txt", CreationCollisionOption.OpenIfExists);
await FileIO.AppendTextAsync(logFile, message);
}
}
catch (Exception ex)
{
}
var messageDialog = new MessageDialog(message);
messageDialog.Commands.Add(new UICommand(
"Close"));
await messageDialog.ShowAsync();
}
private void ShowFPS_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
}
}
}
<ContentDialog
x:Class="NScumm.MonoGame.Pages.MainMenu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:NScumm.MonoGame.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local1="using:NScumm.MonoGame.ViewModels"
mc:Ignorable="d" x:Uid="MainMenu"
Title="Main Menu">
<ContentDialog.DataContext>
<local1:MenuViewModel/>
</ContentDialog.DataContext>
<ContentDialog.Template>
<ControlTemplate TargetType="ContentDialog">
<Border x:Name="Container">
<Grid x:Name="LayoutRoot" Padding="20,5,20,0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border x:Name="BackgroundElement"
Background="{TemplateBinding Background}"
FlowDirection="{TemplateBinding FlowDirection}"
BorderThickness="{ThemeResource ContentDialogBorderWidth}"
BorderBrush="{ThemeResource SystemControlForegroundAccentBrush}"
MaxWidth="{TemplateBinding MaxWidth}"
MaxHeight="{TemplateBinding MaxHeight}"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}" >
<Grid x:Name="DialogSpace" Padding="20,10,20,0" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollViewer x:Name="ContentScrollViewer" VerticalAlignment="Stretch" VerticalContentAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Disabled"
ZoomMode="Disabled"
Margin="{ThemeResource ContentDialogContentScrollViewerMargin}"
IsTabStop="False">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl x:Name="Title"
Margin="{ThemeResource ContentDialogTitleMargin}"
Content="{TemplateBinding Title}"
ContentTemplate="{TemplateBinding TitleTemplate}"
FontSize="20"
FontFamily="Segoe UI"
FontWeight="Normal"
Foreground="{TemplateBinding Foreground}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsTabStop="False"
MaxHeight="{ThemeResource ContentDialogTitleMaxHeight}" >
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<ContentPresenter
Content="{TemplateBinding Content}"
MaxLines="2"
TextWrapping="Wrap"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{TemplateBinding Padding}"
ContentTransitions="{TemplateBinding ContentTransitions}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
<ContentPresenter x:Name="Content"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
FontSize="{ThemeResource ControlContentThemeFontSize}"
FontFamily="{ThemeResource ContentControlThemeFontFamily}"
Margin="{ThemeResource ContentDialogContentMargin}"
Foreground="{TemplateBinding Foreground}"
Grid.Row="1"
TextWrapping="Wrap" />
</Grid>
</ScrollViewer>
<Grid x:Name="CommandSpace" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border x:Name="Button1Host"
Margin="{ThemeResource ContentDialogButton1HostMargin}"
MinWidth="{ThemeResource ContentDialogButtonMinWidth}"
MaxWidth="{ThemeResource ContentDialogButtonMaxWidth}"
Height="{ThemeResource ContentDialogButtonHeight}"
HorizontalAlignment="Stretch" />
<Border x:Name="Button2Host"
Margin="{ThemeResource ContentDialogButton2HostMargin}"
MinWidth="{ThemeResource ContentDialogButtonMinWidth}"
MaxWidth="{ThemeResource ContentDialogButtonMaxWidth}"
Height="{ThemeResource ContentDialogButtonHeight}"
Grid.Column="1"
HorizontalAlignment="Stretch" />
</Grid>
</Grid>
</Border>
</Grid>
</Border>
</ControlTemplate>
</ContentDialog.Template>
<Grid>
<StackPanel x:Name="MainStackPanel" Margin="0 20 0 0" Visibility="Visible">
<Button x:Uid="MainMenuSaveButton" HorizontalAlignment="Stretch" Margin="5" Click="OnSave">Save</Button>
<Button x:Uid="MainMenuLoadButton" HorizontalAlignment="Stretch" Margin="5" Click="OnLoad">Load</Button>
<Button x:Uid="MainMenuResumeButton" HorizontalAlignment="Stretch" Margin="5" Click="OnResume">Resume</Button>
<Button x:Uid="MainMenuExitButton" HorizontalAlignment="Stretch" Margin="5 20 5 5" Click="OnExit">Exit</Button>
<MenuFlyoutSeparator></MenuFlyoutSeparator>
<CheckBox x:Name="ShowFPScheckBox" Margin="5" IsChecked="{Binding ShowFPS, Mode=TwoWay}">FPS Counter</CheckBox>
</StackPanel>
<Grid x:Name="LoadStackPanel" Margin="0 20 0 0" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView x:Name="LoadGameList" MinWidth="150" ItemClick="OnLoadGame" IsItemClickEnabled="True" />
<Button x:Uid="MainMenuBackButton" Grid.Row="1" Margin="5 10 5 5" HorizontalAlignment="Stretch" Click="OnBack">Back</Button>
</Grid>
<Grid x:Name="SaveStackPanel" Margin="0 20 0 0" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView x:Name="SaveGameList" ItemClick="OnSaveGame" MinWidth="150" IsItemClickEnabled="True" ScrollViewer.VerticalScrollBarVisibility="Auto" />
<Button x:Uid="MainMenuBackButton" Grid.Row="1" Margin="5 10 5 5" HorizontalAlignment="Stretch" Click="OnBack">Back</Button>
</Grid>
</Grid>
</ContentDialog>
namespace NScumm.MonoGame.ViewModels
{
public interface IMenuViewModel
{
void UpdateShowFPS();
}
public class MenuViewModel : ViewModel, IMenuViewModel
{
private bool _showFPS;
public bool ShowFPS
{
get { return _showFPS; }
set
{
RaiseAndSetIfChanged(ref _showFPS, value);
}
}
}
}
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace NScumm.MonoGame.ViewModels
{
public abstract class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eh = PropertyChanged;
eh?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public virtual void RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(propertyName);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment