UWP 版の Prism の Prism.Uno を試してみよう


XAML 系プラットフォーム(WPF、Xamarin.Forms、UWP)でよく使われてる MVVM フレームワークの Prism ですが、UWP 版の Prism は一時期削除されましたが、Prism v8 (現在はプレビュー段階) で WPF 版の Prism をベースに UWP に移植されました。削除された昔の UWP 用 Prism は、どちらかというと Xamarin.Forms の Prism を踏襲していましたが、WPF のほうに寄せてきました。

試してみよう

UWP プロジェクトを新規作成します。そして Prism.Unity.Uno のプレビュー版のパッケージを追加します。
そして、Views 名前空間に Shell という名前でページを作成します。XAML は以下のようにして、とりあえず ContentRegion が 1 つあるだけにしました。

Shell.xaml
<Page
    x:Class="HelloPrismApp.Views.Shell"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:HelloPrismApp.Views"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:regions="using:Prism.Regions"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid Padding="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Text="MyApp"
                   Style="{StaticResource HeaderTextBlockStyle}" />
        <ContentControl Grid.Row="1" 
                        regions:RegionManager.RegionName="ContentRegion" />
    </Grid>
</Page>

MainPage.xaml は不要なので削除して App.xaml を以下のように PrismApplication を継承するようにします。

App.xaml
<prism:PrismApplication
    x:Class="HelloPrismApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:prism="using:Prism.Unity"
    xmlns:local="using:HelloPrismApp">

</prism:PrismApplication>

App.xaml.csPrismApplication を継承するようにします。CreateShell メソッドで Shell をコンテナから取得するようにすれば OK です。

App.xaml.cs
using HelloPrismApp.Views;
using Prism.Ioc;
using Prism.Unity;
using Windows.UI.Xaml;

namespace HelloPrismApp
{
    sealed partial class App : PrismApplication
    {
        public App()
        {
            this.InitializeComponent();
        }

        protected override UIElement CreateShell() => Container.Resolve<Shell>();

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }
    }
}

実行すると以下のようになります。Shell が表示されました。

画面の中身を作ろう

Prism は Shell の Region に対して View をモジュールから差し込むことが出来ます。やってみましょう。UWP のクラスライブラリを HelloPrismApp.Main という名前で作って Prism.Uno パッケージ(プレビュー)を追加します。

クラスライブラリ側に ViewModels フォルダーを作って適当に VM を作ります。

TopViewModel.cs
using Prism.Commands;
using Prism.Mvvm;
using System;

namespace HelloPrismApp.Main.ViewModels
{
    public class TopViewModel : BindableBase
    {
        private string _message;
        public string Message
        {
            get { return _message; }
            set { SetProperty(ref _message, value); }
        }

        private DelegateCommand _updateMessageCommand;
        public DelegateCommand UpdateMessageCommand =>
            _updateMessageCommand ?? (_updateMessageCommand = new DelegateCommand(ExecuteUpdateMessageCommand));

        private void ExecuteUpdateMessageCommand()
        {
            Message = DateTime.Now.ToString();
        }
    }
}

View も作ります。UserControl です。ViewModelLocator を設定して ViewModel が DataContext に設定されるようにします。

TopView.xaml
<UserControl
    x:Class="HelloPrismApp.Main.Views.TopView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:HelloPrismApp.Main.Views"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:viewModels="using:Prism.Mvvm"
    viewModels:ViewModelLocator.AutoWireViewModel="True"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <StackPanel>
        <TextBlock Text="{x:Bind ViewModel.Message, Mode=OneWay}"
                   Style="{StaticResource BodyTextBlockStyle}" />
        <Button Content="Update message"
                Command="{x:Bind ViewModel.UpdateMessageCommand}" />
    </StackPanel>
</UserControl>

TopView.xaml.cs に ViewModel プロパティも生やしておきます。

TopView.xaml.cs
using HelloPrismApp.Main.ViewModels;
using Windows.UI.Xaml.Controls;

namespace HelloPrismApp.Main.Views
{
    public sealed partial class TopView : UserControl
    {
        private TopViewModel ViewModel => (TopViewModel)DataContext;
        public TopView()
        {
            this.InitializeComponent();
        }
    }
}

最後に MainModule クラスを作って View の登録と View を Region に追加します。

MainModule.cs
using HelloPrismApp.Main.ViewModels;
using HelloPrismApp.Main.Views;
using Prism.Ioc;
using Prism.Modularity;
using Prism.Regions;

namespace HelloPrismApp.Main
{
    public class MainModule : IModule
    {
        public void OnInitialized(IContainerProvider containerProvider)
        {
            var regionManager = containerProvider.Resolve<IRegionManager>();
            regionManager.RequestNavigate("ContentRegion", "TopView");
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterForNavigation<TopView, TopViewModel>();
        }
    }
}

モジュールが出来たのでアプリ本体のほうに参照を追加して App.xaml.cs にモジュールを登録します。

App.xaml.cs
using HelloPrismApp.Main;
using HelloPrismApp.Views;
using Prism.Ioc;
using Prism.Modularity;
using Prism.Unity;
using Windows.UI.Xaml;

namespace HelloPrismApp
{
    sealed partial class App : PrismApplication
    {
        public App()
        {
            this.InitializeComponent();
        }

        protected override UIElement CreateShell() => Container.Resolve<Shell>();

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }

        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
            base.ConfigureModuleCatalog(moduleCatalog);
            moduleCatalog.AddModule<MainModule>();
        }
    }
}

実行すると ShellTopView が表示されています。コマンドもちゃんと動いているので ViewModel もちゃんと設定されてますね。

まとめ

個人的な予想ですが順当に進化していくと Windows UI Library 3.0 にも対応していくと思います。
そうなってくると理想的には UWP の他に Win32 の WinUI 3.0 にも対応してくれるのではないかと期待しています。

そうなると ViewModel レイヤーより下のレイヤーは WPF/UWP/WinUI3.0 で共有化して View だけ差し替えとかもできるかもしれません。特に WPF と WinUI 3.0 の Win32 はかなり共有できないかなぁと期待してます。

ということで、これからもちょくちょく Prism の状態をウォッチしていこうと思います。