連載
» 2010年08月03日 00時00分 公開

連載:WPF入門:第3回 XAMLコードから生成されるプログラム・コードを理解する ―― XAML(2): WPF固有機能の基礎 ―― (1/4)

XAMLコードから自動生成される中間生成物のプログラム・コード、依存関係プロパティとルーティング・イベントを解説。WPFの内部的な挙動を理解しよう。

[岩永信之(http://ufcpp.net),著]
連載:WPF入門
業務アプリInsider/Insider.NET

powered by Insider.NET

「連載:WPF入門」のインデックス

連載目次

 前回は主に、WPFによらないXAMLの一般的な仕組みについて説明を行った。今回からはWPF固有の機能に踏み込んで説明していく。

 まず、WPFの内部的な挙動の理解を深めてもらうため、XAMLコードから自動生成される中間生成物のプログラム・コード(C#/VB)について説明を行う。また、プログラムの起点となるApplicationクラス(System.Windows名前空間)に関する説明も行う(Applicationクラスと並んでWPFアプリケーションの基礎となるWindowクラスに関しては、「コントロール」の一種と見なせるため、次回以降、「コントロール」に関する回にて説明を行う)。

 さらに、データ・バインディングなどの高度な機能を実現するための要となる「依存関係プロパティ」と「ルーティング・イベント」に関する説明を行う。

■XAMLコードから自動生成される中間生成物のプログラム・コード

 前回からの再掲になるが、標準的なWPFアプリケーションの作りをもう一度見てみよう。Visual StudioのテンプレートからWPFアプリケーション・プロジェクトを作成すると、Figure 1に示すような状態が得られるはずだ。

Figure 1: Visual StudioでWPFアプリケーション・プロジェクト(C#)を作成した直後の状態

 前回は概要のみの説明だったが、今回はもう少し踏み込んで、XAMLコードから自動生成される中間生成物のプログラム・コード(.g.cs/.g.vbファイルなど。詳しくは第1回を参照)について説明していく。

MainWindow.xamlファイルから生成されるプログラム・コード

 MainWindow.xamlファイルの分離コードであるMainWindow.xaml.cs/MainWindow.xaml.vbファイル内に生成されるMainWindowクラスは、名前どおり、アプリケーション起動時に最初に開かれるメイン・ウィンドウで、その実体はWindowクラス(System.Windows名前空間)を継承したクラスである。MainWindowクラスはパーシャル・クラスとなっていて、MainWindow.xamlファイルから自動生成されるプログラム・コードと合わせて1つのクラスとなる。

 Visual Studioのテンプレートから生成されたままのMainWindow.xamlファイル(+その分離コード)をビルド(=コンパイル)すると、List 1に示すようなプログラム・コードが(objフォルダ内に「MainWindow.g.cs/MainWindow.g.vb」などの名前で)自動生成される(見やすさを優先して、属性やコメントなどを一部削っている)。

using System.Windows;
using System.Windows.Markup;
 
namespace SampleWpfApplication
{
  public partial class MainWindow : Window, IComponentConnector
  {
    private bool _contentLoaded;
 
    public void InitializeComponent()
    {
      if (_contentLoaded) return;
      _contentLoaded = true;
      System.Uri resourceLocater = new System.Uri(
        "/SampleWpfApplication;component/mainwindow.xaml",
         System.UriKind.Relative);
 
      System.Windows.Application.LoadComponent(
        this, resourceLocater);
    }
 
    void IComponentConnector.Connect(int connectionId, object target)
    {
      this._contentLoaded = true;
    }
  }
}

Imports System.Windows
Imports System.Windows.Markup

Partial Public Class MainWindow
  Inherits Window
  Implements IComponentConnector
 
  Private _contentLoaded As Boolean
 
  Public Sub InitializeComponent() Implements IComponentConnector.InitializeComponent
    If _contentLoaded Then
      Return
    End If
    _contentLoaded = true
    Dim resourceLocater As System.Uri = New System.Uri( _
      "/WpfApplication1;component/mainwindow.xaml", _
      System.UriKind.Relative)
   
    System.Windows.Application.LoadComponent(
      Me, resourceLocater)
   
  End Sub
 
  Sub System_Windows_Markup_IComponentConnector_Connect(ByVal connectionId As Integer, ByVal target As Object) Implements IComponentConnector.Connect
    Me._contentLoaded = true
  End Sub
End Class

List 1: MainWindow.xamlファイルから自動生成される中間生成物のプログラム・コード(上:MainWindow.g.cs、下:MainWindow.g.vb)

 上記のコードを見ると分かるように、Visual Studioテンプレートからの生成時の状態では、XAMLコードのロード以外には特に何も行っていない。

 ここで、XAMLコードに記述を追加すると、自動生成されるプログラム・コードに以下のような変化が生じる。

  • XAMLコード中でUI要素にName属性を付けると、同名のフィールドが追加され、IComponentConnector.Connectメソッド中での初期化が行われる
  • XAMLコード中でイベント・ハンドラの登録を行うと、IComponentConnector.Connectメソッド中でイベント・ハンドラの追加が行われる
  • <x:Code>要素を追加すると、要素内の記述がそのまま自動生成されたクラス内部にコピーされる

 例えば、MainWindow.xamlファイルをList 2のように書き換えると、自動生成されるプログラム・コードにList 3に示すようなコードが追加される。

<Window x:Class="SampleWpfApplication.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
  <x:Code><![CDATA[
private void Button_Click(object sender, RoutedEventArgs e)
{
  MessageBox.Show("test");
}
]]>
  </x:Code>
  <StackPanel>
    <TextBlock Name="text1" Text="Sample" />
    <Button Name="button1" Click="Button_Click" Content="OK" />
  </StackPanel>
</Window>

List 2: Name属性やClick属性を含むようにMainWindow.xamlファイルを書き換え(XAML)(C#)
VBの場合は、上記の<x:Code>要素内に記述しているC#のイベント・ハンドラの部分を下記のようにVBのコードに書き直す。
Private Sub Button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
  MessageBox.Show("test")
End Sub

internal System.Windows.Controls.TextBlock text1;
internal System.Windows.Controls.Button button1;
 
void IComponentConnector.Connect(int connectionId, object target)
{
  switch (connectionId)
  {
    case 1:
      this.text1 = ((System.Windows.Controls.TextBlock)(target));
      return;
    case 2:
      this.button1 = ((System.Windows.Controls.Button)(target));
      this.button1.Click +=
        new System.Windows.RoutedEventHandler(this.Button_Click);
      return;
  }
  this._contentLoaded = true;
}

private void Button_Click(object sender, RoutedEventArgs e)
{
  MessageBox.Show("test");
}

Friend WithEvents text1 As System.Windows.Controls.TextBlock
Friend WithEvents button1 As System.Windows.Controls.Button

Sub System_Windows_Markup_IComponentConnector_Connect(ByVal connectionId As Integer, ByVal target As Object) Implements IComponentConnector.Connect
  If (connectionId = 1) Then
    Me.text1 = CType(target,System.Windows.Controls.TextBlock)
    Return
  End If
  If (connectionId = 2) Then
    Me.button1 = CType(target,System.Windows.Controls.Button)
    AddHandler Me.button1.Click, _
      New System.Windows.RoutedEventHandler( _
        AddressOf Me.Button_Click)
    Return
  End If
  Me._contentLoaded = true
End Sub

Private Sub Button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
  MessageBox.Show("test")
End Sub

List 3: List 2から自動生成される中間生成物のプログラム・コード(部分的に抜粋)(上:MainWindow.g.cs、下:MainWindow.g.vb)

 XAMLコードに記述した<x:Code>要素は、HTMLでいうところの<script>タグによるスクリプト・コードの埋め込みと同じ感覚で利用できるが、実際のところWPFアプリケーション開発ではあまり利用されない。WPFの場合、プログラム・コードは分離コードの中に記述するのが一般的である。

 また、XAMLコード中の属性構文で指定した値がプロパティの場合は、(InitializeComponentメソッド内で呼び出されている)LoadComponentメソッド内で自動的に読み出してもらえるが(すなわち、プログラム・コードの自動生成に頼らない、XAML自体の仕様)、その値がイベントの場合には、上記のようなプログラム・コードの追加が必要となる。

 続いて、App.xamlファイルから生成されるプログラム・コードについて説明する。

       1|2|3|4 次のページへ

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。