Tuesday, November 24, 2015

Using Prism 6 in Windows 10 UWP

Здравствуйте, уважаемые читатели. Сегодня, как и обещал раньше в блоге, я наконец-то добрался к программированию для Windows 10 с использованием Prism 6.  Во-первых, это позволит мне посмотреть на изменения в разработке под Windows 8.1 и Windows 10. А во-вторых, я уже смогу использовать для этих целей Prism, потому что до этого было только два варианта, которые я выделял для себя: Caliburn.Micro и MvvmLight.
При написании этой статьи я столкнулся со следующей проблемой: моя Visual Studio 2015 не позволяла мне работать с Windows 10 эмулятором под Windows 8.1. Я промучился с переустановкой этого эмулятора полтора дня, но он у меня так и не заработал. Поэтому я принял волевое решение и развернул себе виртуалку с Windows 10, на которую я поставил Visual Studio 2015. После этого у меня все заработало. Думаю, достаточно о теории и проблемах, так как пора переходить к практике. Для начала создадим новое Universal Windows Application. которое назовем “PrismTest” для примера.
Для своих приложений я выбрал .Net Framework версии 4.6, но вы можете выбрать версию 4.5, и у вас все будет великолепно работать. В качестве загрузчика мы будем использовать PrismUnityApplication, поэтому нам понадобятся следующие пакеты, которые нам нужно будет установить из NuGet Package Manager.
Для того чтобы показать, как все работает, мы постараемся писать по минимуму кода; основной акцент будет ориентироваться на написание нашего загрузчика. Первым делом добавим в наш проект папку Views и ViewModels для реализации паттерна MVVM. Моделей сегодня у нас не будет, а будет все лишь одна модель представления. Давайте теперь перейдем в папку ViewModels и создадим нашу модель представления, которую назовем MainPageViewModel, для того чтобы мы могли сделать автопривязку с нашим представлением MainPage.xaml, которую мы рассмотрим немного позже. Рассмотрим, как реализована эта модель представления.
public class MainPageViewModel : ViewModelBase
{
    private DelegateCommand _buttonCommand;
    public DelegateCommand ButtonCommand
    {
        get
        {
            return _buttonCommand ?? (_buttonCommand = new DelegateCommand(ButtonClick));
        }
    }

    private async void ButtonClick()
    {
        var dialog = new MessageDialog("Test button click");
        await dialog.ShowAsync();
    }

    private bool _isNavigationDisabled;

    public bool IsNavigationDisabled
    {
        get { return _isNavigationDisabled; }
        set { SetProperty(ref _isNavigationDisabled, value); }
    }

    public override void OnNavigatingFrom(NavigatingFromEventArgs e, Dictionary<string, object> viewModelState, bool suspending)
    {
        e.Cancel = _isNavigationDisabled;

        base.OnNavigatingFrom(e, viewModelState, suspending);
    }
}
Начнем, пожалуй, с самого простого, что вы уже не раз встречали в Prism, а именно – с команд. Здесь, как и раньше, используется класс DelegateCommand для работы с командами, который особо не отличается внешне между Universal Apps и WPF приложениями. Поэтому не будем останавливаться на этом классе, а рассмотрим поближе класс ViewModelBase, от которого мы произвели наследование нашей модели представления. Это базовая модель представления для всех ваших классов, и вы можете ее использовать.  Основное отличие Prism, который под WPF, заключается в том, что там не нужна навигация, как в приложениях Universal Apps, вокруг которой построена вся логика, поэтому там всю логику выполняет класс BindableBase. Ниже перечислены все методы, которые доступны в классе ViewModelBase.
Наша команда ButtonCommand вызывает простое диалоговое окно (для данного примера это нарушение паттерна MVVM, но придумать что-то другое, с аналогичной простотой, сходу я не смог). Теперь самое время приступить к изменению нашего представления. Для этого мы наше окно MainPage.xaml переместим в папку Views и подправим пространства имен. Затем перейдем в сам редактор MainPage.xaml и изменим его следующим образом:
<Page
    x:Class="PrismTest.Views.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:PrismTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:prism="using:Prism.Windows.Mvvm"
    prism:ViewModelLocator.AutoWireViewModel="true"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

        <Button HorizontalAlignment="Center" Command="{Binding ButtonCommand}">Test</Button>
    </Grid>
