ソフトウェア

C# WPFアプリ XAMLの基本概念を理解する

はじめに

今回はC# WPFアプリケーションのXAMLの基本概念について理解しましょう。
XAMLはViewの中身の言語のことです。
ベースはXML形式です。
最初、見たときはとっつきにくい印象を受けましたが、XAMLの基本概念がつかめた後は驚くほど、XAMLへの理解が進みました。

Viewファイルの中身のXAMLの基本概念


Viewファイルの中身は上の図のようなXML形式のXAMLという言語で書かれています。
その他のXML形式ファイルとの違いは、XAMLはC#の言語で置き換えることができる点です。
上の図でいえば、
XAML形式だと

<Button FontSize = "25">
    <Button.Content>
      ここをクリック
    </Button.Content>
</Button>

これをC#の言語で置き換えたとすると

new  Button
{
    FontSize = 25,
    Content ="ここをクリック" 
};

と書くことができます。

つまり、XAMLはC#と.Netフレームワークのクラスライブラリで表現できます

こう見ると、今まで意味不明(笑)な言語に見えていたXAMLのコーディングの方法が少し見えてみたのではないでしょうか。
もう少し、正確にみると、XAMLは次の形で書いていくことができます。

XAMLの書き方
【ケース1】
<オブジェクトクラス名 同クラスのプロパティ名=値>・・・</オブジェクトクラス名>

【ケース2】
<オブジェクトクラス名>
 <オブジェクトクラス名.プロパティ名=値>
 </オブジェクトクラス名.プロパティ名=値>
</オブジェクトクラス名>

一例をご紹介します。
わかりやすいようにXAMLとあわせてC#で書いた場合のリストもご紹介します。

<Window x:Class="XamlWindowsApplication1.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="XAML テストプログラム" Height="100" Width="140"
  >

  <x:Code>
    <![CDATA[
    private void ButtonClicked(object sender, RoutedEventArgs e)
    {
      MessageBox.Show("ボタンが押されました");
      e.Handled = true;
    }
    ]]>
  </x:Code>
  <Button Click="ButtonClicked">ここを押して</Button>
</Window>

ここでButtonClicked関数はボタンがクリックされたときのイベント関数です。
コールバック関数ともいいますね。
この関数の定義をC#側のファイルに持っていくことができます。
こちらのほうが一般的ですけどね。

以下が、その例です。

namespace XamlWindowsApplication1
{
  public partial class Window1 : System.Windows.Window
  {
    public Window1()
    {
      InitializeComponent();
    }

    private void ButtonClicked(
      object sender, System.Windows.RoutedEventArgs e)
    {
      System.Windows.MessageBox.Show("ボタンが押されました");
      e.Handled = true;
    }
  }
}

このようなXAMLと連携させることができるC#のファイルはコードビハインドと呼ばれ、ファイル名は****.xaml.csで保存します。
そして、実はxamlもコードビハインド(xaml.cs)もファイルは別々ですが、どちらも同一のViewクラスの一部です。
XAMLとC#ファイルのクラス名を見てください。
どちらも同じ名前のWindowクラスになっています。

<Window x:Class="XamlWindowsApplication1.Window1"
  ...
  >
  ...
</Window>

namespace XamlWindowsApplication1
{
  public partial class Window1 : System.Windows.Window
  {
    ...
  }
}

xamlファイルとxaml.csファイルはファイル形式(拡張子や書き方)が違うものの、同一のViewクラスが分割された形になっているんです。

コードビハインドについてもう少し、具体例をご紹介します。

コードビハインドの具体例

xamlもコードビハインド(xaml.cs)もファイルは別々ですが、どちらも同一のViewクラスの一部でした。

では、xamlとコードビハインドの使い分けの方法について、リストのサンプルを見ながら、感覚を身につけていきましょう。

1つ目です。
xaml側

<DataGrid Name="listDataGrid">
…
</DataGrid>

コードビハインド、xaml.cs側

var grid  = this.FindName("listDataGrid") as DataGrid;

xaml側のオブジェクトにコードビハインドからアクセスする場合はxaml側でコントロールのNameまたはx:Nameプロパティでアクセス用の名前を宣言します。
コードビハンドからのFindNameメソッド(this.FindName("Nameプロパティの値")など)や直に"Nameプロパティ"を指定してxaml側のプロパティにアクセスできます。
アクセスできた後は、もちろん、通常のインスタンスと同じなのでxaml側のオブジェクトがもつメソッドも使えます。

2つ目です。
xaml側

<Button Name="b1" Click="MakeButton">ボタン1です。ここをクリック</Button>
<Button Name="b2"></Button>

コードビハインド、xaml.cs側

void MakeButton(object sender, RoutedEventArgs e)	
{
    Button b2 = new Button();
    b2.Click += new RoutedEventHandler(Onb2Click);
    root.Children.Insert(root.Children.Count, b2);
    DockPanel.SetDock(b2, Dock.Top);
    text1.Text = "ボタン1がクリックされた!!";
    b1.IsEnabled = false;
}

void Onb2Click(object sender, RoutedEventArgs e)
{
    text1.Text = "ボタン2がクリックされた!!";	
}

xaml側のボタンにクリックしたときのイベント関数をコードビハインド側で定義している例です。
C#というデリゲートというしくみとDockPanelという自動配置するための.Netフレームワークのクラスを間に挟んでいるので、少し、応用ぽっくみえます。
上のコードの説明をしますと、
xaml側で定義したボタン1をクリックしたときにコードビハインド側でボタン1のイベント関数MakeButtonが呼ばれ、その中でボタン2に対するクリックイベント関数Onb2Clickを登録しています。
ボタン1のクリック関数の宣言はxaml側で行い、ボタン2のクリック関数の宣言はコードビハインド側で行うといった少し、手のこったことをしています笑
ボタン1は一度クリックされると無効、IsEnabledがfalseにしています。

