プログラミング Windows 第6版 第2章 WPF編

この記事では、「プログラミング Windows 第6版」を使って WPF XAML の学習を支援することを目的にしています。この目的から、書籍と併せて読まれることをお勧めします。

最初に、WPF アプリケーションを開発する上で Visual Studio 2013 で設定しておいた方が良いトピックを紹介します。 Visual Studio 2013 を起動して、[ツール]-[外部ツール] を使って Blend for Visual Studio からプロジェクトを開けるように設定します。設定方法は、以下のようにします。

  • コマンド:C:\Program Files (x86)\Microsoft Visual Studio 12.0\Blend\Blend.exe
  • 引数:$(SolutionDir)$(SolutionFileName)
  • 初期ディレクトリー:$(SolutionDir)

Ch02 VS External Tools
このように設定しておくことで、Visual Studio で開いているプロジェクトをいつでも Blend で開けるようになります。Windows ストア アプリ プロジェクトの場合は、ソリューション エクスプローラーのコンテキスト メニューから Blend で開けるようになっていますが、 WPF アプリケーション プロジェクトでは Blend で開くという コンテキスト メニューが表示されないための対策です。

第2章 XAML 構文

2.1(P33) グラデーションブラシ:コード

この節で説明している GradientBrushCode サンプルは、第1章で説明したようにPage を Windowへ、組み込みのスタイルを個別指定へと読み替えることで WPF へと置き換えることができます。次に実行結果を示します。
GradientBrushCode
WinRT XAML との大きな違いは、名前空間を読み替えることです。WinRT XAML は Windows.UI.Xaml で始まる名前空間で定義をしており、 WPF は System.Windows で始まる名前空間で定義しています。したがって、SolidColorBrush などのブラシは、System.Windows.Media 名前空間で定義されており、WebView クラスは WPF では WebBrowser クラスとなり System.Windows.Controls 名前空間で定義されているという形式になります。 WebViewBrush 相当のクラスは、WPF では定義されていませんので、ご注意ください。

2.2(P36) プロパティ要素構文

この節では、XAML におけるプロパティの記述方法を説明しています。基本的な記述方法は、「<TextBlock Background="Red" />」のように XML の属性を使用します。属性に使用するのは、単一の値と覚えておけば良いでしょう。例として記述した Background プロパティを明示的に SolidColorBrush オブジェクトとして定義するには、次のように記述します。

 <TextBlock>
  <TextBlock.Backgound>
    <SolidColorBrush Color="Red" />
  </TextBlock.Backgound>
</TextBlock>

 

「<要素名.プロパティ名>」タグを使うことで、新しいタグを使ってプロパティを設定できるようになります。XML の属性を使った記述方法でも、リソースやバインディング構文を使うことで他のオブジェクトなどを設定することもできますが、これらの記述方法は本書を読み進めていくことで理解できるようになります。

2.3(P40) コンテント プロパティ

この節では、最初に Content プロパティの説明が行われています。Windows Forms と考え方が大きく違う点でもあるので、具体的に説明します。WPF のコンテンツ モデルは、MSDN にドキュメントがありますので参照してください。

コントロール名 プロパティ名 説明
ContentControl Content 1つの任意のオブジェクトを設定するすることができます。 たとえば、Window クラスは Contentを持ちますし、Button コントロールもContent を持ちます。よって、Button.Content="ボタン"とすれば文字列オブジェクトを設定したので、ボタンに文字列が表示されるようになります。つまり、Windows Forms のButton.Text プロパティや Image プロパティに相当することになります。
ItemsControl Children オブジェクトのコレクションを設定することができます。 たとえば、Grid クラス、Canvasクラス、StackPanel クラスなどは Children を持ちます。このプロパティが、Windows Forms の Form.Controls プロパティに相当します。

これらのことを踏まえて書籍を読めば良いでしょう。この節で使用している GradientBrushMarkup サンプルも、第1章で説明したようにPage を Windowへ、組み込みのスタイルを個別指定へと読み替えることで WPF へと置き換えることができます。

