Программируем на Monogame. Часть 5. Windows Store приложения Monogame+Xaml. Многостраничные приложения.

В этот раз мы разработаем простое многостраничное приложение. В конце мы должны будем получить страницу главного меню, страницу помощи по игре и собственно страницу с игрой.

Создадим страницу MainPage.xaml и добавим на нее кнопку, которая будет переносить на основной игровой экран.

<Page
    x:Class="XamlTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Button Content="NEW GAME" HorizontalAlignment="Center" FontSize="48"
                x:Name="ButtonStart"
                Click="ButtonStart_Click_1"/>
    </Grid>
</Page>

В обработчике события Click писать пока ничего не будем.

Изменим App.xaml таким образом, чтобы он загружал страницу MainPage вместо GamePage. При создадим переменные под обе наши страницы.

using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;

// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227

namespace XamlTest
{
    /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </summary>
    sealed partial class App : Application
    {
        private GamePage gamePage;
        private MainPage mainPage;

        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            InitializeComponent();
            Suspending += OnSuspending;

        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used when the application is launched to open a specific file, to display
        /// search results, and so forth.
        /// </summary>
        /// <param name="args">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs args)
        {
            mainPage = Window.Current.Content as MainPage;
            if (gamePage == null)
            {
                gamePage = new GamePage("");
            }

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (mainPage == null)
            {
                // Create a main GamePage
                //gamePage = new GamePage(args.Arguments);
                mainPage = new MainPage();

                if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    // TODO: Load state from previously suspended application
                }

                // Place the GamePage in the current Window
                Window.Current.Content = mainPage;
            }

            // Ensure the current window is active
            Window.Current.Activate();
        }

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        private void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();

            // TODO: Save application state and stop any background activity

            deferral.Complete();
        }
    }
}

Теперь в App.xaml добавим два метода, которые будут выполнять навигацию по страницам.

internal void StartGame()
        {
            Window.Current.Content = gamePage;
        }

        internal void StartMenu()
        {
            Window.Current.Content = mainPage;
        }

И создадим обработчики событий нажатия на книпки, которые будут выполнять навигацию.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace XamlTest
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private void ButtonStart_Click_1(object sender, RoutedEventArgs e)
        {
            (App.Current as App).StartGame();
        }
    }
}

А в GamePage.xaml.cs
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using MonoGame.Framework;

namespace XamlTest
{
    /// <summary>
    /// The root page used to display the game.
    /// </summary>
    public sealed partial class GamePage : SwapChainBackgroundPanel
    {
        readonly Game1 _game;

        public GamePage(string launchArguments)
        {
            this.InitializeComponent();

            // Create the game.
            _game = XamlGame<Game1>.Create(launchArguments, Window.Current.CoreWindow, this);
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            (App.Current as App).StartMenu();
        }
    }
}

Теперь приложение работает и навигация выполняется.

Стоит учитывать, что такой подход к навигации на принят для Xaml страниц и обычно навигация выполняется при помощи Frame. Однако, если мы попытаемся добавить Frame вместо MainPage и попытаемся выполнить навигацию при помощи Frame.Navigate(typeof(GamePage)), то у нас ничего не получится.

Так что мы скомбинируем два подхода. Frame будет управлять всей навигацией по xaml страницам, а переход к основной странице игры будет осуществляться подменой Frame на GamePage.

Замечание:
Раньше при попытке перехода к GamePage и использованием Frame я получал ошибку Page element must be root of visual tree when its content is SwapChainBackgroundPanel element.»(Если страница содержит SwapChainBackgroundPanel, то она должна быть в корне дерева элементов. В нашем случае корнем является Frame). Сейчас я почему-то не могу воспроизвести ее, но переход все равно не происходит.

using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

// The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227

