Livetを使ったC#のWPFプログラミング

システムとして機能が網羅されたサンプルをもとに、Livetを使ったC#のWPFシステムの開発技能を身に付ける為の学習サイト


2.WPF


windowsフォームとWPFの違い

「Windowsフォームアプリケーション」は、C#プログラミング技法でプログラミングを行ないますが、「WPF」は、C#プログラミング技法と、XAMLでのプログラミング技法を組み合わせてプログラミングを行ないます。

WPFは「Windows Presentation Foundation」の略名で、 WPFに組み込まれているのは、XAML(ザムル)と呼ばれる拡張版XML言語です。WPFでは、画面はXAMLを使用して、処理はC#を使用してプログラミングを行なえます。Windowsフォームアプリケーションと比べると表現力が格段に向上しました。

描画では、Windowsフォームアプリケーションは、ソフトウエアレンダリングによるラスタ形式であるのに対しWPFはハードウェアレンダリングによるベクタ形式でGPU装着のPCでは、GPUで処理されるので高速で且つ綺麗です。よって、画面の見栄えを重視するのであればWPFで作ることをお勧めします。WPFはWindowsフォームアプリケーションと比べるといろいろとやることが増えているのでどうしても最初は戸惑いますが色々なWPFコーディングを見ていけば、わかってきます。安心してください。

コントロール・オブジェクト・バインド

WPFを理解するには、コントロール・オブジェクト・バインドを理解する必要があります。
先ず、コントロールとは、マウスやキーボードなどのユーザー操作を受け取ったり、処理の途中経過や結果を適宜表示したりするのもので Button、CheckBox、ComboBox、DataGrid、TextBox、Label、GroupBox、ListBox、Menu、UserControl等々があります。
次に、オブジェクトですが、クラスという設計書で作られた実態がオブジェクトです。 つまり、設計図(クラス)と建築物(オブジェクト)の関係と同じです。 ほとんどのオブジェクトは、その性質を属性(プロパティ)と操作に分けて表現することができます。
バインディング(binding:結合)とは「結び付ける」という意味ですが、具体的には、 WPFでは、「コントロールのプロパティをBind」します。 バインディングとは、コントロールにオブジェクトを Binding している場合、オブジェクトのプロパティの値が変化するたびに、ほかの個所にこの更新されたことを通知し、その変化が即座に伝搬されるということです。


イベントトリガーとプロパティ

これもWindowsフォームアプリケーションとWPFの違いなのですが、Windowsフォームアプリケーションでは、イベントトリガーとプロパティは既に備わっていて、その器の内に処理のコードを記述すればOKでした。

ところがWPFでは、その器から記述する必要があります。器はViewに書き、処理はViewModelに書きます。詳しくは、「画面遷移」で説明します。


まずはWPFを作ってみる

LivetをインストールしたVisual Studio 2010 Expressを起動するとスタート画面が表示されます。


左上の「新しいプロジェクト」をクリックします。


「Livet WPF4 MVVM アプリケーション」をクリックしてプロジェクトの名前を入力します。



次にOKをクリックしますと次の画面になります。


右のソリューションエックスプローラのフォルダを開いてみます。



ViewsフォルダのMainWindow.xamlをクリックします。


何やらエラーが3つ出ています。


メニューのビルドの「ソリューションのビルド」をクリックします。


エラーは消えましたが、まだ画面が表示されていません。
ここでは左の上に画面が表示され、下にコードが表示されるのですが、上に画像が表示されずに、エラーメッセージが表示されています。



「デザイナーの再読み込み」をクリックしますと画面が表示されます。



Livetがインストールされていると「InfrastructureAssembliesフォルダが作成されており、その中に

  ①Livet.dll

  ②Microsoft.Expression.Interactions.dll

  ③System.Windows.Interactivity.dll

LivetとMicrosoftのライブラリが格納されています。また、参照設定の中を見ますと、この3つのライブラリへの参照が設定されています。

その他、Modelsフォルダ、ViewModelsフォルダ、Viewsフォルダが作成されています。


ViewModel

自動で作成されたViewModelフォルダの内のMainWindowViewModelを見ますと

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

using Livet;
using Livet.Commands;
using Livet.Messaging;
using Livet.Messaging.IO;
using Livet.EventListeners;
using Livet.Messaging.Windows;

using LivetWPFApplication1.Models;

namespace LivetWPFApplication1.ViewModels
{
    public class MainWindowViewModel : ViewModel
    {
        public void Initialize()
        {
        }
    }

Livetライブラリの名前空間がusingディレクティブで追加されています。

また、LivetWPFApplication1は、LivetのViewModelを継承すると書かれています。

メソッドとしては、最初に実行されるInitializeが書かれています。


ソリューションエックスプローラのフォルダの一番下にApp.xamlが作成されています。4行目にStartupUriとして最初に表示するウィンドウが書かれています。

App.xamlにもコードビハインドファイル(App.xaml.cs)が作成されていますが、通常ではこれを触ることはありません。

App
 <Application x:Class="LivetWPFApplication100.App"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          StartupUri="Views\MainWindow.xaml"
          Startup="Application_Startup">
    <Application.Resources>
         
