Livetを使ったC#のWPFプログラミング

システムとして機能が網羅されたサンプルをもとに、Livetを使ったC#のWPFシステムの開発技能を身に付ける為の学習サイト


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
    }
}