2.4(P44) TextBlock のコンテント プロパティ

この節では、TextBlock クラスの Content プロパティを詳細に説明しています。Windows Forms では Label クラスに相当すると説明しましたが、この説明はとても単純化したものになります。より正確に表現するのであれば、Windows Forms のRitchTextBox クラスを読み取り専用にしたようなクラスと考えるのが妥当になります。なぜなら、Inline オブジェクトのコレクションを持っており、設定するテキストに対する装飾も行うことができるからです。もちろん、WPF にも RitchTextBox クラスは存在します。

この節で使用している TextFormatting サンプルも、第1章で説明したようにPage を Windowへ、組み込みのスタイルを個別指定へと読み替えることで WPF へと置き換えることができます。次に実行例を示します。
TextFormatting

これが、TextBlock クラスで表現できています。Windows Forms の Label クラスで同じことをしようとすれば、手間がかかることを容易に想像できることでしょう。このことは、XAML という UI 技術の豊かな表現力の一端を示しています。

2.5(P47) ブラシの共有

この節では、XAML におけるリソースの考え方を説明しています。XAML のリソース システムは、柔軟であり、XML のツリー構造を利用したスコープの概念も備えています。
Ch02 XAML Resource
図に示したオブジェクトごとにリソースを定義することができます。リソースを定義したオブジェクトによって、 そのオブジェクトより下の階層(Content やChildren)がリソースを参照することができるようになります(スコープ)。つまり、アプリケーション全体で使用するリソースであれば、App.xaml で定義すれば良いということを意味します。

それでは、SharedBrush プロジェクトのMainWindow.xaml の抜粋を示します。

 <Window ...
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:clr="clr-namespace:System;assembly=mscorlib"
        ...
        >
    <Window.Resources>
        <clr:String x:Key="appName">Shared Brush App</clr:String>

        <LinearGradientBrush x:Key="rainbowBrush">
            <GradientStop Offset="0" Color="Red" />
            <GradientStop Offset="0.17" Color="Orange" />
            <GradientStop Offset="0.33" Color="Yellow" />
            <GradientStop Offset="0.5" Color="Green" />
            <GradientStop Offset="0.67" Color="Blue" />
            <GradientStop Offset="0.83" Color="Indigo" />
            <GradientStop Offset="1" Color="Violet" />
        </LinearGradientBrush>

        <FontFamily x:Key="fontFamily">Times New Roman</FontFamily>

        <clr:Double x:Key="fontSize">96</clr:Double>
    </Window.Resources>

    ...

</Window>

 

すでに説明した Page を Windows クラスへ変更しただけでなく、この変更に伴って WinRT XAML の 「<Page.Resources>」というリソース定義を「<Window.Resources>」に変更しています。また、名前空間に「xmlns:clr="clr-namespace:System;assembly=mscorlib"」を追加している点も注目してください。この名前空間の追加に伴って、WinRT XAML の「<x:String x:Key="appName">」定義を「<clr:String x:Key="appName">」へと変更しています。

WinRT XAML における「x:String」という型が何なのかというところから説明します。この記法は、「x:型」という記法で共通の XAML 言語プリミティブの組み込み型で説明されています。このドキュメントで、x:String を調べると XAML 2009の言語プリミティブで定義されていると記述されていますが、同じドキュメント上で WPF のサポートにおいては「マークアップ コンパイルされない XAML のみのサポート」とあります。つまり、コンパイルする WPF アプリケーションでは未サポートということになります。これが、名前空間として「xmlns:clr」を追加した理由になります。一方で、WinRT XAML ではx:Stringの記法が許可されていることから、XAML 2009 言語プリミティブをサポートしているように見えますが、実は WinRT XAML だけの限定的なサポートであることが、XAML 名前空間(x:)の言語機能というドキュメントに記載されています。要は、WPF XAML をリリースしてから、Silverlight や Windows Phone などの経験からプログラミングとして必要と思われる言語機能だけを XAML 2009 より WinRT XAML で実装したということになります。これなどは、WPF XAML と互換性を持たない機能になりますので、「x:Type」という記述を見つけたら読み替えないといけないということになるのです。