    </Application.Resources>
</Application>

コードビハインド(ファイル)

MainWindow.zamlファイルの下に「~zaml.cs」というファイルが自動でできています。これは、Windowsフォームアプリケーションの時に画面をWクリックして表示されるコードが記述されたファイルと同様な機能のファイルで処理が書かれています。

WPFならxaml.cs、Windowsフォームならフォーム.cs、ASP.NETならaspx.csとなります。これらをコードビハインド(ファイル)と呼んでいます。


例えばXAMLの画面で間違ってButtonをWクリックしますと、Clickの記述が追加されます。

又、コードビハインドファイルにはClickイベントが作成されてしまいます。

このようにコードが書かれてしまったら、XAMLのClick="button1_Clickとコードビハインドのイベントプロシージャを削除して下さい。

xamlファイル
 <Button Content="Button" Height="23" HorizontalAlignment="Left" 
	Margin="395,441,0,0" Name="button1"
        VerticalAlignment="Top" Width="75" Click="button1_Click" />

コードビハインドファイル
private void Click="button1_Click(object sender, RoutedEventArgs e)
{

}

5章 MVVMで解説するように、このサイトでは、LivetというMVVMを使用してWPFのシステムを開発しますので、このコードビハインドファイルには、 ViewsのZAMLとViewsModelのファイルの紐付けであるDataContextの設定以外では使用しません。

コードビハインドファイル
public partial class MainWindow : Window
{
   public MainWindow()
   {
       InitializeComponent();
       this.DataContext = new MainViewModel();
   }
}

DataContextをコードビハインドファイルに書きたくない時は、ZAMLに書きます。

View
<Window x:Class="LivetWPFApplication100.Views.Window8"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
        xmlns:l="http://schemas.livet-mvvm.net/2011/wpf" 
        xmlns:v="clr-namespace:LivetWPFApplication100.Views"
        xmlns:vm="clr-namespace:LivetWPFApplication100.ViewModels"        
        xmlns:b="clr-namespace:LivetWPFApplication100.Behaviors"        
        Title="Window8" Height="714" Width="1035" 
        WindowStartupLocation="CenterScreen">

    <Window.DataContext>
        <vm:ViewModel8 />
    </Window.DataContext>

    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Resources/Styles.xaml"/>
                <ResourceDictionary Source="/Resources/Styles2.xaml"/>
                <ResourceDictionary Source="/Resources/StylesBG.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

    <i:Interaction.Triggers>
        <!--WindowのContentRenderedイベントのタイミングでViewModelのInitialize
                                                         メソッドが呼ばれます-->
        <i:EventTrigger EventName="ContentRendered">
            <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName=
                                                                 "Initialize"/>
        </i:EventTrigger>

        <l:InteractionMessageTrigger 
            MessageKey="MessageKey2" Messenger="{Binding Messenger}">
            <l:TransitionInteractionMessageAction
                WindowType="{x:Type v:Window2}" Mode="Modal"/>
        </l:InteractionMessageTrigger>
    </i:Interaction.Triggers>

    <i:Interaction.Behaviors>
        <b:CloseButtonBehavior IsWindowCloseEnable="{Binding ElementName=
                                                 CloseCheck, Path=IsChecked}"/>
    </i:Interaction.Behaviors>

    <Grid  Background="#FFE6E6FA">

        <TextBox Text="{Binding Path=txtCopy1, Mode=TwoWay}" Height="23" 
                                                 HorizontalAlignment="Left" 
                 Margin="489,159,0,0" Name="textBox2" VerticalAlignment="Top" 
                                                 Width="120" />
        <TextBox Text="{Binding Path=txtCopy2, Mode=TwoWay}" Height="23" 
                                                 HorizontalAlignment="Left" 
                 Margin="489,214,0,0" Name="textBox3" VerticalAlignment="Top" 
                                                Width="120" />

        <Button Command="{Binding Path=TestCommand1}" Content="Test"  Height="35" 
                Margin="489,276,400,364" 
                Foreground="White" Style="{StaticResource ButtonStyle1}" />

        <v:UserControlFooterN1  Margin="0,0,0,0" HorizontalAlignment="Left" 
                                                 VerticalAlignment="Bottom"  />
    </Grid>

</Window>

One Point Lesson 【プロパティの自動実装】

プログラミングでメンバー変数にするか、プロパティにするかと困った時ですが、どちらにしても機能的には余り大差がないのですが、 簡単に言いますとプロパティは、フィールドとメソッドの両方の側面を兼ね備えています。 よって、値を設定する時に、もしくは、値を取得する時に、何かの条件を付けたい場合は、プロパティで定義したほうが良いです。
Visual Studio 2008(厳密には.NET Framework 3.5)のC#(=Visual C# 2008)では、
「プロパティの自動実装」(Automatic Implementation of Property)という機能が搭載され、
フィールド変数の記述を省いて効率的にプロパティを実装できるようになっている。 自動実装するプロパティでは、getアクセサ(getter)とsetアクセサ(setter)の両方が追加されるが、 この片方を消すようなことはできません。
■2005以前
private int _myProperty;
public int MyProperty
{
get { return _myVProperty; }
set { _myVProperty = value; }
}
■2008以降
public int MyProperty { get; set; }


★自動実装方式でうまく動かない時があります。その時には、従来の方式で記述して下さい。