1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【WPF】Window、Page、UserControlの違い【C#】

Last updated at Posted at 2024-11-02

概要

WPFでは「Window」「Page」「UserControl」のように様々なView用のコントロール (クラス) があります。Viewひとつとっても、この3つの中から適切なものを選択することで、アプリケーションの設計が向上します。
本稿ではこのView用の3つのコントロールについて、違いをまとめてみました。
また、WindowにPageを埋め込む、PageにUserControlを埋め込む等、いくつかサンプルコードも記載しました。

Windowとは

独立したウィンドウとして扱われます。
他のWindowやPage、UserControlのホストになる存在です。

Pageとは

Window内のページ内の一部を表現するものです。
単独では独立したウィンドウとして表示はできないため、独立したウィンドウとしての機能は持っていません。
なので、他のコントロール内 (WindowやPageなど) でのみ動作します。
入れ子のように、PageにPageを埋め込むことも可能です。

UserControlとは

再利用できるUIコンポーネントを作成するために使用されます。
カスタムコントロールを作成するための基盤クラスになるイメージです。
他のWindowやPageの一部分に追加するような形で使用するのが一般的です。
なのでPageと同様、UserControl単体では独立したウィンドウとして表示できず、あくまで他のコントロール内で使用されることを前提としたものになります。
Pageと同様入れ子のように、UserControlにUserControlを埋め込むことも可能です。

サンプルコード

以下にWindowにPageを埋め込む、PageにUserControlを埋め込むサンプルコードを記載しました。あくまでサンプルコードで、示した例以外にもいくつか方法はあります (あると思います)。

WindowにPage (UserControl) を入れるサンプルコード

View

HogeWindow.xaml
<Window x:Class="HogeNamespace.HogeWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       	xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        Title="HogeWindow" Height="500" Width="700">
        <Window.DataContext>
    		<vm:HogeWindowViewModel />
    	</Window.DataContext>
    	<i:Interaction.Triggers>
    		<i:EventTrigger EventName="Loaded">
    			<i:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding ElementName=HogeWindow}" />
    		</i:EventTrigger>
    	</i:Interaction.Triggers> 
    <Grid>
        <!-- メインコンテンツ -->
        <StackPanel>
            <Button Content="ボタン1" Click="Button_Click"/>
        </StackPanel>
        <!-- フレーム(Pageを表示するためのコンテナ) -->
        <Frame x:Name="SampleFrame" />
    </Grid>
</Window>

ViewModel

HogeWindowViewModel.cs
    public class HogeWindowViewModel
    {
        /// <summary>
        /// Loadedイベント
        /// </summary>
        public ICommand LoadedCommand => _LoadedCommand ?? (_LoadedCommand = new DelegateCommand<Window>((window) =>
        {      
        // HogePageという名前のPageをnewする
        var hogePage = new HogePage();
        var pageVM = hogePage.DataContext as HogePageViewModel;

        // HogeWindowにHogePageをセット
        var frame = _window.FindName("ContentFrame") as Frame;
        frame.Content = hogePage;
        
        }));
        private DelegateCommand<Window> _LoadedCommand;
    }

ViewのInteraction.Triggers部分について

上記のコードはInteraction.TriggersInvokeCommandActionを使用して、Windowのロード時にViewModelでロード処理を行うようにできます。
以下に少し説明を記載しました。

Interaction.Triggers

Interaction.TriggersはUIの要素に対し、イベントのトリガーを設定するためのものです。
EventNameを指定して、ここではwindowのロードが終わったタイミング (Loaded) をトリガーとして指定しています。

InvokeCommandAction

InvokeCommandActionを使用することで、ViewModelのメソッドを呼び出すことができます。
CommandプロパティでViewModelのコマンドを指定でき、CommandParameterでコマンドに渡すパラメータを指定しています。
また、ElementNameは現在のXAMLファイルで定義されている要素を参照するために使用され、XAML内の他の要素からデータをバインドすることができます。ここではHogeWindowを指定して、Window自体をViewModelにバインドしています。

Frameについて

FrameはPageやUserControlを表示するためのコンテナとして使用されます。

ViewModelのICommandについて

ICommandインターフェースを実装することで、Viewで定義したCommandプロパティからViewModelのコマンドを呼び出すことができます。
:::

ViewModelのDelegateCommandについて

DelegateCommandはprism (WPFのMVVMパターンでの開発を支援するフレームワーク) で提供されているコマンドクラスです。

上記のPage部分をUserControlに変更すると、WindowにUserControlも追加できます。
※UserControlにはTitleプロパティは無いので要注意です。

PageにUserControlを入れるサンプルコード1

HogeControl.xaml
<UserControl x:Class="HogeNamespace.HogeControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
    <Grid>
        <!-- メインコンテンツ -->
    </Grid>
</UserControl>
HogeControlViewModel.cs
    public class HogeControlViewModel
    {
        // 省略
    }