namespace XamlTest
{
    /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </summary>
    sealed partial class App : Application
    {
        private GamePage gamePage;
        Frame frame;

        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            InitializeComponent();
            Suspending += OnSuspending;

        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used when the application is launched to open a specific file, to display
        /// search results, and so forth.
        /// </summary>
        /// <param name="args">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs args)
        {
            frame = Window.Current.Content as Frame;
            if (gamePage == null)
            {
                gamePage = new GamePage("");
            }

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (frame == null)
            {
                // Create a main GamePage
                //gamePage = new GamePage(args.Arguments);
                frame = new Frame();

                if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    // TODO: Load state from previously suspended application
                }

                // Place the GamePage in the current Window
                frame.Navigate(typeof(MainPage));
                Window.Current.Content = frame;
            }

            // Ensure the current window is active
            Window.Current.Activate();
        }

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        private void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();

            // TODO: Save application state and stop any background activity

            deferral.Complete();
        }

        internal void StartGame()
        {
            Window.Current.Content = gamePage;
        }

        internal void EndGame()
        {
            Window.Current.Content = frame;
        }
    }
}

Обратите внимание на то, что теперь у нас появился метод EndGame. StartGame() будет выполнять навигацию к странице с игрой, а EndGame будет возвращать приложение с игрового экрана. За остальную навигацию будет отвечать Frame.

Добавим заодно страницу HelpPage, на которой позже можно будет разместить какую-нибудь полезную информацию. Пока с этой страницы можно будет только вернуться к предыдущей.

HelpPage.xaml

<Page
    x:Class="XamlTest.HelpPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Button Content="MAIN MENU" HorizontalAlignment="Center" FontSize="48" x:Name="ButtonGoToMenu" Click="ButtonGoToMenu_Click_1"/>
    </Grid>
</Page>

HelpPage.xaml.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace XamlTest
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class HelpPage : Page
    {
        public HelpPage()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private void ButtonGoToMenu_Click_1(object sender, RoutedEventArgs e)
        {
            Frame.Navigate(typeof(MainPage));
        }
    }
}

Изменим также и страницу MainPage: добавим на нее кнопку для перехода к странице HelpPage и изменим обработчики событий.

MainPage.xaml

<Page
    x:Class="XamlTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <Button Content="NEW GAME" HorizontalAlignment="Center" FontSize="48"
                x:Name="ButtonStart"
                Click="ButtonStart_Click_1"/>
            <Button Content="HELP" HorizontalAlignment="Center" FontSize="48"
                x:Name="ButtonHelp"
                Click="ButtonHelp_Click_1"/>
        </StackPanel>
    </Grid>
</Page>

MainPage.xaml.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace XamlTest
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private void ButtonStart_Click_1(object sender, RoutedEventArgs e)
        {
            (App.Current as App).StartGame();
        }

        private void ButtonHelp_Click_1(object sender, RoutedEventArgs e)
        {
            Frame.Navigate(typeof(HelpPage));
        }
    }
}

Также изменим GamePage так, чтобы это была страница (Page), на которую позже мы сможем добавлять ресурсы, application bar`ы и т.д.

GamePage.xaml

<Page
    x:Class="XamlTest.GamePage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <SwapChainBackgroundPanel x:Name="gamePanel">
        <Grid>
            <Button Content="THIS IS THE TEST BUTTON" HorizontalAlignment="Center" Click="Button_Click_1"/>
        </Grid>
    </SwapChainBackgroundPanel>

</Page>

GamePage.xaml.cs

using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using MonoGame.Framework;

namespace XamlTest
{
    /// <summary>
    /// The root page used to display the game.
    /// </summary>
    public sealed partial class GamePage : Page
    {
        readonly Game1 _game;

        public GamePage(string launchArguments)
        {
            this.InitializeComponent();

            // Create the game.
            _game = XamlGame<Game1>.Create(launchArguments, Window.Current.CoreWindow, gamePanel);
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            (App.Current as App).EndGame();
        }
    }
}

Теперь навигация должна выполняться правильно.

Реклама
Запись опубликована в рубрике Компьютерная графика с метками , , , , , , . Добавьте в закладки постоянную ссылку.

2 комментария на «Программируем на Monogame. Часть 5. Windows Store приложения Monogame+Xaml. Многостраничные приложения.»

  1. Геннадий:

    Большое спасибо за статьи. Хотелось бы продолжения.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s