ここまで説明したこと以外は、SharedBrush プロジェクトのMainWindow.xaml は同じですから次に実行結果を示します。
SharedBrush

この節で補足するとすれば、スタイル リソースとテーマ リソースということになります。WinRT XAML では、generic.xaml(Windows 8 プロジェクトでは StandardStyles.xaml) というスタイル シートが Visual Studio 2013 の Windows 8.1 用の Windows ストア アプリ プロジェクトでは、ビルド時に自動的に組み込まれます(Windows 8 プロジェクトでは、プロジェクトに含まれます)。また、利用できる テーマ リソースとして、themeresources.xaml も自動的に組み込まれることになります。これが、RequestedTheme プロパティに指定できるテーマ リソースを定義しています(デフォルトでは、Dark テーマ)。一方で、WPF の場合は標準のスタイル シートの提供はなく、テーマ リソースが提供されているだけになります。含まれているテーマは、以下のアセンブリになります。

  • PresentationFramework.Luna.dll
  • PresentationFramework.Aero.dll
  • presentationframework.aero2.dll
  • presentationframework.aerolite.dll
  • PresentationFramework.Classic.dll
  • PresentationFramework.Royale.dll

これらのアセンブリに対する名前空間を定義することで、テーマ リソースを WPF で利用できるようになります。または、オープン ソースとして公開されているスタイル シートやテーマもありますので、自分が使いたいと思えるスタイル シートやテーマを探すか、作成してもよいでしょう。

2.6(P51) リソースは共有される

この節では、SharedBrush サンプルに対してコードで操作することで、リソースが共有されることを確認しています。この動きは、WPF でも同じなので自分で確認してみてください。

2.7(P52) ベクター グラフィックスの操作

この節で説明している Spiral プロジェクトの MainWindpow.xaml の抜粋を示します。

 <Grid>
    <Polyline Name="polyline"
              Stroke="Black"
              StrokeThickness="3"
              HorizontalAlignment="Center"
              VerticalAlignment="Center" />

</Grid>

 

ここまで説明してきた通りで、Stroke プロパティに指定していたリソースを「Black」に変更するだけで WPF で動作します。実行結果を示します。
Spiral
この描画されている内容が、ベクター グラフィックスになっている点が、Windows Forms と異なる点になります。Windows Forms で描画した図形は、GDI ベースになっていますからピクセルをベースにしたビットマップ画像であると言っても良いでしょう。ビットマップ画像に対して、WPF の描画ではベクター グラフィックスになります。ベクター グラフィックスのメリットは、拡大や縮小しても画像が滑らかになるという点です。UX の観点から、読み難いとユーザーが感じればピンチ操作などで拡大しようと試みることでしょう。拡大した時に、ドットが表示されてカクカクになったコーナーを目にすることは、ユーザー エクスペリエンスを損ないます。つまり、ベクター グラフィックスの採用そのもの ユーザー エクスペリエンスを向上させる目的を達成するためなのです。

次の StretchedSpiral プロジェクトも Spiral プロジェクトと同じ対応を行うことで、WPF で動作します。実行結果を示します。
StretchedSpiral
Spiral サンプルに Stretch プロパティを設定したのが、StreachedSpiral サンプルですが、このサンプルによってベクター グラフィックスのメリットを確認することができることでしょう。

この節で説明しているサンプルとして、ImageBrushedSpiral、HelloVectorGraphics、HelloVectorGraphicsPath プロジェクトはここまでに説明してきた内容を踏まえて書き換えれば(Page を Windowへ、スタイルを固定値へ)、問題なく WPF で動作します。以下に実行結果を示します。
ImageBrushedSpiralHelloVectorGraphicsHelloVectorGraphicsPath

