WPFで複数の画面を連携させるようなプログラムを作成する場合、画面間で何らかの値を受け渡しする必要が生じます。
今回は、面遷移におけるデータ連携と、画面をDLL化して再利用できるようにする方法について解説したいと思います。
ちなみに、記事のタイトルに「プロジェクト間」と記載していますが、プロジェクト内にあろうが無かろうが、画面の遷移方法、値の受け渡し方法は全く同じです。
サンプルプログラムの概要
ボタンをクリックすると子画面に遷移し、子画面で押されたボタンや入力された文字列を返すというシンプルな画面のサンプルです。
今回はダイアログという形で画面遷移を行っていますが、もっと複雑な画面に遷移して値を受け渡しする場合も全く同じです。
サンプルプロジェクトのダウンロード
サンプル画面のプロジェクト一式は下記からダウンロードできます。
WinDllText プロジェクト一式
画面の呼び出し方法
例えば、MessageDialog という名前でWindowを作成したとします。
この場合は次の様になります。
Window dlg = new ImputDialog();
dlg.ShowDialog()
dlg.ShowDialog() はモーダル(表示したウィンドウ以外は操作できない)状態で開きますが、dlg.Show() にするとモードレス(表示したウインドウと呼び出し元のウィンドウの両方が操作できる)状態になります。
今回のサンプルソースでは、引数に親画面のWindowと表示したい文字列を渡して画面呼び出し(画面遷移)をしていますが、これは子画面(遷移先)のコンストラクタで2つの引数を受け取るようにしているからです。
new MessageDialog(this, str).ShowDialog()
わざわざ第一引数に呼び出し元のWindow(this)を渡しているのは、親画面の中央に子画面を表示させたいという理由です。
画面遷移における値の受け渡し方法
呼び出し先(遷移先)へ値を渡して、遷移先で処理された値を受け取る方法は下図の様にいくつかの方法が考えられますが、サンプルでは一番シンプル且つ一般的な方法として、プロパティとDialogResult を採用しています。
値の渡し方
遷移先への値の渡し方は、コンストラクタかプロパティを使います。
例えば、サンプルプログラムでは ImputDialog というWindowを作成していますが、ここではコンストラクタとプロパティを使って値のやり取りをしています。
public partial class ImputDialog : Window
{
/// <summary>
/// 編集した入力値を呼び出し元に返すためのプロパティ
/// </summary>
public string Text { get { return uxImputText.Text; } set { uxImputText.Text = value; } }
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="owner">呼び出し元のWindow</param>
/// <param name="str">編集したい文字列</param>
public ImputDialog(Window owner,string str)
{
InitializeComponent();
this.Owner = owner; //呼び出し元のWindow
this.WindowStartupLocation = WindowStartupLocation.CenterOwner; //起動時の表示位置を親画面の中央に合わせる
uxImputText.Text = str;//引数で渡された文字列をテキストボックスにセット
}
}
値の受け取り方
DialogResultは、ShowDialog() メソッドで表示した場合にのみ有効な値の受け取り方で、 true 、 false、null の3種類しか値を返すことが出来ませんが、Windowの戻り値として受け取ることが出来ます。
オリジナルのダイアログボックスを用意したい場合や、遷移先の操作が有効(又は確定)か無効(又は中止)かを判定したい場合はDialogResult が便利です。
private void uxOK_Click(object sender, RoutedEventArgs e)
{
//ダイアログの戻り値として true をセット
DialogResult = true;
}
一方、4個以上のボタンがあってどれを押されたかを識別したい場合や、文字列や数値、或いは何らかのオブジェクトを受け取りたい場合は、プロパティを利用するのが一番簡単です。
例えば、下記は uxImputText と言う名前のテキストボックスがあって、その中身(編集結果)を呼び出し元に返す場合のサンプルです。
public string Text { get { return uxImputText.Text; } set { uxImputText.Text = value; } }
プロパティを使うと、呼び出し側から初期値をセットすることが出来るというメリットもあります。
画面のDLL化と呼び出し方法
何故記事のタイトルに「プロジェクト間」という文言を入れたかと言うと、画面をDLL化して再利用したいからです。
再利用したい画面を別プロジェクトに固めておくと、他のプロジェクトで使う場合の取り回しが楽になるため、あえてこうしました。
さて、画面をDLLにすると何が嬉しいかと言うと、Windowを1つの部品として取り扱えるようになるからです。
実行ファイル(EXE)のまま再利用するのであれば、System.Diagnostics.Process.Start()を使って呼び出す必要がありますし、値の受け渡しはファイルを経由するか、共有メモリを利用するか、プロセス間通信を利用するか、いずれにせよ面倒な手順を踏まなければなりません。
しかし、DLLにしておけば既存のクラスのインスタンスを生成して、プロパティやメソッドで値の受け渡しが出来る様になるため、面倒な処理は必要無くなります。
ただ、残念なことに、画面をDLL化するには、少しだけ手を加えてあげる必要があります。
画面をDLL化するための方法
画面の作り方は従来通り、プロジェクト名の右クリック⇒追加⇒ウィンドウ(WPF)で新しいWindowを作ってあげるだけなのですが、DLL化したい画面が入っているプロジェクトのプロパティと、App.xaml のプロパティについて、それぞれ1か所つづ次の様に変更します。
最初に、Visual Stuidoの一番上にあるメニューから「プロジェクト」をクリックし、表示されるメニューの一番下にある「○○○○のプロパティ」を選択します。
するとアプリケーションのプロパティが表示されるので、出力の種類を「クラスライブラリ」に変更して下さい。
続けて、今度はDLL化したい画面が入っているプロジェクトの中にApp.xaml というファイルがあるので、これをクリックして、プロパティのビルドアクションを「なし」に変更します。
ビルドアクションを「なし」に変更せずにビルドするとビルドエラーになりますので、必ず「なし」に設定して下さい。
以上の設定を行うと、DLL化したい画面が入っているプロジェクトをビルドしても、EXEではなくDLLが生成されるようになります。
DLL化した画面の呼び出し方
別プロジェクトに含まれている画面を表示することになるので、参照設定が必要となります。
次の図の通り①、②、③、とクリックし、④でチェックを入れて、⑤をクリックすれば完了です。
この操作を行った後、C#のソースコード上で using を記述すれば、後は普通のWindowと全く同じ手順で呼び出しが可能です。
例えば、今回のサンプルでは、名前空間(namespace)が MyMessageBox になっていますので、冒頭に次の1行を記述しておきます。
using MyMessageBox;
後は、次の様に通常のWindowを呼び出す手順で画面遷移が行えます。
new MessageDialog(this, str).ShowDialog();
サンプルソースの解説
それでは、実際のソースコードを載せておきます。
遷移元(親画面)の画面イメージ
遷移元(親画面)のXAML
<Window x:Class="WinDllText.MainWindow"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WinDllText"
mc:Ignorable="d"
Title="MainWindow" Height="348.555" Width="746.676">
<Grid>
<Button x:Name="uxCallMessageDialog" Content="メッセージダイアログ呼び出し" HorizontalAlignment="Left" Margin="85,58,0,0" VerticalAlignment="Top" Width="156" Click="uxCallMessageDialog_Click"/>
<Button x:Name="uxCallImputDialog" Content="入力ダイアログ呼び出し" HorizontalAlignment="Left" Margin="85,92,0,0" VerticalAlignment="Top" Width="156" Click="uxCallImputDialog_Click"/>
<TextBox x:Name="uxGetMessage" IsReadOnly="True" HorizontalAlignment="Left" Height="23" Margin="281,92,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="316"/>
</Grid>
</Window>
遷移元(親画面)のC#コード
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using MyMessageBox;
namespace WinDllText
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
/// <summary>
/// コンストラクタ
/// </summary>
public MainWindow()
{
InitializeComponent();
}
//=========================================================
// ボタンのイベントハンドラ
//=========================================================
/// <summary>
/// メッセージダイアログの表示ボタンクリック処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxCallMessageDialog_Click(object sender, RoutedEventArgs e)
{
MessageDialog("実行してもよろしいですか?");
}
/// <summary>
/// 入力ダイアログの表示ボタンのクリック処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxCallImputDialog_Click(object sender, RoutedEventArgs e)
{
uxGetMessage.Text = ImputDialog(uxGetMessage.Text);
}
//=========================================================
// ダイアログ表示用メソッド
//=========================================================
/// <summary>
/// メッセージダイアログの表示
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private bool MessageDialog(string str)
{
return (bool)(new MessageDialog(this, str).ShowDialog());
}
/// <summary>
/// 入力ダイアログの表示
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private string ImputDialog(string str)
{
var dialog = new ImputDialog(this, str);
return ((bool)dialog.ShowDialog()) ? dialog.Text : str;
}
}
}
遷移先(子画面) MessageDialogの画面イメージ
遷移先(子画面) MessageDialogのXAML
<Window x:Class="MyMessageBox.MessageDialog"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyMessageBox"
mc:Ignorable="d"
Title="MessageDialog" Height="191.739" Width="466.087" ResizeMode="NoResize">
<Grid>
<Button x:Name="uxOK" Content="OK" HorizontalAlignment="Left" Margin="46,116,0,0" VerticalAlignment="Top" Width="152" Click="uxOK_Click"/>
<Button x:Name="uxCancel" Content="Cancel" HorizontalAlignment="Left" Margin="282,116,0,0" VerticalAlignment="Top" Width="152" Click="uxCancel_Click"/>
<Border BorderBrush="#FF14C5EC" BorderThickness="1" HorizontalAlignment="Left" Height="77" Margin="39,20,0,0" VerticalAlignment="Top" Width="382" CornerRadius="5">
<TextBlock x:Name="uxMessage" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top"/>
</Border>
</Grid>
</Window>
遷移先(子画面) MessageDialogのC#コード
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace MyMessageBox
{
/// <summary>
/// MessageDialog.xaml の相互作用ロジック
/// </summary>
public partial class MessageDialog : Window
{
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="owner">呼び出し元のWindow</param>
/// <param name="message">表示したい文字列</param>
public MessageDialog(Window owner,string message)
{
InitializeComponent();
this.Owner = owner; //呼び出し元のWindow
this.WindowStartupLocation = WindowStartupLocation.CenterOwner; //起動時の表示位置を親画面の中央に合わせる
uxMessage.Text = message; //引数で渡された文字列をテキストボックスにセット
}
/// <summary>
/// OKボタンクリック時の処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxOK_Click(object sender, RoutedEventArgs e)
{
//ダイアログの戻り値として true をセット
DialogResult = true;
}
/// <summary>
/// キャンセルボタンクリック時の処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxCancel_Click(object sender, RoutedEventArgs e)
{
//ダイアログの戻り値戻り値としてfalseをセット
DialogResult = false;
}
}
}
遷移先(子画面) ImputDialogの画面イメージ
遷移先(子画面) ImputDialogのXAML
<Window x:Class="MyMessageBox.ImputDialog"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyMessageBox"
mc:Ignorable="d"
Title="ImputDialog" Height="194.348" Width="511.739" ResizeMode="NoResize" WindowStartupLocation="Manual">
<Grid>
<TextBox x:Name="uxImputText" Height="58" AcceptsReturn="True" Margin="56,27,55,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
<Button x:Name="uxOK" Content="登録" HorizontalAlignment="Left" Height="24" Margin="56,106,0,0" VerticalAlignment="Top" Width="128" Click="uxOK_Click"/>
<Button x:Name="uxCancel" Content="中止" HorizontalAlignment="Right" Height="24" Margin="0,106,55,0" VerticalAlignment="Top" Width="128" Click="uxCancel_Click"/>
</Grid>
</Window>
遷移先(子画面) ImputDialogのC#コード
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace MyMessageBox
{
/// <summary>
/// ImputDialog.xaml の相互作用ロジック
/// </summary>
public partial class ImputDialog : Window
{
/// <summary>
/// 編集した入力値を呼び出し元に返すためのプロパティ
/// </summary>
public string Text { get { return uxImputText.Text; } set { uxImputText.Text = value; } }
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="owner">呼び出し元のWindow</param>
/// <param name="str">編集したい文字列</param>
public ImputDialog(Window owner,string str)
{
InitializeComponent();
this.Owner = owner; //呼び出し元のWindow
this.WindowStartupLocation = WindowStartupLocation.CenterOwner; //起動時の表示位置を親画面の中央に合わせる
uxImputText.Text = str;//引数で渡された文字列をテキストボックスにセット
}
/// <summary>
/// 登録ボタンクリック時の処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxOK_Click(object sender, RoutedEventArgs e)
{
//ダイアログの戻り値として true をセット
DialogResult = true;
}
/// <summary>
/// 中止ボタンクリック時の処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void uxCancel_Click(object sender, RoutedEventArgs e)
{
//ダイアログの戻り値戻り値としてfalseをセット
DialogResult = false;
}
}
}
まとめ
今回はWPFにおける画面(Winodw)の遷移方法と、その際の値の受け渡し方法、そして画面のDLL化について解説しました。
画面をDLL化する方法としてユーザーコントロールがありますが、ユーザーコントロールは画面に張りけて使うものであるため、必ず土台となるWindowが必要となります。
その点、WindowをDLL化しておけば、呼び出すだけで事が足りてしまうため、かなり便利になります。
ログイン画面やメッセージ系のダイアログ画面など、ちょっとした画面をDLL化するだけでも効果はありますので、是非お試しください。