.NETで作る!

.NETに関するあれこれ(C#、VB.NET)

WPF+Prism 5.0 でMVVMアプリを作る(ダイアログ)

ダイアログに関しては以前の記事でも取り上げましたが、以下のサイトが参考になるでしょう。

Interactivity Code Sample using the Prism Library 5.0 for WPF in C# for Visual Studio 2013

これを踏まえてちょこっとだけカスタマイズしてみます。

標準のダイアログの外観

f:id:mk3008net:20150125193548p:plain

カスタマイズしたダイアログの外観

Windows7風。

f:id:mk3008net:20150125193635p:plain

f:id:mk3008net:20150125193650p:plain

標準の使い方(View、ConfirmationRequest)

<UserControl x:Class="SampleView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:prism="http://www.codeplex.com/prism"
             mc:Ignorable="d" 
             DataContext="{Binding LoginViewModel, Source={StaticResource Locator}}">

    <i:Interaction.Triggers>
        <prism:InteractionRequestTrigger SourceObject="{Binding ConfirmationRequest, Mode=OneWay}">
            <prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True"/>
        </prism:InteractionRequestTrigger>
    </i:Interaction.Triggers>
    <Grid>
        <!--略-->
    </Grid>    
</UserControl>

prism:PopupWindowActionのプロパティの意味は以下の通り。

  • IsModel="True"Window.ShowDialog
  • IsModel="False"Window.Show
  • CenterOverAssociatedObject=TrueWindowStartupLocation="CenterScreen"
  • CenterOverAssociatedObject=FalseWindowStartupLocation="Manual"

標準の使い方(ViewModel)

Imports Microsoft.Practices.Prism.Mvvm
Imports Microsoft.Practices.Prism.Commands
Imports Microsoft.Practices.Prism.Interactivity.InteractionRequest
Imports System.Runtime.InteropServices

Public Class SampleViewModel
    Inherits BindableBase
    
    'Commandのコードは省略
    
    Private Sub OnExecute()
        If Me.RaiseConfirm = False Then Return
        'TODO:実処理
    End Sub

    Public Function RaiseConfirm() As Boolean
        'コールバック格納変数
        Dim isConfirm As Boolean = True

        'View側で受けるコードがない場合はコールバックされません。
        Me.ConfirmationRequest.Raise(
            New Confirmation With {.Content = "実行しますよろしいですか?", .Title = "確認"},
            Sub(c) isConfirm = c.Confirmed)

        Return isConfirm
    End Function

    Private _confirmationRequest As New InteractionRequest(Of IConfirmation)
    ''' <summary>
    ''' 確認リクエスト
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property ConfirmationRequest As InteractionRequest(Of IConfirmation)
        Get
            Return _confirmationRequest
        End Get
        Private Set(value As InteractionRequest(Of IConfirmation))
            _confirmationRequest = value
        End Set
    End Property
End Class

これはサンプルのコードほぼそのまま、かつそのまま特記事項もないので省略。これで確認ダイアログが表示できます。

カスタマイズしたダイアログの使い方(View、ConfirmationRequest)

View部分を自分で作成したUserControlに差し替えます。

まずは見た目となるViewのコードから。

<UserControl x:Class="ConfirmView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d">
    
    <DockPanel MinWidth="400">
        <DockPanel DockPanel.Dock="Bottom" Background="#F0F0F0" >
            <Grid Margin="8" >
                <Grid.Resources>
                    <Style TargetType="Button" x:Key="ReturnButton" >
                        <Setter Property="Padding" Value="3" />
                        <Setter Property="Margin" Value="6,0,0,0" />
                    </Style>
                </Grid.Resources>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="104" />
                    <ColumnDefinition Width="104" />
                </Grid.ColumnDefinitions>
                <Button Grid.Column="1" Content="OK" Command="{Binding OkCommand}" Style="{StaticResource ReturnButton}" IsDefault="True"/>
                <Button Grid.Column="3" Content="キャンセル" Command="{Binding CancelCommand}" Style="{StaticResource ReturnButton}" IsCancel="True" />
            </Grid>
        </DockPanel>

        <TextBlock Margin="8,8,8,16" MinHeight="48" Text="{Binding Notification.Content}" />
    </DockPanel>
</UserControl>

そしてViewModel。OKボタン押下時、戻り値をセットして、Me.FinishInteraction.Invoke()を発生させる。Cancelボタン押下時も同様。

Imports Microsoft.Practices.Prism.Mvvm
Imports Microsoft.Practices.Prism.Interactivity.InteractionRequest
Imports Microsoft.Practices.Prism.Commands

Public Class ConfirmViewModel
    Inherits BindableBase
    Implements IInteractionRequestAware

    Public Sub New()
        Me.OkCommand = New DelegateCommand(AddressOf Me.OnOk)
        Me.CancelCommand = New DelegateCommand(AddressOf Me.OnCancel)
    End Sub

    Public Property OkCommand As DelegateCommand

    Public Sub OnOk()
        Me.Confirmation.Confirmed = True
        Me.FinishInteraction.Invoke()
    End Sub

    Public Property CancelCommand As DelegateCommand

    Public Sub OnCancel()
        Me.Confirmation.Confirmed = False
        Me.FinishInteraction.Invoke()
    End Sub

#Region "IInteractionRequestAware"
    Public Property FinishInteraction As Action Implements IInteractionRequestAware.FinishInteraction

    Private _notification As INotification
    Public Property Notification As INotification Implements IInteractionRequestAware.Notification
        Get
            Return _notification
        End Get
        Set(value As INotification)
            Me.SetProperty(_notification, value)
        End Set
    End Property
    Private ReadOnly Property Confirmation As Confirmation
        Get
            Return DirectCast(Me.Notification, Confirmation)
        End Get
    End Property
#End Region
End Class

ViewとViewModelの紐づけはコードビハインドで。

Public Class ConfirmView
    Sub New()
        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。
        Me.DataContext = New ConfirmViewModel
    End Sub
End Class

最後に呼び出し元のコードを以下のように変更して完成。

変更前

    <i:Interaction.Triggers>
        <prism:InteractionRequestTrigger SourceObject="{Binding ConfirmationRequest, Mode=OneWay}">
            <prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True"/>
        </prism:InteractionRequestTrigger>
    </i:Interaction.Triggers>

変更後

    <i:Interaction.Triggers>
        <prism:InteractionRequestTrigger SourceObject="{Binding LeaveConfirmationRequest, Mode=OneWay}">
            <prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
                <prism:PopupWindowAction.WindowContent>
                    <local:LeaveConfirmView />
                </prism:PopupWindowAction.WindowContent>
            </prism:PopupWindowAction>
        </prism:InteractionRequestTrigger>
    </i:Interaction.Triggers>

カスタムな戻り値

2択の場合はConfirmationで良かったですが、先に挙げた3択のようにしたい場合は、独自のNotificationクラスを作って…

Imports Microsoft.Practices.Prism.Interactivity.InteractionRequest

Public Class LeaveNotification
    Inherits Notification

    Public Property Answer As LeaveConfirmAnswer
End Class

Public Enum LeaveConfirmAnswer
    ''' <summary>
    ''' 保存して移動
    ''' </summary>
    ''' <remarks></remarks>
    SaveAndLeave
    ''' <summary>
    ''' 保存せずに移動
    ''' </summary>
    ''' <remarks></remarks>
    NotSaveAndLeave
    ''' <summary>
    ''' 移動自体を取りやめる
    ''' </summary>
    ''' <remarks></remarks>
    Cancel
End Enum

専用のViewを作って(ここはまるっと省略)…

専用のViewModelを作って…

Public Class LeaveConfirmViewModel
'略
    Public Property SaveAndLeaveCommand As DelegateCommand

    Public Sub OnSaveAndLeave()
        Me.LeaveNotification.Answer = LeaveConfirmAnswer.SaveAndLeave
        Me.FinishInteraction.Invoke()
    End Sub
'略
End Class

コードビハインドでViewとViewModelを紐づけたら完成。

. .