Instantly share code, notes, and snippets.
Last active
August 11, 2024 12:19
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save markusbkk/2b72f616f6e0f6b77af818d2d82e2954 to your computer and use it in GitHub Desktop.
NSCUMM View Model Binding Problem
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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) | |
| { | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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