#概要
Qiitaで"C++ WPF"と検索しても、C++の記事はヒットせずひたすらC#が出てきます。
VS標準でC++によるWPF対応がなされていないことや、WindowsのGUIはC#で書くものというのが定説っぽくなりつつあるからでしょうか。
なので、少しあまのじゃく気取りながらWPFをC++で書くやり方をQiitaに残します。
基本的には参考URLを見たほうが早いかもしれませんが、ここで書くなりの独自性を出すつもりです。
なお前置きで言っておきますが、**すべてC++では書いていません。**いろいろな手間の問題があるからです。
使用環境:
OS: Windows 10
Visual Studio 2017
VSでは、標準的なWindows製作をC++とC#で可能な形になるようにインストールしておいてください。
##プロジェクトの組み方
まず、VSでプロジェクトを作ります。C++から「CLRコンソールアプリ」を選べばいいです。
もちろんこのままだと本当にコンソールにはろわするだけのプログラムになってしまうので、プロジェクトの設定を組みなおします。
ここはあまりいじらないでもいいと思います。
WPFでいろいろ組む場合、アンマネージ言語の仕組みと会話できるようにコードを配慮してやりましょう。
がっちがちのC++st様方には説明不要でしょうが、もし言語機能でより進んだものを使いたければここを設定します。
今回はそこまで進んだ記述はしない予定なのでデフォルトで。
リンカータブのシステム欄にあるサブシステムをCONSOLEからWINDOWSにします。
この設定が生きていれば、このままコンパイルしてもコンソールが出なくなります。
デフォルトのプロジェクトだと、System、System.Data、System.Xml、mscorlibの4つの参照が入っています。参照を右クリックで参照の追加を選び、アセンブリのフレームワークという項目の中からPresentetionCore、PresentationFramework、System.Xaml、WindowsBaseの4つを追加します。
これで準備が完了したので、実際にコードを組んでみます。
##サンプルコード
#include "stdafx.h"
using System::STAThreadAttribute;
using System::String;
using System::Windows::Application;
using System::Windows::Window;
[STAThread]
int main(array<String ^> ^args)
{
Application ^application = gcnew Application();
application->Run(gcnew Window());
return 0;
}
とにかく簡単なWindowsを開くだけのアプリです。まだはろわ表示はできません。
WPFではSTAThread属性が必要なので入れています。C++/CLIなら何のことはありませんね。
##Xaml使ってGUI
これからXaml使ってWPF作るのですが、実はC++だけでXamlしようとするといろいろな意味で敗北します。UWPならVSがxamlを制御してくれるのですが……
仕方ないので、Xaml側の制御をC#なりVBなりで作ってVSに任せるようにすると楽ができます。
今回はとりあえずC#のプロジェクトでXamlを制御してもらう方向でいきます。
ソリューションに新しいプロジェクトの追加を行い、C#からWindows クラシックタブ内にあるクラスライブラリを選択します。プロジェクト名は仮にMainViewとします。
次に見た目の土台にあたるWindowコントロールを定義します。プロジェクトに追加の項目を行い、WPFタブからユーザーコントロールを選びます。名前はとりあえずMainWindowとします。
そうするとUserControlクラスの派生でMainWindowが定義され、それに基づいてXamlファイルと、それにぶら下がるMainWindow.csと、その制御コードが深部に作られます。
ただ、UserControlクラスの派生のままではアプリの土台には不向きなので、Windowクラスの派生として作り替えてしまいます。
まずMainViewプロジェクトの参照にSystem.Xamlを追加します。Xamlを使うわけなので当然ですね。
つづいてxamlファイル側を以下のように書き換えてしまいます。
<Window x:Class="MainView.MainWindow"
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:local="clr-namespace:MainView"
mc:Ignorable="d"
Title="はろわ"
Height="300" Width="300">
<Grid>
<TextBlock>go, hellowork!</TextBlock>
</Grid>
</Window>
最初に作られていたコードはUserControlクラスを示す要素でしたが、これをWindowにすることで自動的にWindowを基底クラスとするコードに裏側で書きかえが行われます。
それ以外は適当にウィンドウのデザインとはろわを表示するように書き換えてあります。
あとはMainWindow.csのほうも同様に書きかえてしまいましょう。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MainView
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
元のコードだとMainWindowの既定クラスがUserControlになっているので、単にWindowに書きかえただけです。これでpartial定義がかみ合うようになるのですね。
ざっくり準備ができたので、いったんこちらのMainView側をビルドし、アセンブリ化しますが、その前にソリューションのプロパティから構成マネージャを開き、C#側のプロジェクト設定をx86で新規作成します。
これでOKを押して設定しておくことで、プロジェクト全体で使用するアーキテクチャを統一できます。
xaml側をビルドすると、MainView.dllとしてアセンブリが完成するので、これをc++側で参照します。
###やり方::C++側
cppwpfのプロジェクトにMainViewアセンブリの参照を追加します。
これでMainViewのxamlファイルをそのままリソースで使用することができます。
あとはこのxamlファイルを使用する方向でコードを作ってやるだけで完成です。
#include "stdafx.h"
#include "cppwpf.h"
using System::STAThreadAttribute;
using System::String;
using System::Windows::Application;
using System::Windows::Window;
[STAThread]
int main(array<String ^> ^args)
{
Application ^application = gcnew Application();
application->StartupUri = gcnew System::Uri("pack://application:,,,/MainView;component/MainWindow.xaml");
application->Run();
return 0;
}
一応この実装はStartUpUriプロパティにMainWindow.xamlファイルを投げ込んでいますが、Runの引数にMainView::MainWindowのインスタンスを投げ込んでも同じ結果を得られます。
まだ勉強不足でどういった差が出るかはわからないのですが、もっと入り組んだプログラムを作っていった場合にその差がはっきりわかってくるかもと思っています。
##終わりに
今回の実装は、実行ファイルをC++、ビュー部分をXaml with C#のDLLライブラリとして実装していますが、実行ファイル側でXaml内にバインディングすることは仕組み的に不可能です。
なので、いわゆるWPFのMVVMという形で言うならば、別のアセンブリでModel及びViewModelを作ってやる必要があります。
機会があればまたそちらの方も書ければと思います。
今回のサンプルコードをgithubに上げておきましたので良ければ使ってみてください。
https://github.com/exli3141/cppwpf
参考URL:
http://spark-debris.blogspot.jp/2015/06/cwpfcwpf.html
http://spark-debris.blogspot.jp/2015/06/cc.html
https://msdn.microsoft.com/ja-jp/library/aa970069(v=vs.110).aspx