フォームに組み込む
もうひといき、ICalculatorの実装Calculatorができたので、これをCalculatorFormに組み込みましょう。
アトリビュート[Import(ICalculator::typeid)]をつけたICalculator^ calculator_をメンバに追加します。
#pragma once
#include "Interface.h"
namespace SimpleCalculator {
public ref class CalculatorForm : public System::Windows::Forms::Form
{
public:
CalculatorForm();
protected:
~CalculatorForm();
private: System::Windows::Forms::TextBox^ tbxLeft;
private: System::Windows::Forms::ComboBox^ cbxOpr;
private: System::Windows::Forms::TextBox^ tbxRight;
private: System::Windows::Forms::Button^ btnExec;
private: System::Windows::Forms::Label^ lblResult;
private:
System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
....省略
#pragma endregion
private:
// 「=」ボタン・クリックのハンドラ
System::Void btnExec_Click(System::Object^ sender, System::EventArgs^ e);
[System::ComponentModel::Composition::Import(ICalculator::typeid)]
ICalculator^ calculator_;
};
}
そして実装。
#include "CalculatorForm.h"
using namespace System;
using namespace System::ComponentModel::Composition;
using namespace System::ComponentModel::Composition::Hosting;
namespace SimpleCalculator {
CalculatorForm::CalculatorForm() {
InitializeComponent();
// Import/Export カタログをつくる
auto catalog = gcnew AggregateCatalog();
// 自分自身のアセンブリから探して追加する
catalog->Catalogs->Add(gcnew AssemblyCatalog(CalculatorForm::typeid->Assembly));
// カタログから作られたコンテナを基にImport/Exportを結びつける
AttributedModelServices::ComposeParts(gcnew CompositionContainer(catalog), this);
// 得られた演算子(Symbol)をComboBoxに追加する
for each ( String^ symbol in calculator_->Symbols() ) {
cbxOpr->Items->Add(symbol);
}
cbxOpr->SelectedIndex = 0;
}
CalculatorForm::~CalculatorForm() {
if (components) {
delete components;
}
}
System::Void CalculatorForm::btnExec_Click(System::Object^ sender, System::EventArgs^ e) {
Int32 left;
Int32 right;
// フォームから 左辺/右辺/演算子を取り出し、計算して結果を表示する
if ( Int32::TryParse(tbxLeft->Text, left) && Int32::TryParse(tbxRight->Text, right) ) {
try {
int result = calculator_->Calculate(left, cbxOpr->SelectedItem->ToString(), right);
lblResult->Text = result.ToString();
} catch ( Exception^ ) {
lblResult->Text = L"error";
}
} else {
lblResult->Text = L"?";
}
}
}
ここでのキモはコンストラクタ、これまでに用意した[Export(~)]と[Import(~)]/[ImportMany]とをコンストラクタで(つまり実行時に)結びつけます。
ExportとImportの一覧表に相当するカタログ:AggregateCatalogを生成し、自分自身が属するアセンブリをAdd()することで、アセンブリ内のExport/Importがカタログに登録されます。
カタログを引数に生成されたコンテナ:CompositionContainerとthis(=CalculatorForm)とをAttributedModelServices::ComposeParts()に与えると、CalculatorForm内から[Import(~)]が付けられた変数と、対応する(同じtypeidを持った)[Export(~)]の追加クラスをコンテナから探し出して結び付けます。
このとき、ICalculatorを実装したCalculator内には[ImportMany]なoperations_があるので、さらにこいつに対応する[Export(~)]もコンテナから探し出して……と、再帰的に結び付けてくれます。
「=」ボタンが押された時のハンドラは簡単。TextBoxとComboBoxからint値2つと演算子を取り出し、operator_に投げて結果を表示すればよし。
ビルド/実行してみましょう。ComboBoxに「+」と「-」が入っていますね。