次に説明している Path マークアップ構文を使った PathMarkupSyntaxCode サンプルでは、ここまでに説明してきた内容に加えてコードを少しだけ書き換える必要がありますので、MainWindow.xaml.cs の抜粋を示します。

 Geometry PathMarkupToGeometry(string pathMarkup)
{
    string xaml =
        "<Path " +
        "xmlns='https://schemas.microsoft.com/winfx/2006/xaml/presentation'>" +
        "<Path.Data>" + pathMarkup + "</Path.Data></Path>";

    Path path = XamlReader.Parse(xaml) as Path;  // Load を Parseへ

    // Detach the PathGeometry from the Path
    Geometry geometry = path.Data;
    path.Data = null;
    return geometry;
}

 

書き換えたのは、XamlReaderクラスの Load メソッドを Parse メソッドにしたことです。これは、WinRT XAML における XamlReader クラスと WPF の XamlReader クラスの違いによるものです。正確に表現するのであれば、WPF の XamlReader クラスのほうが高機能であり、WinRT XAML の XamlReader は機能を限定していることが原因になります。もちろん、書き換えても実行結果は同じになります。
PathMarkupSyntaxCode

2.8(P62) ViewBox による拡大と縮小

この節で説明している TextStretch、VectorGraphicsStretch サンプルも、ここまでの説明に従って書き換えることで、問題なく WPF で動作します。節のタイトルにある ViewBox コントロールとは、配置したコントロールを自動的に枠内に拡大・縮小することで枠からはみ出さないようにするものになります。
TextStretchVectorGraphicsStretch

2.9(P65) スタイル

この節では、XAML のスタイル定義に関して説明しています。2.5 ブラシの共有で説明した内容に従って、リソース定義などを書き換えるだけで WPF で動作させることができます。書き換えた SharedBrushWithStyle プロジェクトの MainWindow.xaml の抜粋を示します。

 <Window.Resources>
    <clr:String x:Key="appName">Shared Brush with Style</clr:String>

    <LinearGradientBrush x:Key="rainbowBrush">
        <GradientStop Offset="0" Color="Red" />
        <GradientStop Offset="0.17" Color="Orange" />
        <GradientStop Offset="0.33" Color="Yellow" />
        <GradientStop Offset="0.5" Color="Green" />
        <GradientStop Offset="0.67" Color="Blue" />
        <GradientStop Offset="0.83" Color="Indigo" />
        <GradientStop Offset="1" Color="Violet" />
    </LinearGradientBrush>

    <Style x:Key="rainbowStyle" TargetType="TextBlock">
        <Setter Property="FontFamily" Value="Times New Roman" />
        <Setter Property="FontSize" Value="96" />
        <Setter Property="Foreground" Value="{StaticResource rainbowBrush}" />
    </Style>
</Window.Resources>

 

2.5 ブラシの共有と同じように、名前空間で clr を定義することで x:Type という記述を書き換えています。そして、ImplicitStyle サンプルも同様に書き換えることで、WPF で動作させることができます。
SharedBrushWithStyleImplicitStyle

2.10(P71) データ バインディング

この節では、データ バインディングの基本的な記述方法を説明しています。データ バインディングの基本的な記述方法は、WinRT XAML も WPF も同じになります。従って、ここまでに説明したきたように書き換えることで WPF で問題なく動作させることができます(Page を Window へ、リソースの定義方法、x:Type 記述)。特に注意して頂きたいのは、2.5 ブラシの共有で説明した XAML 名前空間(x:) の言語機能になります。これは、WinRT XAML 固有の機能になりますので、WPF では .NET Framework のデータ型を利用しなければならないという点になります。
しかしながら、「Background="{x:Null}"」という null を指定する場合は、WPF も WinRT XAML と同じ指定をすることになります。
SharedBrushWithBinding

ここまで説明してた違いを意識しながら、第2章を読むことで WPF にも書籍の内容を応用することができるようになることでしょう。

ch02.zip