22-ConfirmCancelNavigation

Descriptionには Use the IConfirmNavigationReqest interface to confirm or cancel navigation とあります。

実際に実行したら、ViewA と ViewB を切り替えるボタンがありますが… ViewA から ViewB に切り替える時にはメッセージボックスが出るのに、 ViewB から ViewA に切り替える時にメッセージボックスが出ない、え?これ欠陥品じゃないの?

ああ、ソースコードを読んだら納得しました。

ModuleA 内の ViewModels\ViewAViewModel.cs

using System.Windows;

namespace ModuleA.ViewModels
{
    public class ViewAViewModel : BindableBase, IConfirmNavigationRequest
    {
        public ViewAViewModel() { }

        public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
        {
            bool result = true;

            if (MessageBox.Show("Do you to navigate?", "Navigate?", MessageBoxButton.YesNo) == MessageBoxResult.No)
                result = false;

            continuationCallback(result);
        }

        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            return true;
        }

        public void OnNavigatedFrom(NavigationContext navigationContext) { }

        public void OnNavigatedTo(NavigationContext navigationContext) { }
    }
}

ModuleA 内の ViewModels\ViewBViewModel.cs

namespace ModuleA.ViewModels
{
    public class ViewBViewModel : BindableBase
    {
        public ViewBViewModel() { }
    }
}

っていうか、訳がわからないですね?

俺からすると「こんな機能を使ったら、むしろバグの温床になるぞ」としか言えません。次行きますよ次!

23-RegionMemberLifetime

Descriptionには Automatically remove views from memory with IRegionMemberLifetime とあります。

動かしてみたけど「え、これで何をしたいの?」という気持ちが止まりません。

ViewA と ViewB を切り替えたとき、ObservableCollection で TextBlock を追加していくだけ。

いや、それだけならまだ真剣に解説しますよ?だけど、このサンプル 挙動が不安定 なんですよ!

例えば「Navigate to View B」→「Navigate to View A」→「Navigate to View B」で「View A」が上書きされるというね…?

流石に、何をしたいのか理解できない物には、沈黙せざるを得ないのです。

24-NavigationJournal

Descriptionには Learn how to use the Navigation Journal とあります。

これは、ListBox の項目を選択したら、その画面に移行して表示する。「Go Back」で元に戻る、そういうサンプルですね?

ModuleA の Views\PersonList.xaml(一部)

<ListBox x:Name="_listOfPeople" ItemsSource="{Binding People}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <prism:InvokeCommandAction Command="{Binding PersonSelectedCommand}" CommandParameter="{Binding SelectedItem, ElementName=_listOfPeople}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ListBox>

ここで選択されたアイテムに対して Command を実行しているわけです…だから SelectedItem お前はどこにいるんだと!

ModuleA の ViewModels\PersonListViewModel.cs (一部)

public PersonListViewModel(IRegionManager regionManager)
{
    _regionManager = regionManager;

    PersonSelectedCommand = new DelegateCommand<Person>(PersonSelected);
    CreatePeople();

    GoForwardCommand = new DelegateCommand(GoForward, CanGoForward);
}

private void PersonSelected(Person person)
{
    var parameters = new NavigationParameters();
    parameters.Add("person", person);

    if (person != null)
        _regionManager.RequestNavigate("ContentRegion", "PersonDetail", parameters);
}

まあ比較的素直なコードじゃないでしょうか?

もっとも、書きやすいかと言われたら、俺は首を捻りますが。

26-UsingDialogService

25- が飛んでいますけど編集ミスではありません。 Prism-Sample-WPFでご確認ください。

Descriptionには Learn how to use the Dialog Service とあります。

このコードが何をするかというと「Show Dialog」ボタンを押したら、独自で生成したダイアログを表示するだけです。

ViewModels\MainWindowViewModel.cs

private void ShowDialog()
{
    var message = "This is a message that should be shown in the dialog.";
    //using the dialog service as-is
    _dialogService.ShowDialog("NotificationDialog", new DialogParameters($"message={message}"), r =>
    {
        if (r.Result == ButtonResult.None)
            Title = "Result is None";
        else if (r.Result == ButtonResult.OK)
            Title = "Result is OK";
        else if (r.Result == ButtonResult.Cancel)
            Title = "Result is Cancel";
        else
            Title = "I Don't know what you did!?";
    });
}

肝となるのはこのコードだけでしょう。挙動は予想通り、OKを押したら OK 処理に、Cancel を押したら Cancel 処理に、 ウィンドウ自体を閉じたら None 処理に行くわけです。

27-StylingDialog

Descriptionには Learn how to style the dialog とあります。

…なんだこれ、26-UsingDialogService との差がわからないぞ?

いやあ、解説のためのサンプルコードなら、せめて最低限のコメント、適切な命名くらいしようよ…本当に。

はい、次行きますよ次!

28-UsingCustomWindow

Descriptionには Learn how to use a custom window とあります。

訳がわからないですね…27-StylingDialog とほぼ動きは同じなんですが…

Views\MyCustomWindow.xaml

<Window
    x:Class="UsingCustomWindow.Views.MyCustomWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:UsingCustomWindow.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MyCustomWindow"
    Background="Azure"
    SizeToContent="WidthAndHeight"
    mc:Ignorable="d" />