</Page>
В нашем коде добавилось немного изменений. Первое изменение: мы указали Prism автоматически пытаться связать модель с моделью представления с помощью свойства AutoWireViewModel, установленного в true. И второе изменение: мы добавили кнопку со связыванием на команду ButtonCommand. Теперь осталось реализовать загрузчик, чтобы у нас все взлетело. По умолчанию вся логика запуска начального окна имплементирована в App.xaml.xs, в отличие от WPF приложений, в которых логика запуска окна прописана в самом xaml. Как я уже говорил раньше в своем блоге, для Universal Apps (Windows 8.1/10) мы можем вполне обойтись без загрузчика. Для UWP приложений и нет такого загрузчика, как, например, UnityBootstrapper для WFP. Однако мы с самого начала установили пакет Prism.Unity, который позволит писать более компактный, читабельный код, благодаря загрузчику PrismUnityApplication. Он отличается от привычного загрузчика UnityBootstrapper, поскольку расширяет класс Application.
Вот что нам нужно сделать, чтобы воспользоваться данным загрузчиком. Во-первых, зайти в класс App.xaml.cs и удалить всю реализацию, кроме самого класса App.  То есть, все, что внутри класса App, просто удаляем. Затем переписываем наш класс следующим образом:
sealed partial class App : PrismUnityApplication
{
    /// <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()
    {
        this.InitializeComponent();
    }

    protected override Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args)
    {
        NavigationService.Navigate("Main", null);
        return Task.FromResult<object>(null);
    }
}
Как видите, куча мусора убралась, и код максимально упростился. Но к сожалению, он еще не заработает. Основная причина в том, что в файле App.xaml у нас осталось старое наследование и используется класс Application. Для этого перейдем в файл App.xaml и укажем ему использовать, вместо Application, класс PrismUnityApplication, как показано в примере ниже.
<windows:PrismUnityApplication
    x:Class="PrismTest.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:PrismTest"
    xmlns:windows="using:Prism.Unity.Windows"
    RequestedTheme="Light">
    <Application.Resources>
    </Application.Resources>
</windows:PrismUnityApplication>
Если вы все проделали правильно, ваш код должен успешно скомпилироваться. При запуске проекта вы сможете увидеть на экране то, что у вас получилось. В Windows 10 это сделать несложно, достаточно нажать на Local Machine в панели запуска.
Под разные эмуляторы есть вероятность, что вам придется немного потанцевать с бубном и пошаманить. чтобы это все взлетело. Но так как у меня Windows 10, я просто запустил и смотрю на то, что получилось.
Затем нажмем на кнопку "Test", чтобы проверить, что наша команда отрабатывает.

Итоги
Думаю, на этой позитивной ноте пока остановимся. Загрузчик мы рассмотрели, работу с моделями представления и командами также. Более сложные случаи, когда вам нужно регистрировать интерфейсы и реализацию с помощью Unity, мы не рассмотрели, но это делается также несложно, и если вы посмотрите, какие методы доступны для переопределения в PrismUnityApplication, вы сразу же догадаетесь, какой метод используется для регистрации всего этого добра (он схожий по семантике с реализацией в UnityBoostrapper). Лично мне не очень по душе Universal Applications из-за огромного количества всюду напиханных async/await, которые здесь поощряются для использования. Надеюсь, что когда-либо это изменится к лучшему. Буду рад ответить на ваши вопросы в комментариях, поэтому не стесняйтесь спрашивать, что вас интересует. Возможно, смогу написать несколько примеров в следующей статье с более глубоким погружением.

No comments:

Post a Comment