3つ目です。
xaml側

<DataGrid  … Sorting="DataGrid_Sorting" >

コードビハインド、xaml.cs側

private void DataGrid_Sorting( object sender, DataGridSortingEventArgs e )	
{
    /// DataGridでソートをさせないようにソート完了状態に
    e.Handled = true;

    /// ソート方法の決定
    /// 現在が昇順 → 今回は降順にソート
    /// 現在が降順 → 今回は昇順にソート
    var preDir = e.Column.SortDirection;
    var newDir = (preDir == ListSortDirection.Ascending) ? ListSortDirection.Descending : ListSortDirection.Ascending;

    //ソートの実行
    e.Column.SortDirection = newDir;
}

リストのタブをクリックしたとき、自動でソートさせる仕組みの例です。
上のコードの説明をしますと、
xaml側のSortingはDataGridクラスのイベント名で、ソートするときに呼ばれるイベントです。
そこに値を設定すると、その値の名前でコールバック関数を宣言することになります。
ここではDataGrid_Sortingという名前ですね。
関数は宣言しただけでは使えませんので、コードビハインド側でDataGrid_Sorting関数の定義を行っています。
この関数が呼ばれたとき、現在の設定、昇順か降順をみて、その反対の設定で改めてソートしなおす処理を行っています。

次に上のxaml側のサンプルリストでDockPanelという画面を自動整列するための.Netクラスライブラリが出てきましたけど、DockPanel以外にもよく使われるものがあります。
これについて少しご紹介します。

画面レイアウトで使用する.Netクラスライブラリ

私の設計経験をもとに画面レイアウトでよく使用する.Netクラスライブラリをまとめてました。

基本レイアウトを決めるコントロール
コントロール名 説明 補足
Grid テーブルレイアウトを行うために使用する。
行の高さ、列の幅を指定して、固定で配置。
RowDefinitionタグとColumnDefinitionタグでグリッドの行及び列を定義する。
【備考】
→アイテムサイズにあわせてサイズ変更(可変サイズ)
→残った領域が固定サイズになる
※ColumnDefinitionも同じ
DataGrid 表形式のデータを表示するために使用する。 データグリッドで表示する内容はクラスで定義することができる。
表示用のクラスメンバで非表示用と表示用を設定することができる。
表示する時は、DataGridの実装過程でクラスのメンバ変数名を指定する。
非表示にした時はメンバ変数名を指定しない。
StackPanel 上から下または左から右へ自動でアイテムを整列させることができる。
※Gridとの組み合わせ相性がよい
-
DockPanel DockPanel.Dockで指定した辺に張り付ける形で表示 ●Gridの範囲で座標指定可能
●DockPanel.Dock自体は並び順で描画される。
例えば、下記では表示内容が異なる。
<lButton DockPanel.Doc="Top" Content="Top">
<lButton DockPanel.Doc="Bottom" Content="Bottom">

<lButton DockPanel.Doc="Bottom" Content="Bottom">
<lButton DockPanel.Doc="Top" Content="Top">
WrapPanel Windowsのサイズにあわせて横方向に自動的に折り返す。
※画像サイズにあわせて自動的に内部のコントロールサイズを変更するのに向いている。
-
ListView 表形式のデータを表示するために使用する。 -
ListBox リストから値を選択する場合に使用する。 -
Canvas 指定した座標位置に表示する。 -
ScrollViewer スクロールを表示する。 -
ComboBox リストから値を選択する場合に使用する。 -

画面レイアウトを実現するのに必要な.Netクラスライブラリは上のとおりです。
最後にDockPanelとGridとDataGridの利用例をご紹介します。

XAMLで書かれたView側

<UserControl ... >
    <DockPanel>
        ...
        <Grid>
            <DataGrid Name="listDataGrid"
                ...
                ItemsSource="{Binding ListData, Mode=OneWay}"
                ... >
            ...
            </DataGrid>
        </Grid>
        ...
    </DockPanel>
</UserControl>

ここのポイントはItemsSourceです。
ItemsSourceはDataGridクラスのプロパティです。

C#で書かれたViewModel側

private ObservableCollection<ListItemData> _ListData = new ObservableCollection<ListItemData>();

public ObservableCollection<ListItemData> ListData {
    get { return _ListData; }
    set {
        if (_ListData == value)
            return;
            _ListData = value;
            RaisePropertyChanged("ListData");
    }
}

ここのポイントはObservableCollectionです。
ObservableCollectionクラスはView側のDataGridクラスとデータを連携するために利用するクラスです。
ListItemDataはです。
このメンバ変数には表示しないデータの変数を含むこともできます。
表には表示しないけど、裏側でデータをもっておきたい場合に使えます。

最後に

今回はViewを形成するXAMLの基本概念についてご紹介しました。
XAMLはC#と表現方法は全くことなりますが、中身はC#で行ってきたことと同じ技術、.Netフレームワーク上で書かれていることをイメージしていただければ、十分です。
後はxamlもコードビハインド(xaml.cs)もファイルは別々ですが、どちらも同一のViewクラスの一部である点をおさえておきましょう。
xamlの基本は以上ですが、上記の考えを抑えて、不明な点はGoogle先生や.Netフレームワークの公式ドキュメントを頼れば、迷うことはないと思います。

-ソフトウェア