前回は、WPF アプリケーション全体を管理する Application クラスについて見ました。
今回は、実際に画面を表す Window クラスを取り上げます。
Application がアプリケーション全体を管理するクラスだとすれば、Window はユーザーが実際に操作する画面の単位です。タイトルバー、サイズ変更、最小化、最大化、閉じるボタン、そしてその中に配置される UI など、デスクトップアプリケーションの「1 つのウィンドウ」を表します。
ただし、Window は単独で存在しているクラスではありません。WPF の多くの機能は、継承階層の中で少しずつ積み上げられています。
この記事では、Window クラスの役割と、そこに至る主要な基底クラスのうち、次の 3 つを整理します。
System.Windows.Controls.ContentControlSystem.Windows.Controls.ControlSystem.Windows.FrameworkElement
残りの基底クラスである UIElement、Visual、DependencyObject、DispatcherObject については、次の記事で扱います。
Window クラスの位置づけ
Window クラスは、System.Windows 名前空間に属します。
代表的な継承階層は次のようになります。
DispatcherObject
└─ DependencyObject
└─ Visual
└─ UIElement
└─ FrameworkElement
└─ Control
└─ ContentControl
└─ Window
この階層を見ると、Window は「単にタイトルバー付きの入れ物」ではないことが分かります。
Window は、次のような機能をすべて継承しています。
| クラス | Window に与える主な機能 |
|---|---|
ContentControl |
1 つのコンテンツを持てる |
Control |
テンプレート、フォント、前景色、背景色などを持てる |
FrameworkElement |
レイアウト、名前、リソース、データコンテキストを持てる |
UIElement |
入力、フォーカス、イベント、表示状態を扱える |
Visual |
描画ツリー上の要素として表示できる |
DependencyObject |
依存関係プロパティを持てる |
DispatcherObject |
UI スレッドに関連付けられる |
WPF の Window は、これらの機能の上に、ウィンドウ固有の機能を追加したクラスです。
Window の役割
Window の主な役割は、WPF アプリケーションのトップレベルウィンドウを表すことです。
通常、WPF アプリケーションでは次のような XAML でウィンドウを定義します。
<Window x:Class="SampleApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Sample"
Width="400"
Height="250">
<Grid>
<Button Content="OK" Width="100" Height="32" />
</Grid>
</Window>
この例では、Window がアプリケーションの画面全体を表し、その中に Grid と Button が配置されています。
コードだけで書くと、次のようになります。
using System.Windows;
using System.Windows.Controls;
Window window = new Window
{
Title = "Sample",
Width = 400,
Height = 250,
Content = new Grid
{
Children =
{
new Button
{
Content = "OK",
Width = 100,
Height = 32
}
}
}
};
window.Show();
Window はトップレベルの表示領域であり、その中に 1 つの Content を持ちます。実際の画面では、その Content に Grid や StackPanel などのレイアウトパネルを置き、その中に複数のコントロールを配置します。
Window の主要メンバー
Window には、ウィンドウ固有の状態や動作を表すメンバーが用意されています。
| メンバー | 役割 |
|---|---|
Title |
タイトルバーに表示する文字列 |
Width / Height |
ウィンドウの幅と高さ |
MinWidth / MinHeight |
最小サイズ |
MaxWidth / MaxHeight |
最大サイズ |
WindowState |
通常、最小化、最大化の状態 |
WindowStyle |
タイトルバーや境界線のスタイル |
ResizeMode |
サイズ変更できるかどうか |
Topmost |
他のウィンドウより前面に表示するか |
Owner |
親ウィンドウ |
DialogResult |
ダイアログの結果 |
Show() |
モードレスでウィンドウを表示する |
ShowDialog() |
モーダルダイアログとして表示する |
Close() |
ウィンドウを閉じる |
たとえば、サイズ変更できないダイアログ風のウィンドウは次のように作れます。
Window dialog = new Window
{
Title = "設定",
Width = 360,
Height = 240,
ResizeMode = ResizeMode.NoResize,
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
Show() は、ウィンドウを表示したあとも呼び出し元の処理が続きます。
window.Show();
Console.WriteLine("ウィンドウ表示後も処理が続きます。");
一方、ShowDialog() は、表示したウィンドウが閉じられるまで呼び出し元の処理を待機します。
bool? result = dialog.ShowDialog();
if (result == true)
{
// OK が押された場合の処理
}
設定画面、確認画面、ファイル選択のように、ユーザーの操作結果を待ってから次に進みたい場合は ShowDialog() を使います。
Window のイベント
Window には、ウィンドウのライフサイクルに関するイベントがあります。
| イベント | 発生タイミング |
|---|---|
Initialized |
初期化されたとき |
Loaded |
レイアウトされ、表示準備が整ったとき |
ContentRendered |
コンテンツの描画が完了したとき |
Closing |
閉じようとしているとき |
Closed |
閉じられたあと |
Activated |
アクティブになったとき |
Deactivated |
非アクティブになったとき |
特によく使うのは Loaded と Closing です。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += (s, e) =>
{
// 画面表示後の初期化
};
Closing += (s, e) =>
{
// 終了確認
bool canClose = ConfirmClose();
if (!canClose)
{
e.Cancel = true;
}
};
}
}
Closing イベントでは、CancelEventArgs.Cancel を true にすると、ウィンドウを閉じる処理をキャンセルできます。
System.Windows.Controls.ContentControl
ContentControl は、1 つのコンテンツを持つコントロールの基底クラスです。
Window が Content プロパティを持てるのは、ContentControl を継承しているためです。
<Window>
<Grid>
<Button Content="OK" />
</Grid>
</Window>
この XAML では、Window の Content に Grid が入っています。
ContentControl の主要メンバーは次の通りです。
| メンバー | 役割 |
|---|---|
Content |
表示する 1 つのコンテンツ |
ContentTemplate |
コンテンツの表示方法を定義するテンプレート |
ContentTemplateSelector |
条件に応じてテンプレートを選択する |
ContentStringFormat |
コンテンツを文字列表示するときの書式 |
ContentControl の特徴は、コンテンツが文字列に限られないことです。
<Button>
<StackPanel Orientation="Horizontal">
<TextBlock Text="保存" />
<TextBlock Text=" Ctrl+S" Foreground="Gray" />
</StackPanel>
</Button>
この例では、Button の Content に StackPanel が入っています。Button も ContentControl を継承しているため、文字列だけでなく任意の UI 要素を内容として持てます。
Window も同じです。ただし、Window は 1 つの Content しか直接持てないため、複数の要素を置きたい場合は Grid や StackPanel のようなパネルを中に入れます。
System.Windows.Controls.Control
Control は、ユーザーが操作する UI 部品の基底クラスです。
Button、TextBox、ListBox、ComboBox など、多くの WPF コントロールは Control を継承しています。Window も直接操作される表示要素として、Control の機能を受け継いでいます。
Control の主要メンバーは次の通りです。
| メンバー | 役割 |
|---|---|
Template |
コントロールの見た目を定義するテンプレート |
Background |
背景ブラシ |
Foreground |
前景ブラシ |
BorderBrush |
境界線のブラシ |
BorderThickness |
境界線の太さ |
FontFamily |
フォント |
FontSize |
フォントサイズ |
FontWeight |
フォントの太さ |
HorizontalContentAlignment |
コンテンツの水平配置 |
VerticalContentAlignment |
コンテンツの垂直配置 |
WPF の大きな特徴のひとつは、コントロールの動作と見た目が分離されていることです。
Control.Template を差し替えると、コントロールの機能は保ったまま、見た目を大きく変更できます。
<Button Content="OK"
Background="SteelBlue"
Foreground="White"
FontSize="16"
Padding="16,8" />
この例では、Button の背景色、文字色、フォントサイズ、余白を設定しています。これらのプロパティは Control 由来のものです。
Window では Button ほど頻繁に Template を変更することはありませんが、Control の系統にいるため、スタイルやテンプレートという WPF らしい仕組みの上に成り立っています。
System.Windows.FrameworkElement
FrameworkElement は、WPF のフレームワークレベルの UI 要素の基底クラスです。
FrameworkElement は、レイアウト、データバインディング、リソース、名前、サイズ、親子関係など、アプリケーション開発でよく使う機能を提供します。
主要メンバーは次の通りです。
| メンバー | 役割 |
|---|---|
Name |
要素の名前 |
Width / Height |
幅と高さ |
MinWidth / MinHeight |
最小サイズ |
MaxWidth / MaxHeight |
最大サイズ |
ActualWidth / ActualHeight |
実際にレイアウトされたサイズ |
Margin |
外側の余白 |
HorizontalAlignment |
水平方向の配置 |
VerticalAlignment |
垂直方向の配置 |
DataContext |
データバインディングの基準となるデータ |
Resources |
要素に紐づくリソース |
Style |
適用するスタイル |
Loaded / Unloaded |
読み込み、解放時のイベント |
たとえば、Window に Width や Height を設定できるのは、FrameworkElement からサイズ関連の仕組みを継承しているためです。
<Window Width="400"
Height="250"
WindowStartupLocation="CenterScreen">
</Window>
また、DataContext も FrameworkElement 由来の重要なプロパティです。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
DataContext は子要素に継承されるため、Window に設定しておくと、その中の TextBox や Button から同じ ViewModel を参照できます。
<TextBox Text="{Binding UserName}" />
Resources も重要です。Window.Resources に定義したスタイルやブラシは、そのウィンドウ内の要素から参照できます。
<Window.Resources>
<SolidColorBrush x:Key="AccentBrush" Color="SteelBlue" />
</Window.Resources>
FrameworkElement は、WPF の UI 要素を「アプリケーションで扱いやすい部品」にするための機能をまとめて提供しているクラスです。
Window、Page、UserControl の違い
Window と混同しやすいものに、Page と UserControl があります。
| クラス | 役割 |
|---|---|
Window |
トップレベルのウィンドウ |
Page |
ナビゲーション内で表示されるページ |
UserControl |
再利用可能な UI 部品 |
Window は、タイトルバーや閉じるボタンを持つ独立した画面です。
Page は、NavigationWindow や Frame の中で表示されるページです。Web ブラウザーのような「戻る」「進む」を持つ画面構成で使われます。
UserControl は、画面の一部を部品化するためのクラスです。複数の画面で同じ入力フォームや一覧部品を使いたい場合に便利です。
アプリケーション全体のメイン画面には Window、画面の一部分を再利用したい場合は UserControl、ページ遷移型の構成では Page を使う、と考えると分かりやすいです。
まとめ
Window は、WPF アプリケーションのトップレベルウィンドウを表すクラスです。
タイトル、サイズ、表示位置、ウィンドウ状態、閉じる処理、ダイアログ表示など、デスクトップアプリケーションの画面として必要な機能を提供します。
一方で、Window の機能は Window クラスだけで完結しているわけではありません。
ContentControl からは 1 つのコンテンツを持つ仕組み、Control からはテンプレートやフォントなどのコントロール共通機能、FrameworkElement からはレイアウト、リソース、データバインディングなどの仕組みを継承しています。
次回は、さらに基底側に進み、UIElement、Visual、DependencyObject、DispatcherObject が Window にどのような土台を与えているのかを見ていきます。