これでダイアログの背景色が Azure になってますが、どこで Window と UserControl を紐付けているのか…

App.xaml.cs (一部)

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterDialogWindow<MyCustomWindow>();
    containerRegistry.RegisterDialog<NotificationDialog, NotificationDialogViewModel>();
}

これがそれっぽいんですが、いや MyCustomWindow と NotificationDialog を独立して登録してるよね? だから、訳がわからないんです。

っていうか、わざわざ UserControl として独立させる必要あった?とか色々な疑問はありますが…次行きますよ次!

29-InvokeCommandAction

Descriptionには Invoke commands in response to any event とあります。

実行時に出てくる TextBlock を日本語訳してみましょう。

InvokeCommandActionは、 ビュー内のコントロールによって発生したイベントに応答してコマンドを呼び出す必要がある場合に便利です。 以下の例では、アイテムのリストがあり、アイテムが選択された際にビューモデルでコマンドを実行したいとします。 すると、ビューモデルが「選択されたアイテム」を変更します。

まあ、説明された通りの動きをします。

ListBox のアイテムを選択したら、下部の TextBlock に表示されます。

Views\MainWindow.xaml

<Window
    x:Class="UsingInvokeCommandAction.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
    xmlns:prism="http://prismlibrary.com/"
    Title="{Binding Title}"
    Width="525"
    Height="350"
    prism:ViewModelLocator.AutoWireViewModel="True">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0">
            <TextBlock
                Margin="5"
                FontSize="24"
                Foreground="DarkRed"
                TextWrapping="Wrap">
                InvokeCommandAction
            </TextBlock>
            <TextBlock Margin="5" TextWrapping="Wrap">
                The<Bold>InvokeCommandAction</Bold>
                is useful when you need to invoke a command in response to an event raised by a control in the view.</TextBlock>
            <TextBlock Margin="5" TextWrapping="Wrap">
                In the following example there is a list of items and we want to execute a command in the view model when an item is selected.
                The view model will then change the "Selected Item" shown below.
                The<Bold>InvokeCommandAction</Bold>
                is useful when you need to invoke a command in response to an event raised by a control in the view.</TextBlock>
            <TextBlock Margin="5" TextWrapping="Wrap">
                In the following example there is a list of items and we want to execute a command in the view model when an item is selected.
                The view model will then change the "Selected Item" shown below.
            </TextBlock>
        </StackPanel>

        <ListBox
            Grid.Row="1"
            Margin="5"
            ItemsSource="{Binding Items}"
            SelectionMode="Single">
            <i:Interaction.Triggers>
                <!--  This event trigger will execute the action when the corresponding event is raised by the ListBox.  -->
                <i:EventTrigger EventName="SelectionChanged">
                    <!--  This action will invoke the selected command in the view model and pass the parameters of the event to it.  -->
                    <prism:InvokeCommandAction Command="{Binding SelectedCommand}" TriggerParameterPath="AddedItems" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </ListBox>

        <StackPanel
            Grid.Row="2"
            Margin="5"
            Orientation="Horizontal">
            <TextBlock FontWeight="Bold" Foreground="DarkRed">Selected Item:</TextBlock>
            <TextBlock
                Margin="5,0"
                AutomationProperties.AutomationId="SelectedItemTextBlock"
                FontWeight="Bold"
                Foreground="DarkRed"
                Text="{Binding SelectedItemText}" />
        </StackPanel>

    </Grid>
</Window>

ViewModels\MainWindowViewModel.cs

namespace UsingInvokeCommandAction.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        private string _title = "Prism Unity Application";
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        private string _selectedItemText;
        public string SelectedItemText
        {
            get { return _selectedItemText; }
            private set { SetProperty(ref _selectedItemText, value); }
        }

        public IList<string> Items { get; private set; }

        public DelegateCommand<object[]> SelectedCommand { get; private set; }

        public MainWindowViewModel()
        {
            Items = new List<string>();

            Items.Add("Item1");
            Items.Add("Item2");
            Items.Add("Item3");
            Items.Add("Item4");
            Items.Add("Item5");

            // This command will be executed when the selection of the ListBox in the view changes.
            SelectedCommand = new DelegateCommand<object[]>(OnItemSelected);
        }

        private void OnItemSelected(object[] selectedItems)
        {
            if (selectedItems != null && selectedItems.Count() > 0)
            {
                SelectedItemText = selectedItems.FirstOrDefault().ToString();
            }
        }
    }
}

何をやっているのか、おおよそは分かるんだけど、意図が理解できないというね。

肝要なところはソース提示しましたし、次行きますよ次!と思ったらこれが最後でした…

締めますね

Prism短期集中講座と銘打ちつつ、結局大半が Prism を dis るような内容になってしまいました。

いや、正確には Prism サンプルを dis ってますね… とにかく、実際に触ってみて感じたことは、Prism 自体も既に設計が時代遅れになりつつある、ということ。

あらゆるプログラミングで、ライブラリで、銀の弾丸なんてありません。 今の私は CommunityToolkit.Mvvm が大好きですが、いずれ CommunityToolkit.Mvvm も「古い」と言われる時がくるかもしれません。

その時その時で、優れた選択肢を選んで行きたいですね!