8-2.画面遷移の骨格
戻る
従来のWindowsフォームアプリケーションの画面遷移
通常はボタンをクリックして画面遷移します。
遷画面移元(親)
// 親画面で子画面を開く時 private void button1_Click(object sender, EventArgs e) { Form F = new クラス名(); F.Owner = this; //F.ShowDialog(this); ダイアログで開く時 F.Show(); F.Owner.Hide(); }
遷移先(子画面)
// 子画面を閉じて親画面を開く時 private void button2_Click(object sender, EventArgs e) { //this.Close(); 終了する時 this.Owner.Show(); this.Close(); }
上記の例は、Windowsフォームアプリケーションでは、親画面でButtonをWクリックして、button1_Clickメソッドを自動作成させて、その内に画面遷移のコードを書きます。子画面では「閉じる」のButtonをWクリックして、button2_Clickメソッドを自動作成させて、その内に画面遷移のコードを書きます。つまりデザイン画面でButtonをWクリックすればbuttonX_Clickメソッドが自動作成されます。WPFでも同様にClickメソッドを自動作成させて、その内に処理のコードを書くという簡単な方法はあります。(後述のコードビハインドによるコーディング)
しかし、折角、WPFを勉強するならば、次から説明するMVVMを採用すべきです。MVVMは画面とコードをファイルで分離します。そのために、ちょっとコーディング量が多くなりますが慣れれば既に書いたコードをコピーするという方法をとれば面倒さも軽減します。
ここのサンプルのダウンロード(LivetWPFApplicationBasic.lzh)WPFのMVVMでの画面遷移
Buttonクリックによる画面遷移を実装するには、
XAMLでは、
①Buttonをクリックした時のトリガーの記述
②Buttonに画面遷移の処理を記述するCommandの記述
ViewModelでは、
③遷移元画面をタスクバーに残さない記述方法
④画面遷移の処理を記述するICommandと言うメソッドの記述
が必要となります。
例ainWiえば、Mndowの「Window2へ」ボタンを押してWindow2ヘ画面遷移する例です。
Windows2が表示されても、下のタスクバーには、Window2のみが表示されて、MainWindowは表示されていません。
又、Window2を表示してからMainWindowを隠すので、画面の途切れが発生しないのでちらつきは発生しません。
MainWindowのXAMLは下記のようになります。
12~14行は、ViewとViewModelの紐付けの設定です。。
Interaction.Triggersは、
21~22行は、「Initialize」処理」を実行します。
26~28行は、「Window2への画面遷移」を実行します。
Interaction.Behaviorsは、
33~36行は、「コントロールボックスの閉じるボタンの非使用化」を実行します。
39~41行は、「「Window2への画面遷移」ボタンの実行します。
43~45行は、「「Window2への画面遷移」ボタンの実行します。
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="300" Width="450" WindowStartupLocation="CenterScreen"> <Window.DataContext> <vm:MainViewModel /> </Window.DataContext> <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> <Button Command="{Binding GotoCommand2}" Content="Window2へ" Height="32" HorizontalAlignment="Left" Margin="34,34,0,0" Name="button2" VerticalAlignment="Top" Width="147" /> <Button Command="{Binding CloseCommand}" Content="終了" Height="32" HorizontalAlignment="Left" Margin="319,217,0,0" Name="button1" VerticalAlignment="Top" Width="97" /> </Grid> </Window>
MainWindow.xamlとMainViewModel.csの紐付けは、MainWindow.xamlのコードビハインドファイルであるMainWindow.xaml.csのコンストラクタに「this.DataContext = new MainViewModel();」とも記述出来ます。
コードビハインドによるViewとViewModelの紐付け
public MainWindow() { InitializeComponent(); this.DataContext = new MainViewModel(); }
ZAMLのButton(39~41行)をクリックした時の処理(命令)はCommandと呼ばれています。このCommandはViewModelの「GotoCommand2」メソッドと紐付け(Binding)されています。
MainWindowのViewModelであるMainViewModelは下記のようになります。
GotoCommand2(ViewModel)
public ViewModelCommand GotoCommand2 { get { return new Livet.Commands.ViewModelCommand(Goto2); } } public void Goto2() { Messenger.Raise(new TransitionMessage(new ViewModel2() { NeedHideOwner = true }, "MessageKey2")); }
ZAMLのButton(43~45行)をクリックした時の処理(命令)は「CloseCommand」メソッドと紐付け(Binding)されています。
CloseCommand(ViewModel)
private ViewModelCommand _CloseCommand; public ViewModelCommand CloseCommand { get { if (_CloseCommand == null) { _CloseCommand = new ViewModelCommand(Close); } return _CloseCommand; } } public void Close() { var window = Application.Current.Windows.OfType<Window>().SingleOrDefault((w) => w.IsActive); }
遷移先の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:vm="clr-namespace:LivetWPFApplication100.ViewModels" xmlns:b="clr-namespace:LivetWPFApplication100.Behaviors" Title="Window2" Height="300" Width="450" WindowStartupLocation="CenterScreen"> <i:Interaction.Triggers> <!--WindowのContentRenderedイベントのタイミングでViewModelの Initializeメソッドが呼ばれます--> <i:EventTrigger EventName="ContentRendered"> <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName= "Initialize"/> </i:EventTrigger> <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> <Button Command="{Binding CloseCommand}" Content="閉じる" Height="32" HorizontalAlignment="Left" Margin="319,217,0,0" Name="button1" VerticalAlignment="Top" Width="97" /> </Grid> </Window>
Window2のViewModelであるViewModel2.csは下記のようになります。
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; namespace LivetWPFApplication100.ViewModels { class ViewModel2 : ViewModel { public ViewModel2() { var Loaded = new Livet.Commands.ListenerCommand<Window>((w) => { if (NeedHideOwner && w.Owner != null && w.Owner.Visibility == Visibility.Visible) { w.Owner.Hide(); } }); var 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() { XCloseButtonManager.Disable(); } #region CloseCommand private ViewModelCommand _CloseCommand; public ViewModelCommand CloseCommand { get { if (_CloseCommand == null) { _CloseCommand = new ViewModelCommand(Close); } return _CloseCommand; } } public void Close() { var window = Application.Current.Windows.OfType<Window>(). SingleOrDefault((w) => w.IsActive); window.Close(); } #endregion } }