8.画面遷移
MVVMの画面遷移
MVVMのWPFの画面遷移の基本(基礎)は下記の「画面遷移の骨格」を読んで理解してから、これからの解説を読んで下さい。 (ソースのダウンロードが出来ます)
画面

MainWindowでログインボタンを押してWindow2ヘ画面遷移する例のXAMLは下記のようになります。

View
<Window x:Class="LivetWPFApplication100.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/expression/2010/interactivity"
xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
xmlns:v="clr-namespace:LivetWPFApplication100.Views"
xmlns:vm="clr-namespace:LivetWPFApplication100.ViewModels"
xmlns:b="clr-namespace:LivetWPFApplication100.Behaviors"
Title="MainWindow" Height="714" Width="1035"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/Styles.xaml"/>
<ResourceDictionary Source="/Resources/Styles2.xaml"/>
<ResourceDictionary Source="/Resources/StylesBG.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<i:Interaction.Triggers>
<!--WindowのContentRenderedイベントのタイミングでViewModelの
Initializeメソッドが呼ばれます-->
<i:EventTrigger EventName="ContentRendered">
<l:LivetCallMethodAction MethodTarget="{Binding}"
MethodName="Initialize"/>
</i:EventTrigger>
<l:InteractionMessageTrigger
MessageKey="MessageKey2" Messenger="{Binding Messenger}">
<l:TransitionInteractionMessageAction
WindowType="{x:Type v:Window2}" Mode="Modal"/>
</l:InteractionMessageTrigger>
</i:Interaction.Triggers>
<i:Interaction.Behaviors>
<b:CloseButtonBehavior IsWindowCloseEnable="{Binding ElementName=CloseCheck,
Path=IsChecked}"/>
</i:Interaction.Behaviors>
<Grid Background="#FFE6E6FA">
<Label Background="AliceBlue" Content="ユーザーID" Height="22"
HorizontalAlignment="Left" Margin="381,259,0,0" Name="label1"
VerticalAlignment="Top" Width="70" />
<Label Background="AliceBlue" Content="パスワード" Height="22"
HorizontalAlignment="Left" Margin="381,287,0,0" Name="label2"
VerticalAlignment="Top" Width="70" />
<TextBox InputMethod.PreferredImeState="Off"
Style="{StaticResource HighlightTextBox}" Height="22"
HorizontalAlignment="Left" Margin="450,259,429,0" Name="txtユーザーID"
Text="{Binding Path=txtユーザーID}" VerticalAlignment="Top" Width="134"/>
<PasswordBox MaxLength="10" Height="22" HorizontalAlignment="Left"
Margin="450,287,0,0" VerticalAlignment="Top" Width="134">
<i:Interaction.Behaviors>
<b:PasswordBindBehavior Password="{Binding txtパスワード,
Mode=TwoWay}" />
</i:Interaction.Behaviors>
</PasswordBox>
<Button Style="{StaticResource CommandTemplate}" Content="ログイン"
Width="130" Height="30"
Command="{Binding GotoCommand22}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Margin="450,322,433,323" Foreground="White" />
<v:UserControlFooterN1 Margin="0,0,0,0" HorizontalAlignment="Left"
VerticalAlignment="Bottom" />
</Grid>
</Window>
Windows フォームでは、IME (かな漢字変換) を制御するためにコントロールごとに ImeMode というプロパティが用意されていましたが、WPF の TextBox には、ImeMode プロパティがありません。
WPF では 入力方法を制御するために InputMethod というクラスが用意されています。51行目の「InputMethod.PreferredImeState="Off" 」は、IME を無効にします。
MainWindow.xamlとMainViewModel.csの紐付けは、MainWindow.xamlのコードビハインドファイルであるMainWindow.xaml.csのコンストラクタに「this.DataContext = new MainViewModel();」と記述します。
ViewとViewModelの紐付け
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
ViewのMainWindowのユーザーID(51~54行)とパスワード(56~62行)はMainViewModelでは変更通知プロパティとして設定する必要があります。
txtユーザーIDとtxtパスワードのプロパティのViewModelでの処理(実装)が下記の変更通知プロパティです。
ViewModel
#region txtユーザーID変更通知プロパティ
public string txtユーザーID { get; set; }
public string txtパスワード { get; set; }
#endregion
ZAMLのログインのButton(64~68行)をクリックした時の処理(命令)はCommandと呼ばれています。このCommandはViewModelの「GotoCommand22」メソッドと紐付け(Binding)されています。
MainWindowのViewModelであるMainViewModelは下記のようになります。
GotoCommand22(ViewModel)
public ViewModelCommand GotoCommand22
{
get { return new Livet.Commands.ViewModelCommand(Goto22); }
}
public void Goto22()
{
if (txtユーザーID == null)
{
MessageBox.inWShow("ユーザーIDが未入力です");
return;
}
if (txtパスワード == null)
{
MessageBox.Show("パスワードが未入力です");
return;
}
if (!作業員マスターログインチェック(txtユーザーID.ToUpper(), txtパスワード))
{
MessageBox.Show("ユーザーIDが違うかパスワードが違います");
return;
}
Messenger.Raise(new TransitionMessage(new ViewModel2()
{ NeedHideOwner = true }, "MessageKey2"));
}
作業員マスターログインチェック
#region 作業員マスターログインチェック
public bool 作業員マスターログインチェック(string str作業員コード,
string strパスワード)
{
bool bRet = false;
using (OracleConnection con = new OracleConnection())
{
con.ConnectionString = "User Id=UserId; Password=Password;
Data Source=DnsName;Pooling=false;";
con.Open();
//
string strSQL = String.Format("SELECT COUNT(*) AS CNT FROM
作業員マスター Where 作業員コード = '{0}' AND パスワード = '{1}'",
str作業員コード.ToUpper(), strパスワード);
OracleCommand oracmd = new OracleCommand(strSQL, con);
OracleDataReader reader = oracmd.ExecuteReader();
//*** Tran ***
//tx.Commit();
while (reader.Read() == true)
{
if (Convert.ToInt16(reader["CNT"].ToString()) > 0)
{
bRet = true;
}
}
return bRet;
}
}
#endregion
UserId、Password、DnsNameは実際のものに置き換えてください。str作業員コード.ToUpper()は、コードを大文字にしています。
遷移先のWindow2のXAMLは下記のようになります。太字部分(LoadedやClosing)はMVVMパターンでは有名な Expression Blend SDK に含まれる System.Windows.Interactivity.dll のクラス (EventTrigger, InvokeAction, Interaction 等)を使用してWPFのコントロールのイベントとViewModel等で公開される ICommand を実装したコマンドプロパティをコードビハインドファイルやハンドラを使用せずにxaml上で関連付ける方法 でSystem.Windows.Interactivity名前空間の EventTrigger クラスの EventName に コマンドと関連付けるイベントを設定し、 InvokeCommandAction によりコマンドとの関連づけを行っています。 ListBoxでは、EventTriggerのEventNameとしては「MouseEnter」、「MouseLeave」、「SelectionChanged」などがあります。
[WPF] コントロールの任意のイベントとコマンド xaml上で関連付けるView
<Window x:Class="LivetWPFApplication100.Views.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
xmlns:v="clr-namespace:LivetWPFApplication100.Views"
xmlns:b="clr-namespace:LivetWPFApplication100.Behaviors"
Title="Window2" Height="714" Width="1035"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/Styles.xaml"/>
<ResourceDictionary Source="/Resources/Styles2.xaml"/>
<ResourceDictionary Source="/Resources/StylesBG.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<i:Interaction.Triggers>
<!--WindowのContentRenderedイベントのタイミングでViewModelのInitialize
メソッドが呼ばれます-->
<i:EventTrigger EventName="ContentRendered">
<l:LivetCallMethodAction MethodTarget="{Binding}"
MethodName="Initialize"/>
</i:EventTrigger>
<l:InteractionMessageTrigger
MessageKey="MessageKey3" Messenger="{Binding Messenger}">
<l:TransitionInteractionMessageAction
WindowType="{x:Type v:Window3}" Mode="Modal"/>
</l:InteractionMessageTrigger>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding Path=Loaded}"
CommandParameter="{Binding Mode=OneTime,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type Window}}}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Closing ">
<i:InvokeCommandAction Command="{Binding Path=Closing}"
CommandParameter="{Binding Mode=OneTime,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type Window}}}"/>
</i:EventTrigger>
<l:InteractionMessageTrigger MessageKey="Close"
Messenger="{Binding Messenger}">
<l:WindowInteractionMessageAction/>
</l:InteractionMessageTrigger>
</i:Interaction.Triggers>
<i:Interaction.Behaviors>
<b:CloseButtonBehavior IsWindowCloseEnable="{Binding ElementName=CloseCheck,
Path=IsChecked}"/>
</i:Interaction.Behaviors>
<Grid Background="#FFE6E6FA">
<Button Command="{Binding Path=GotoCommand63-2}" Content="Goto63-2"
FontSize="16" Foreground="White" Width="470" Height="35"
Margin="40,0,503,266" Style="{StaticResource CommandTemplate}"
VerticalAlignment="Bottom" />
<v:UserControlFooterN2 Margin="0,0,0,0" HorizontalAlignment="Left"
VerticalAlignment="Bottom" />
</Grid>
</Window>
Window2のViewModelであるViewModel2.csは下記のようになります。
ViewModelのコンストラクタでは、画面が呼び出された時は呼び出した親画面をHide(隠し)をします。
この時に、処理が重くて画面にちらつきが生じないないように、33,39,43行目の記述で、親画面のHideを遅らせています。
31,39,58行目を取って、40行目のコメントをはずすと、少々重い画面ですとちらつきが出ます。
開いている画面を閉じる時は48行目で親画面をShow(表示)します。
ViewModel
using System;
// ObservableCollection
using System.Collections.Generic;
using System.Linq;
//INotifyPropertyChanged
//PropertyChanged
using System.ComponentModel;
//参照設定が必要
//using System.Configuration;
using Livet;
using Livet.Commands;
using Livet.Messaging;
//CloseCommand
using Livet.Messaging.Windows;
//ICommand
using System.Windows.Input;
using System.Windows;
using Common;
namespace LivetWPFApplication100.ViewModels
{
class ViewModel2 : ViewModel
{
Window win = null;
public ViewModel2()
{
Loaded = new Livet.Commands.ListenerCommand<Window>((w) =>
{
if (NeedHideOwner && w.Owner != null &&
w.Owner.Visibility == Visibility.Visible)
{
win = w;
//w.Owner.Hide();
}
});
Closing = new Livet.Commands.ListenerCommand<Window>((w) =>
{
if (NeedHideOwner && w.Owner != null)
{
w.Owner.Show();
}
});
}
public bool NeedHideOwner { get; set; }
public ICommand Loaded { get; private set; }
public ICommand Closing { get; private set; }
public void Initialize()
{
if (win != null) win.Owner.Hide();
XCloseButtonManager.Disable();
}