HogePage.xaml
<Page
	x:Class="HogeNamespace.HogePage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:view="clr-namespace:HogeNamespace">
    <StackPanel>
        <view:HogeControl DataContext="{Binding HogeControlViewModel}" />
    </StackPanel>
HogePageViewModel.cs
    public class HogePageViewModel
    {
        // 省略
    }

ViewのDataContext部分について

DataContextは上記のxamlのように<view:HogeControl DataContext="{Binding HogeControlViewModel}" />のような書き方で指定することも可能です。

PageにUserControlを入れるサンプルコード2

PageにUserControlを入れるサンプルコード1の応用として、下記のようにも実装できます。

HogeControl1.xaml
<UserControl x:Class="HogeNamespace.HogeControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:converter="Hoge.Converters"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
    <UserControl.Resources>
        <ResourceDictionary>
            <converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}">
        <!-- HogeControl1のメインコンテンツ -->
    </Grid>
</UserControl>
HogeControl1ViewModel.cs
    public class HogeControl1ViewModel
    {
        public bool IsVisible {get; set; }

        // HogeControl1の処理 (省略)
    }
HogeControl2.xaml
<UserControl x:Class="HogeNamespace.HogeControl2"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:converter="Hoge.Converters"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
    <UserControl.Resources>
        <ResourceDictionary>
            <converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}">
        <!-- HogeControl2のメインコンテンツ -->
    </Grid>
</UserControl>
HogeControl2ViewModel.cs
    public class HogeControl2ViewModel
    {
        public bool IsVisible {get; set; }

        // HogeControl2の処理 (省略)
    }
HogeControl3.xaml
<UserControl x:Class="HogeNamespace.HogeControl3"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:converter="Hoge.Converters"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
    <UserControl.Resources>
        <ResourceDictionary>
            <converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}">
        <!-- HogeControl3のメインコンテンツ -->
    </Grid>
</UserControl>
HogeControl3ViewModel.cs
    public class HogeControl3ViewModel
    {
        public bool IsVisible {get; set; }

        // HogeControl3の処理 (省略)
    }
HogePage.xaml
<Page
	x:Class="HogeNamespace.HogePage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:view="clr-namespace:HogeNamespace">
    <StackPanel>
    <!-- Page上部の定義 (省略) -->
        <StackPanel>
        <!-- Page下部の定義 -->
            <view:HogeControl DataContext="{Binding ParentViewModel.HogeControl1ViewModel}" />
            <view:HogeControl DataContext="{Binding ParentViewModel.HogeControl2ViewModel}" />
            <view:HogeControl DataContext="{Binding ParentViewModel.HogeControl3ViewModel}" />
        </StackPanel>
    <StackPanel>
HogePageViewModel.cs
    public class HogePageViewModel
    {   
        // 画面上部のViewModel側の実装 (省略)

        // 画面下部部分のViewModelをまとめたクラス
        public ParentViewModel ParentViewModel { get; set; }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public HogePageViewModel()
        {
            ParentViewModel = new ParentViewModel();
        }
    }
ParentViewModel.cs
    public class ParentViewModel
    {
       public HogeControl1ViewModel HogeControl1ViewModel { get; }

       public HogeControl2ViewModel HogeControl2ViewModel { get; }

       public HogeControl3ViewModel HogeControl3ViewModel { get; }
       
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public ParentViewModel()
        {
            HogeControl1ViewModel = new HogeControl1ViewModel();
            HogeControl2ViewModel = new HogeControl2ViewModel();
            HogeControl3ViewModel = new HogeControl3ViewModel();
        }
    }

上記のように、HogePage.xamlの画面の下部を定義している部分に、UserControl (HogeControl) が3つ定義されているとします。
3つのHogeControlのViewModelにはそれぞれVisibleプロパテを定義しておき、必ず1つのみVisible = trueとなり、それ以外の2つはVisible = falseとなるよう実装しておきます。(その実装はここでは省略しています)
そうすると、HogePage.xamlの画面下部分は、Visible = trueとなっているHogeControlのみ表示されるようになります。なので特定の操作での処理で、表示するHogeControlを変えてあげると、ユーザーの操作に合わせてHogePageの画面下部に表示されるUserControlの種類を変えることができます。

また、ここではコードが煩雑にならないようHogePageViewModel.csに3つのHogeControlをまとめたクラス (ParentViewModel) を作成しました。このようにしても、HogePage.xamlからはDataContext="{Binding ParentViewModel.HogeControl3ViewModel}"のようにドット.を使用して各HogeControlを呼び出す定義が行えます。

xamlでbool型をVisibility型に変換するConverterクラスを使用していますが、Converterクラスについては下記を参照ください。
【WPF】Converterの作成方法【C#】

おわりに

xamlは結構複雑なところもあるので、記事を記載していくことで自分の頭も整理される感覚になりました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?