[C++] そういえば auto の落とし穴を示しておくと

C++11 あたりから、つーか、VC++2008 でもあったような気がするのですが、C++ では C# の var のように auto が使えます。
VB.NET の dim が dim に変わったように、C++ の auto が auto に変わったわけですが、昔の auto を知らない方は、まあ、知らなくてもよいかと。事実上使わなかったし。

さて、C++ でも auto で型推論ができるようになった訳ですが、これにちょっと落とし穴があるってのを少し。
auto を何に使うかというと、最初は typedef の代わりですかね。よくやる std::vector<string>::iterator ってのを、var で書き直すと非常に楽になります。

vector<string> vec;
for ( vector<string>::iterator it=vec.begin(); it != vec.end(); ++it )
{
  ...
}

こんな風に横に長いコードが

vector<string> vec;
for ( auto it=vec.begin(); it != vec.end(); ++it )
{
  ...
}

という風に書けます。かつては、typedef をして

vector<string> vec;
typedef vector<string>::iterator VECTOR_IT:
</p>
<p>
for ( VECTOR_IT it=vec.begin(); it != vec.end(); ++it )
{
  ...
}

というコードもあったのですが、これで無駄な typedef が駆逐されます。この typedef って #ifdef ができないので、結構厄介なのです。

■型推論を推論する落とし穴

皆さまご存じの通り、C++ には、「値型」と「ポインタ」って区別があります。更に云えば、「参照」ってのがあります。

先の型推論「auto」を使うと、

vector<int> vec;
auto bar = vec;

としたときに、bar の中身は vec と同じになります。なので、一見、「参照」のように見えるので、

vector<int> vec;
vector<int> &bar = vec;

と思い気や、実は違います。vector<int> のコピーになり、vec と bar は別ものなのですよ。

#include <vector>
#include <iostream>
using namespace std;

int main()
{
	vector<int> vec;
	auto bar = vec;
	// 要素を追加する
	vec.push_back(1);
	cout << &quot;vec:&quot; << vec.size() << endl;
	cout << &quot;bar:&quot; << bar.size() << endl;
	return 0;
}

のコードを動かすと vec:1, bar:0 という値を得ます。

そこで「参照」であることを明確にして「&bar」のようにすると、

int main()
{
	vector<int> vec;
	auto &bar = vec;
	// 要素を追加する
	vec.push_back(1);
	cout << &quot;vec:&quot; << vec.size() << endl;
	cout << &quot;bar:&quot; << bar.size() << endl;
	return 0;
}

vec:1, bar:1 という値を得ます。

ちなみに、ポインタを推論させて 「&vec」と指定すると、うまく vec:1 bar:1 になります。

int main()
{
	vector<int> vec;
	auto bar = &vec;
	// 要素を追加する
	vec.push_back(1);
	cout << &quot;vec:&quot; << vec.size() << endl;
	cout << &quot;bar:&quot; << bar->size() << endl;
	return 0;
}

なので、C++ の auto による型推論は、bar と &bar と *bar をうまく使い分けないと駄目なんですよ。まあ、大抵の場合大丈夫なんですが、コピーコンストラクタが定義してある場合はコンパイルエラーにならないのではまりどころです。
ちなみに、C# の場合は、このような装飾子がないので、推論に任せるしかないって感じなんですけどね。暗黙の変換を利用すると、var と 型指定では違った値にするトリックもできるし、dynamic の場合は型が後から変換されるために更にややこしかったり。

カテゴリー: C++ | 4件のコメント

実践 UIDD 入門のツールを考える

かつて、MVCが華やかし頃、その昔にはDoc-Viewというのがあり、その前には UI はキャラクタインターフェースとコマンドの組み合わせ出来ていた。そして、今は「プロトタイプUI」を「ユーザビリティUI」と分離させて、いまや GUI は完全にユーザーのものになったのである。

…というのを妄想してプレゼン資料を書いてみるテスト。MDA(Model Driven Architecture) とは違ったアプローチの仕方ですね。

■プレゼン資料

 

■ちまちまと説明

User Interface Driven Development は、UI を中心にしてソフトウェアを構築しよう、という主旨があって「考え中」なのですが、 そのためには、UI と内部ロジックの分離は必須なのですよ。ユーザーとはいえ、ライブラリに対して言えばプログラマもユーザー、プログラマが作ったアプリケーションを使うユーザー、という相対的な面もあり、いわば「おもてなしの心」って奴です(とお茶を濁します)。

さて、VB2 の頃に出てきた一番の売り文句が「プロトタイプが簡単にできる」だったのですよ。当時、VC++ で UI を作るのは非常に大変で、方眼紙を使って Windows アプリの UI を顧客に説明するか、Excel でぽちぽちと作るぐらいしか方法がなかったのです。まぁ、簡単なものであれば Excel を使うのもよく、当時 Excel VBA を使ってユーザーがアプリケーションを作れる、という一大ブームもあったわけで(それで被害をこうむっている人も多いみたいですが)、そのあたりはかつての COBOL を彷彿とさせます。いやいや、皆様あまり歴史は得意ではないようです。
そんなわけで、VB2 を使って「プロトタイプ」というものを作るわけですが、ComboBox やら TreeView やらをまじめに作ると結構大変なのですね。しかも、「顧客に見せる」ということ中身のデータを「ほんもの」らしく揃えないといけないという「固定概念」もあって、プロトタイプのくせに時間がかかっていました。
そして、当時「時間がかかったプロトタイプ」をどうしたかというと、「もったいない」から実プログラムに使おうという「もったいない」号令が発せられたわけです。まあ、プロトタイプにせよ、時間がかかり人件費がかかりコストがかかっているわけですから、マネージャとしてはなるべく「流用」をして、実コーディングの時間を減らす=コストを減らすことをしたい訳ですよ。が、実際はどうだったかというと、プロトタイプはプロトタイプに過ぎず、中身を本格的なプログラムに切り替えるには、それ相応の労力が必要であり、一概にプロトタイプを流用したからといって工数が減るという訳ではなかったのです。むしろ、時間がかかったりします。このあたり「プロトタイプ」としての試作品と、実コーディングを効率化させる「テンプレート」技法との差がわかっていなかった(という人が多かった)ということになります。
さらに言えば、なまじ「プロトタイプ」がきれいに出来上がってしまうものだから、プログラム自体がよくわかっていらっしゃらない顧客様におかれましては、「これ動いているから、これでいいじゃん」とのたまう始末でして、いやいや、所詮、紙芝居にすぎないのですから中身は空っぽなのです。なので、これから作りこみをしなければいけないのですが、「もうできているから、工数を削っていいよね」という仰りようもありまして、「プロトタイプ」に関してはもうこりごりというプログラマの方もたくさんいるかと。

で、ひとつの解決案としては、プロトタイプであること、紙芝居であることをわかりやすくするために、ペーパープロトタイピングという手法を取ったり、Microsoft の提案する Flow ツールを使ったりするわけです。あと、Visio の画面作成ツールとか。

■変更コストについての考察

まあ、画面作成というもの、ユーザビリティテストというものは、ソフトウェアの開発工程において一番最期におかれることが多いのですが、結局のところそれは上記な理由から「プロトタイプ」の練り込み具合、と実コードの入れ込みの乖離が原因だったりします。作ってみないことには、本格手な UI のテストができない、という訳ですね。確かに、車の試作品を作ってコースを走らせて、乗り心地をチェックするという「ユーザビリティ」テストがあるわけですが、これって車の量産よりも前にあるわけで、全体の製造工程の最後にある訳ではありません。もちろん、ソフトウェア開発は通常の製造業(プロダクト)とは違い、一過性のプロジェクトであるので、ソフトウェア開発自体はすべて「設計」であるということお言えるのですが、にしても、わざわざ仕様変更にコストがかかる(人件費と時間の両方がかかる)プロジェクトの最後の工程にユーザビリティテストを置くのはどうかな?と以前より思っていたわけです。

解決方法としては、実は2つあります。

  • ユーザビリティテストでの変更が、無視できるぐらいコストが安い(時間が早い)場合
  • ユーザビリティテストを変更コストの安い、前の工程に戻す。

という2つの方法です。たとえば「無視できるくらいコストが安い」の方法としては、Visual Studio 上でコントロールの背景色を変える、ってのがあります。以前は、コードでちまちまやっていたのですが、最近はプロパティの値を変えるだけですから、おそろしく「コストが安く」なりました。なので、この手のカラーリングに関しては、最後の工程でも構わないのです。よく「仕様変更」の受け入れ判断の基準は、この「変更コスト」が重視されますよね。

そうすると問題は「変更コスト」の高いものです。大体の場合、お客に「諦めて」もらうわけですが、社内アプリならばともかく、パッケージ製品であったり、iPhone などで配布するアプリだったりすると、そのあたりの「ユーザビリティ」は販売数に影響することがあるので、慎重に考える必要がありますよね。場合によっては徹夜で直すという、プログラマの安いコストを使う手段を使うことが多いのです。

でも、ちょっと考え直すと、方法はいくつかあって

  • 変更コストが高いものを、安くする手法を取り入れる。
  • 変更コストが高いものが、販売数(機会損失)に直結するか検討する。

という風に、変更コストに対する対処もいくつかあるのです。機会損失のほうは、もう少し分割できて、Windows Update のように後からプログラムを更新できるようにするとか、リリース後に対処するという「時間コスト」を伴わない方法を取ることもできます。

しかし、このリリース後の変更コストなのですが、リリースした後にもプログラマの時間コストを消費するために、隠れた変更コストではあるのですよ。あまり言及されていませんが、たいていの Web サービスが、サービスをリリースしたあとの不具合対蹠に時間を取られてしまい、新規機能がうまくリリースできない、あるいはサービス自体が陳腐化してしまう、のはこのあたりに原因があります。

というわけで、頭を使って「変更コストが高い」というのを「変更コストが安い」にシフトさせましょうというんが、プロトタイプ UI と実 UI の分離の主旨です。

■プロトタイプ UI の分離

実は、MVC パターンをうまく活用すれば(MVC パターンの実装の活用ではなくて、デザインパターンのひとつとして、ってことです)、View の変更はかなり楽になります。私がよくやる手法として、

  1. プロジェクトの初期の段階では、チープな View を用意して業務ロジックをテスト
  2. 業務ロジック自体は、xUnit を使う。
  3. 画面操作に関しては、チープな View でテストする。
  4. チープな View を徐々に本番 View に変更していく。

という手順で View を作ります。これの良い点は、最初のチープな View を変更するコストが恐ろしく安いことです。画面を増やすのも楽だし、画面自体のバグに悩まされることも少ないのです。かつ、業務ロジックを xUnit でテスト済みとして動かすので、画面のバグと業務ロジックのバグがきれいに分離できます。

しかし、長くこの手法をとっているのですが、問題もあって、工程が進んでいき複雑な View となってしまったときに、業務ロジックの変更が容易ではなくなってしまうのです。本来ならば業務ロジックは View と分離されるべきなのですが、そこは実学としてのソフトウェア開発ですから、混ざってしまうこともしばしばです。その混ざったところでバグが発生すると、なかなか調べるのが大変ということになるのですよ。このあたりは、システム試験までソフトウェア工程を付き合っている方ならばわかると思います。

そこで、昔とっていた手法としては、チープ View を隠し View として残しておくことです。業務ロジックを確認するためのデバッグ UI という立ち位置ですね。複雑になりそうな画面は、途中からデバッグ用の UI を新たに作ったりします。
しかし、これもちょっと問題があって、業務ロジックにチープ View が追随しないことがあるということです。実際デバッグ用に動かしてみたら、画面ロジックが違ってしまっているために「動かない」ということで、デバッグにならない、という落ちになるのです。

そんな訳で、プロトタイプ UI を残す場合には、業務ロジックに追随する形で、プロトタイプ UI を改変する、という手間が必要となり、結局のところ手間=コスト面から、プロトタイプ UI を残さないという結論にならざるを得ないのです。

■高速開発としてのプロトタイプ UI と実 UI

そういう訳で、既存の構造で「プロトタイプ UI」を残すのは難しいという考えから、では、根本から MVC パターンを考え直したらどうなるのだろうか?と思考実験してみたのが先のプレゼンテーションです。MDA が大変なのは、手作業(コーディング量)が多いからです。オブジェクト指向をインターフェース経由で行おうとすると、インターフェース絡みは自動化しておかないと(あるいは言語拡張をしないと)ちまちまとしたコードが多くなります。またインターフェースを重視という思想自体が「神の視点」(この話は後日)という時間軸を無視した思想に基づいてるので、当初の思惑の範囲内でしかシステム構築をできないのです。

そんな訳で、いつでも「後戻り」が可能であるという前提に経って、プロトタイプ UI と実 UI の両方を使っていきます。プロトタイプ UI 自体は極力コストを掛けないというパターンを使って、Excel のグリッドに項目を作ります。実際は、Excel 風のプロトタイプ作成ツールを用意するわけです。

永続化となるデータベースとの O/R マッピング、Model の構成、画面の基本的な Validation、項目同士の結合は、あらかじめ機能として用意してしまいます。項目の追加や削除は、まずはプロトタイプ UI に対して追加削除を行った後に、実 UI に反映させます。プロトタイプ UI の操作、更新のコストを減らすことにより、二度手間のコストを減らす、という方法ですね。

これの発想自体は、Rails や WebMatrix にあるものですが、発想のもととしては CakePHP です。プログラムを動かしながら、画面を構築していくという発想は、他にはないものなので一見してみるとよいです。

CakePHP はマニュアル無しでサイトを作れるのか? | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1753

な訳で、これを妄想とするか、実装するかはちょっと思案中。Windows Metro か iPad で作るとよりベターかなと考えていますが、どうでしょう?

カテゴリー: UIDD | 1件のコメント

【募集】Revese Reference Book Series の執筆協力者を募集

計画段階ではありますが、ざっとこんな感じで「執筆協力者」を募集します。
「協力者」となっているのは、単著、共著という形ではなくて、印税やらなにやらを共有して、次の資金にしましょうってな具合なので「協力者」になります。
まあ、自動的に PP-Club メンバになって貰って(準会員かもしれないけど)、そこから収入を得て貰いましょうってな具合です。

近々、(必ず?)某逆引きの執筆はスタートする予定なので、VB, C# の 2012版は確実。C++/CX 版は、状況によりけり。
技術メルマガはほぼ決まり、電子書籍化は自動化ツールを作る予定、WordPress Book 版は作らないと駄目だろうという状態ですね。

興味のある方は、以下まで。「RRB 執筆協力希望」とタイトルを付けて送ってください。折り返し、開発者エントリーシートなどを送ります。

お問い合わせ | Moonmile Solutions Blog
http://www.moonmile.net/blog/contact

カテゴリー: 仕事 | 【募集】Revese Reference Book Series の執筆協力者を募集 はコメントを受け付けていません

くたばれ TDD 原理主義者

いや、どこに「TDD 原理主義者」が居る訳ではないのですが、仮想敵としての「TDD 原理主義者」です(笑)。

早速、仮想な「TDD 原理主義者」の主な言い分を列記すると。

  • 全てのプログラムは、テストコードから作成しなくてはいけないッ!!!
  • 変更が発生した場合は、テストコードを修正してからッ!!!
  • 全てのメソッドはテストコードを書くべきであるッ!!!
  • UI 部分もいずれは xUnit を使わないといけないッ!!!
  • 実コードとテストコードは同じ言語でなくてはいけないッ!!!

な言い分がある…としますね。ええ、仮想ですから。実はケントベック著「テスト駆動開発入門」をざっと読んだだけだと上記な「原理主義」的な発言をし始めます。中身は、Java で書いてあるのですが、これをひと通り自分の使っている言語でやっていくと上記のような発言が「原理主義」的な発言でしかないことが分かります。

という訳で、以下、私なりの論破を書いておきますね。

■全てのプログラムは、テストコードから作成しなくてはいけない?

まあ、原則としてはそういうほうがベターなのですが、最近のように IDE 全盛の時代になり、コーディングをするときにインテリセンス(補完機能)が付くとなると作り方は別ですよね。テストコードを作る時には、まだ母体(テスト対象のコード)が無い状態では、当然のことながらインテリセンスは効きません。コンパイルさえも難しいわけです。

なので、テスト駆動にもあるのですが、いちど母体のひな型を作ってからテストコードを書きます。

…ということになっていますが、もうちょっと先に進むと、テストコードで母体の使い方を模索した後に本体のコードを書くというのが、実はユーザーインターフェースとしては良いことが分かっています。というのは、テスト駆動を行う場合(あるいは xUnit を導入する場合)いきおい、テストコードを書くために、関数やクラス名を先に決定しようとしてしまいます。実際のコードは「書いてみないとよくわからない」という前提があるのに、クラス名や関数名やパラメータを先に決定しようとする矛盾があるわけです。なので、本来のアジャイル的な手法を TDD に導入するならば(実際は、アジャイル手法を支える手段が TDD なのですが)、

  1. クラス、関数の使い方をテストコード=実験コードを使って書く。
  2. ひとまず、コンパイルが通るまでの本体コードを書く。この時点では、Assert はいれない。
  3. いくつか繰り返して、利用できるコードができたら、Assert を入れる。
  4. 以下は TDD の手法で繰り返し/リファクタリングする。

という流れになります。1,2 の「模索」が抜けると、TDD なのに一気にウォーターフォール地獄に突入するので注意が必要です。

■変更が発生した場合は、テストコードを修正してから?

まあ、原理的にはそうなのですが、実際はどちらでもいいのですよ。と言うのも、ある程度きっちりとテストコードを書いていると、

  • 本体コードを修正することにより、他のコードでエラーが出る。

ことが多いのです。本来のテストコードではなくて「影響範囲」と呼ばれるコードの範囲がエラーになるのです。実は、作業量的にはこちらの「影響範囲」のエラーのほうが重要なのです。TDD を使う場合、通常はオブジェクト指向言語を使う訳で、それに従うと「隠蔽化」がうまくできていれば、この手の「影響範囲」は非常に少なくてすみます。場合によっては全くありません。ですが、いろいろな static 関数やもろもろの設定が入り混じっているとこの「影響範囲」が非常に大きくなります。

こういう場合には、そこに手を入れるのは「危険」だから、別の方法を模索せよッ!!! という信号をキャッチしているのですね。というわけで、いきなりコードを修正したほうが作業量的に良い場合があります。

■全てのメソッドはテストコードを書くべきである?

これは、ケントベック氏自身が最近言っているように、全てのメソッドに対して書く必要はありません。単純なget/setのプロパティや、間違いようのない単純な計算までをテストコードに入れるのは時間の無駄です。そういえば、Visual Studio 2010 ではテストコードの自動生成があったのですが、VS 2012 ではなくなっています。「自動生成」自体は、あまり良い機能とはいえませんが、先に言うように「ひな形」を作るのは適しています。この件に関しては、M$ が原理主義に陥ってしまったのかもしれません。

なので、private メソッドは普通テストコードを書きません…が、複雑な private メソッドの場合は必要ですよね。そういう場合は、適当にコンパイルスイッチを使って public にしてテストをするコードを作ったほうがベターです。リフレクションを模索する方法もありますが、public するだけならば、作業量としては楽ですからね。ええ、メモリの配置が違うので、バイナリ構成が違う、というパターンもあるのですが、それほどシビアではない場合はよいでしょう、ってことで。

■UI 部分もいずれは xUnit を使わないといけない?

ここ、私も10年前はそう思っていたのですが、TDD の本質といいますか作業効率を上げるというアジャイル的な指針に沿うならば、無駄なことはやめよってことですね。UI の場合は、マウスクリック、テキストの入力、ウィンドウの移動など様々な要素が絡み合っています。その「絡み合っている」部分、曖昧な部分を、xUnit という「きっちり」とした部分で対応させるのはどうかな?と思う訳です。なので最近は、UI 関係のテストは、手作業に徹するようにしています。それ以外のライブラリの部分を xUnit を使う訳です。

なので、この方法を取るためには、MVC パターンを使うか、それに似たパターンでロジックの部分で xUnit を使うという設計が必要になるのです。

かつて、ガラケーの開発をした時に、ガラケーアプリのテストはひたすら人間がやっていました。親指でぽちぽちとひたすら1日8時間テストを繰り返すわけです。ゲームのテストもそうですが、いやぁこれはちょっと…人間のやることではないな、と思ったものです。ええ、派遣さんがやるんですけどね。

スマートフォンの場合はどうなんでしょうかね?シミュレータがあるから多少マシになったかもしれませんが、テスターの苦労は大変なもので、ええ本音を言えば関わりたくありませんッ!!! ってな位だと思ってます。

なのでこれを避けるために取る方策としては、xUnit で UI テストを自動化するのではなく、

  • 設計の段階で、xUnit を使う部分を決めておく。
  • 設計の段階で、UI テストを行う人件費/作業量を算出しておく

ってことですね。そうすると、人的作業の費用対効果が分かるので、このインターフェースは避けたほうがいいぞ、ってことになります。

■実コードとテストコードは同じ言語でなくてはいけない

これ、昔から言われていたことで、Java のテストをするならば、Java で書く。C# のテストをするならば、C# で書く。というのが「原理」です。この原理の理由としては、自分のテストコードは自分で書く、自分でリファクタリングをする、というパターンがあるからなんですよ。他の言語を使うと、頭を切り替えないといけないので、コーディング→修正のサイクルが遅くなってしまうという訳です。また、先の UIDD の理念から、テストコード=ライブラリの利用方法になるので、同じコードが望ましいわけですね。

が、なにも「同じでなくちゃダメ」って訳でもありません。ストアドプロシージャのテストをするのに、SQL 文を駆使しなくてもよいし、Fortran のテストをするのに、Fortran 自身で書く必要もありません。

  • 効率的にコンピュータを動かす言語(SQL や Fortran など)
  • テストを効率的に書きやすい言語

とは別であっても良いのです。

ちなみに、いくつか xUnit の「方言」がありますが、どれを使っても一緒です。と言いますか、長く使えるライブラリを使うのが無難ですね。私の場合、未だに古い CppUnit を使ってテストコードを書きます。実コードのほうは色々と新技術を使ってもいいのですが、xUnit の場合は古臭いパターンを使って、Assert を書くほうがよいのです。と言うのも、テストコード自体を書くのに頭を使いたい訳ではないですよね?本コードを作るのに頭と時間を割きたいのです。って理由が第一です。

 

さて、ここまで書いて「立ち上がれ TDD 原理主義者」というのも考えたのだけど、やめておきましょう。多分、いつまで経っても立ち上がらないし。

カテゴリー: 開発 | くたばれ TDD 原理主義者 はコメントを受け付けていません

[C#]Excel VBA で jQuery のようにアクセスできるC#ライブラリを作る

Excel VBA で jQuery のようにアクセスできるライブラリを作れるか? | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3217

なところの続き。と言うか、あのときは Excel VBA 内に閉じてないと使いづらいのでは?と思っていたのですが、Excel VBA から COM ライブラリを呼び出す、という方法でもいいことに気づきました。そこで、C# の相互運用(COMアクセス)を利用して、Excel を jQuery 風に使うライブラリ作成の続きおば。

■用法

こんなコードを書いて実行をすると、

Sub test()
    ' 初期化
    Dim obj As New ExQuery.Query
    obj.SetApplication Excel.Application

    obj.Cell("A1").Text = "最初"
    obj.Cell("A2:B10").Text = "埋める"
    ' 背景色を赤に設定
    obj.Cell("A2").CSS("background-color") = RGB(255, 0, 0)

    ' 文字色と背景色を変える
    Dim v As Long
    v = RGB(0, 0, 255)
    With obj.Cell("A3")
        '.CSS("color") = RGB(255, 255, 255)
        .CSS("color") = "#FF00000"
        .CSS("background-color") = RGB(0, 0, 255)
    End With
End Sub

以下のように Excel のシートを操作できます。

先行きは表を簡単に作るとか、検索を楽にするとかを実装したいですね。

■C#のソースコード

面倒なので全文を晒しておきます。プロジェクトを作る時に、

  • Visual Studio 2010 は管理者モードで起動する
  • Microsoft Excel を参照設定する。
  • プロジェクトのプロパティで「COM 相互運用機能の登録」をチェックする

を忘れずに。管理者モードのほうは、COM をレジストリに登録するの必要です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;

namespace Moonmile.ExQuery
{
	[ClassInterfaceAttribute(ClassInterfaceType.AutoDual)]
	[ComVisible(true)]
	public class Query
	{
		private Excel.Application _app;
		private Excel.Workbook _book;
		private Excel.Worksheet _sheet;
		private Excel.Range _sel;

		///
		/// Initalize Excel.Application object
		///
		public Excel.Application Application
		{
			get { return _app; }
			set
			{
				_app = value;
				_book = _app.ActiveWorkbook;
				_sheet = _app.ActiveSheet;
				_sel = _app.Selection;
			}
		}
		public void SetApplication(Excel.Application app)
		{
			this.Application = app;
		}

		public Excel.Workbook Book
		{
			get { return _book; }
			set
			{
				_book = value;
				_sheet = _app.ActiveSheet;
				_sel = _app.Selection;
			}
		}
		public Excel.Worksheet Sheet
		{
			get { return _sheet; }
			set {
				_sheet = value;
				_sel = _app.Selection;
			}
		}
		public Excel.Range Selection
		{
			get { return _sel; }
			set { _sel = value; }
		}

		///
		/// default constructor
		///
		public Query() { }

		public ExRange Cell( object row1, object col1 = null, object row2 = null, object col2 = null )
		{
			if (row1 == null)
				return new ExRange();

			var s = row1 as string;
			if (s != null)
				return Cell(row1.ToString());
			if (row2 == null)
				return Cell(int.Parse(row1.ToString()), int.Parse(col1.ToString()));

			return Cell(
				int.Parse(row1.ToString()), int.Parse(col1.ToString()),
				int.Parse(row2.ToString()), int.Parse(col2.ToString()));
		}

		///
		/// pattern:
		/// Cell("A1")
		/// Cell("A1:B10")
		/// Cell("#id")
		///
		///
		///
		protected ExRange Cell(string s)
		{
			Excel.Range rg;
			if (s.StartsWith("#"))
				rg = _sheet.get_Range(s.Substring(1));
			else if (s.StartsWith("."))
				rg = _sheet.get_Range(s.Substring(1));
			else
				rg = _sheet.get_Range(s);
			return new ExRange(rg);
		}

		///
		/// pattern:
		/// Cell(1,2)
		///
		///
		///
		///
		protected ExRange Cell(int row, int col)
		{
			if (_sheet == null)
				return new ExRange();
			var rg = _sheet.Cells[row, col];
			return new ExRange(rg);
		}

		///
		/// pattern:
		/// Cell(1,2,3,4)
		///
		///
		///
		///
		///
		///
		protected ExRange Cell(int row1, int col1, int row2, int col2)
		{
			if (_sheet == null)
				return new ExRange();
			var rg = _sheet.Range[
				_sheet.Cells[row1, col1], _sheet.Cells[row2, col2]];
			return new ExRange(rg);
		}
	}

	[ClassInterfaceAttribute(ClassInterfaceType.AutoDual)]
	[ComVisible(true)]
	public class ExRange
	{
		private Excel.Range _range ;
		private CSS _css;
		protected internal ExRange() { }

		public ExRange(Excel.Range rg)
		{
			_range = rg;
			_css = new CSS(_range);
		}

		public string Text
		{
			get { return _range.Value; }
			set { _range.Value = value; }
		}

		public CSS css
		{
			get { return _css; }
		}

	}

	[ClassInterfaceAttribute(ClassInterfaceType.AutoDual)]
	[ComVisible(true)]
	public class CSS
	{
		private Excel.Range _range;

		protected internal CSS() { }
		protected internal CSS(Excel.Range rg)
		{
			_range = rg;
		}

		public string this[string key]
		{
			get
			{
				switch (key.ToLower())
				{
					case "color":
						return _range.Font.Color;
					case "background-color":
						return _range.Interior.Color;
				}
				return "0";
			}
			set
			{
				if (value.StartsWith("#"))
				{
					int r = Convert.ToInt32(value.Substring(1, 2), 16);
					int g = Convert.ToInt32(value.Substring(1, 4), 16);
					int b = Convert.ToInt32(value.Substring(1, 6), 16);
					value = (r + g * 0x100 + b * 0x10000).ToString();
				}
				switch (key.ToLower())
				{
					case "color":
						_range.Font.Color = value;
						break;
					case "background-color":
						_range.Interior.Color = value;
						break;
				}
			}
		}
	}
}

■Excel VBA から使う COM 作成のコツ

いくつか引っ掛かるところを書き下しておきます。

Excel VBA の「integer」は、C# の Int64/short  Int16/short にあたります。16ビットの数値なんですね。なので、COM のメソッドの引数は、short にしておく必要があります。そうしないと、Excel VBA から COM に値を引き渡すときにエラーになります。

Cell メソッドの引数で object を使っていますが、VBA の Variant は、object でしか取れないようです。
最初は、Cell メソッドを多重定義して公開していたのですが、VBA 側で、Cell/Cell_2/Cell_3 と名前を変えられてしまうので、デフォルト値を付けて同じ名前で実行できるように変えました。

Excel VBA のインテリセンスを利用するために [ClassInterfaceAttribute(ClassInterfaceType.AutoDual)] という属性をつけます。実は AutoDual を使うと、COM 側のインターフェースが変わる(メソッドの順番が変わるなど)たびに、VB 側のビルドが必要になるのですが、今回は Excel VBA を対象にするのでこのまま使っています。Excel VBA の場合はインタープリタ的に COM を読み込むので、タイプライブラリを起動時に読み込んでくれるためです。このあたりは、以下のサイトを参考にしてください。

.NETコンポーネントをVB6から使用するための方法
http://www.sev.or.jp/ijupiter/world/dc_interrop/dotnet_com_interrop.html
Visual Basic 6.0 から Visual Basic .NET または Visual Basic 2005 アセンブリを呼び出す方法
http://support.microsoft.com/default.aspx?scid=kb;ja;817248
ClassInterfaceType 列挙体 (System.Runtime.InteropServices)
http://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.classinterfacetype(v=vs.110).aspx

■今後の予定?

css メソッドをちまちまと実装して、Excel VBA の複雑な UI を楽にアクセスできるようになると良いかも。
あと、C# で書いたので内部的に LINQ が使えますよね。Where/Select メソッド等を適当に公開してやれば、Excel VBA で LINQ を使っている感じに、なるかもしれない。とかとか。

カテゴリー: C#, Excel VBA | 3件のコメント

[C++]ATLで64bit版のCOMを作成する

Twitter / Marupeke_IKD: Windows7下でVisualStudio2005 …
https://twitter.com/Marupeke_IKD/status/245237326995927040

なところで、「Windows7下でVisualStudio2005で64bitなATLをVC++で作って、それを同じくVS2005環境で64bitビルドなVBで使おうとして「参照の追加」をするのだけど」の部分が気になって、一応確認してみたという話を少し。

VB6 自体は、Windows 7 上では動かないので、

  • Windows 7 上の Visual Studio 2005 で 64bit の COM を作成
  • Windows XP(かな?)の VB6 で先の COM を参照

なところでしょう。「VB の参照設定に出てこない」というところは、ファイルを参照させれば良いような気もするのですが、ちょっと実験してみます。

■環境

同じ環境を用意したい…つーのは面倒なので、似た環境を用意します。

  • Windows 7 x64 の Visual Studio 2010 で COM を作る。
  • Windows 7 x64 の Excel x86 を使って試す。

VB6 から参照するのも、Excel VBA から参照するのも似たようなものなので、これでテストします。以前、よくやっていた方法です。

■ATLでCOMを作る

  1. Visual Studio 2010 で「ATLプロジェクト」を選択

  1. プロジェクトに「ATLシンプル オブジェクト」を追加

  1. インターフェースを「デュアル」にするのを忘れずに

  1. クラスビューを使って、適当にメソッドやプロパティを追加(ここでは ICSmpのほうに追加)

IDL ファイルが自動生成されます。

 [
 	object,
 	uuid(A64C2AE7-E5C9-4B95-9C69-B48CB5E97966),
 	dual,
 	nonextensible,
 	pointer_default(unique)
 ]
 interface ICSmp : IDispatch{
 	[propget, id(1)] HRESULT Length([out, retval] SHORT* pVal);
 	[propput, id(1)] HRESULT Length([in] SHORT newVal);
 };
 [
 	uuid(9394A921-EE32-4302-B544-90EC6D538D69),
 	version(1.0),
 ]
 library SampleAtlCom64Lib
 {
 	importlib("stdole2.tlb");
 	[
 		uuid(C8A6D4E6-BFAF-48B4-AE57-F65847F2ACA6)		
 	]
 	coclass CSmp
 	{
 		[default] interface ICSmp;
 	};
 };
  1. プロパティのコードを修正
// CCSmp
STDMETHODIMP CCSmp::get_Length(SHORT* pVal)
{
	*pVal = m_length;
	return S_OK;
}


STDMETHODIMP CCSmp::put_Length(SHORT newVal)
{
	m_length = newVal;
	return S_OK;
}

ひとまず、これをビルドすると「Win32」のほうの 32bit 版の COM が作成できます。

■64bit版のCOMを作る

構成マネージャを使って「新規作成」をします。すると、Win32 の構成をコピーして「x64」の構成ができます。

VC++のライブラリは自動的にx64のほうに切り替えられます(これは、VS2005の頃は手動だったはず)。

これをビルドすると、x64/Debug というフォルダに COM が作成されます。Win32 のほうは、Debug フォルダですね。

■Excel VBA から 64bit COM を参照する

Excel VBA を立ち上げて、「ツール」→「参照設定」を開きます。

参照ボタンを押して、先の x64/Debug にある COM(*.dll)を参照させます。

無事に参照が出来た模様

オブジェクトブラウザでも確認

さっき適当に作った Length というプロパティが見れます。

■Excel VBA で動作確認する。

テストコードを使ってステップ実行してみる。

まぁ、普通に実行ができるわけです。

一応 Excel 2010 を確認しておくと、「ファイル」→「ヘルプ」から 32ビット版なのか 64ビット版なのかが分かります。

■どうして、Marupeke_IKD さんの環境では動かないのか?

という訳で、手元の環境では ATL で x64 の COM を作って 32ビット版の Excel VBA から利用できたわけです。
ただし、VB6 の 64ビット環境については、

Visual Basic 6.0 で 64 ビットの Windows オペレーティング システムの使用方法について
http://support.microsoft.com/kb/894373/ja

に書いてある通り、微妙な動きをするそうなので Win32 に揃えてしまったほうがよさそうですね。
あと、Excel 2010 のヘルプを開いて「64」で検索して、「Microsoft Office の 32 ビット版と 64 ビット版を選択する」の項目を見ると、いまいち Office 64bit の下位互換性が不完全であることが分かります。まぁ、不完全というか「切り捨てた」と言っていいのでしょうが、インストールしている office が 32ビットなのか64ビットなのかを判別しながら、Excel VBA を組むのはやりたくないですよね。という訳で、特に不具合がなければ32ビット版にするのがよいのかも。

このテストで作ったのはシンプルな「ATLシンプル オブジェクト」なので、MFC やら外部 DLL を使っている場合は、もっと注意が必要ですよね。そのあたりでうまく参照設定できないのかもしれません。

カテゴリー: C++ | [C++]ATLで64bit版のCOMを作成する はコメントを受け付けていません

[C#]Stringに正規表現の拡張メソッドを追加してLINQで使う

System::String に正規表現が使えたらいいなぁと思いつつ、書いてみたのがこれです。

■用法

        [TestMethod]
        public void TestMethod1()
        {
            string[] lst = {
                "masuda",
                "tomoaki",
                "yamasaki",
                "yumi" };

            var q = from t in lst
                    where t.IsMatch("y.*i")
                    select t;

            Assert.AreEqual(2, q.Count());
            Assert.AreEqual("yamasaki", q.First());
        }

        [TestMethod]
        public void TestMethod2()
        {
            string[] lst = {
                "masuda",
                "tomoaki",
                "yamasaki",
                "yumi" };

            var q = from t in lst
                    where t.IsMatch("ki$")
                    select new { match = t.Match("..ki$") };

            Assert.AreEqual(2, q.Count());
            Assert.AreEqual("oaki", q.First().match);
        }

単純に、Regex の Match, IsMatch メソッドを String クラスにくっつけただけなのですが、LINQ 内で直接使えるというのがメリットですね。別途、new Regex(…) を使えば、LINQ 内でも書けるのすが、それだと文が長くなってしまうしというパターンです。

■実装

実装はおそろしく簡単で以下な感じ。単純に Match, IsMatch をラップします。

using System.Text.RegularExpressions;

namespace Moonmile.StringRegex
{
    public static class StringRegexExtentions
    {
        public static string Match( this string target, string pattern )
        {
            var rx = new Regex(pattern);
            var mc = rx.Matches(target);
            return (mc.Count == 0) ? "" : mc[0].Value;
        }
        public static bool IsMatch(this string target, string pattern)
        {
            var rx = new Regex(pattern);
            return rx.IsMatch( target );
        }
    }
}

■利用方法

で、最終的には何をしたいのかという、以下な感じで2つのファイルの DIFF を調べたかったのです。
2つのファイルに、ファイル名の羅列があって、それを使って同期をするツールですね。マッチさせたい拡張子が限られているのでそれを内部で指定するという現場の即したツールです。

static void Main(string[] args)
{
#if false
	if (args.Length != 2)
	{
	    Console.WriteLine(&quot;usage: difffolders [src] [dest]&quot;);
	    return;
	}
	var lsrc = new List();
	var ldst = new List();

	StreamReader sr = File.OpenText(src);
	while (!sr.EndOfStream)
	    lsrc.Add(sr.ReadLine());
	sr.Close();
	sr = File.OpenText(dest);
	while (!sr.EndOfStream)
	    ldst.Add(sr.ReadLine());
	sr.Close();

	var llsrc = new List();
	var lldst = new List();

	string[] exts = {
	                    &quot;\\.cpp&quot;, &quot;\\.h&quot;, &quot;\\.hpp&quot;, &quot;\\.rc&quot;, &quot;\\.h&quot;, &quot;\\.f90&quot;, &quot;\\.fi&quot;, &quot;\\.for&quot;, &quot;\\.inc&quot;, &quot;\\.cmn&quot;
	                    };
	foreach (var ext in exts)
	{
	    string ext1 = ext + &quot;$&quot;;
	    llsrc.AddRange(lsrc.Where(n => n.IsMatch(ext1) == true).Select(n => n.Replace(&quot;C:\\Develop\\Main-branch-0&quot;,&quot;&quot;)));
	    lldst.AddRange(ldst.Where(n => n.IsMatch(ext1) == true).Select(n => n.Replace(&quot;C:\\Develop\\Main-branch-1&quot;,&quot;&quot;)));
	}
	llsrc.RemoveAll(n => n.IsMatch(&quot;\\Debug&quot;)); ;
	lldst.RemoveAll(n => n.IsMatch(&quot;\\Debug&quot;));
	llsrc.Sort();
	lldst.Sort();

	// 差分を計算する
	Debug.Print(&quot;src count: {0}&quot;, llsrc.Count);
	Debug.Print(&quot;dst count: {0}&quot;, lldst.Count);

	var onlysrc = llsrc.Where(n => !lldst.Contains(n)).Select(n => n );
	var onlydst = lldst.Where(n => !llsrc.Contains(n)).Select(n => n );
	var andlst = llsrc.Where(n => lldst.Contains(n)).Select(n => n);

	Debug.Print(&quot;only/and {0} {1} {2}&quot;, onlysrc.Count(), onlydst.Count(), andlst.Count());

	StreamWriter sw = new StreamWriter(File.OpenWrite(&quot;out-diff.txt&quot;));
	foreach( var f in onlysrc )
	    sw.WriteLine(&quot;- {0}&quot;, f );
	foreach (var f in onlydst)
	    sw.WriteLine(&quot;+ {0}&quot;, f);
	foreach (var f in andlst)
	    sw.WriteLine(&quot;m {0}&quot;, f);
	sw.Close();
}

で、これを作るうえで困ったのが、.NET の正規表現のまずさ(Regexの実装かな?)ですね。本来ならば、

var q = llsrc.Where( n => n.IsMatch(&quot;\\.(cpp|h|hpp|rc|f98|for|fi|inc|cmn)&quot;).Select( n => n );

のように一発で書きたいところなのですが、.NET 正規表現では、OR 演算子が無い模様。Group を使うんですかね?

2012/09/18 追記

とか思ったら、

代替構成体
http://msdn.microsoft.com/ja-jp/library/36xybswe(v=vs.100).aspx

に「|」が使えるやんッ!!! ってことで、多分先のコードは書けるハズ。後で書き直しますか。

 

 

 

正規表現クラス
http://msdn.microsoft.com/ja-jp/library/30wbz966(v=vs.80)

仕方がないので array で用意して foreach で廻しています。Perl 互換の正規表現ライブラリがあればそれを使いたいなと。

ちなみに、ファイル名から拡張子を取る Path.GetExtension を使っても良いんですけどね。

	string[] exts = {
            ".cpp", ".h", ".hpp", ".rc", ".h", ".f90", ".fi", ".for", ".inc", ".cmn"
            };

	foreach ( var n in lsrc ) {
		if (exts.Contains(Path.GetExtension(n)))
		{
			llsrc.Add(n.Replace("C:\\Develop\\Main-branch-0", ""));
		}
	}
	foreach ( var n in ldst ) {
		if (exts.Contains(Path.GetExtension(n)))
		{
			lldst.Add(n.Replace("C:\\Develop\\Main-branch-1", ""));
		}
	}
カテゴリー: C# | 3件のコメント

Windows 8 に「スタート」ボタンは要らない、という構造

Welcome to Classic Shell
http://classicshell.sourceforge.net/
dnki.co.jp
http://dnki.co.jp/system/joomla_1_0_xx/joomla_1_0_15JP_Stable/content/view/145/1/

なところで Windows 8 用のスタートボタンをダウンロードできるのですが、実は Windows 8 では「スタートボタンが要らない操作/構造」を実現しているというのを書いておきます。ええと、「Classic Shell」はそれはそれでいいんですけどね。

■最初はコマンドプロンプトだった。

皆さまご存じの通り?最初のMS-DOSはUnixの真似っこで、コマンドラインでした。コマンドラインというのは「キーボードでコマンドを打つ」というのと「コマンドの結果がテキストで表示される」ってのが主なインターフェースです。

image

この時に、各アプリケーションはコマンドという形で、DOS ディレクトリや BIN ディレクトリに置いていたのです。Unix のシェルコマンドと同じで、大抵はひとつの exe ファイルになっていたのです。
ユーザーは、アプリケーションを実行したい場合は「コマンド」を「キーボード」で打ちました。打ち込むキーの位置はプロンプトで決まっていて、必ずテキストの最後に入力されていた訳です。いわゆる、CUI という奴ですね。

まあ、その後 Unix にエスケープシーケンスを使った疑似Windowを作るライブラリもできたわけですが。

■複数のWindowに入力する方式

Windows 3.1 から複数のウィンドウを使ってアプリケーションを使うようになりました。Apple II の真似っこという訳で、マウスを使ってウィンドウを切り替えます。実は、MS-DOS の頃から、ゲームアプリなり、一太郎なりはマウスを使うインターフェースを作っていました。

マウスを使う場合には、右手(あるいは左手)がキーボードから離れます。大抵の人間は腕が3本ではないので(1本の人とか0本の人とかが居るからね)、キーボードの2本、マウスに1本というのは結構大変な作業なわけです。なので、いきおい、

  • 主な作業を、マウスを中心にして扱う(1本の腕を使う)
  • 主な作業を、キーボードを中心にして扱う(2本の腕を使う)

という使い分けが出てきたのですね。Unix の世界では、vi, emacs が「環境」と呼ばれる所以で、マウスを使わずに、全てのアプリをキーボードで操作しようという操作方法です。
最初の Windows 3.1 にはタスクバーがありませんでしたが、Windows 95 あたりから?タスクバーが出てきました。デスクトップの表示も OS/2、Apple などの関係から「実行しているアプリケーション」を表示するのか?、「なんらかの作業領域を示すのか?」と二分されていた時期があります。結局「デスクトップ」は「作業場所」になるわけですが。

この時、アプリケーションは肥大化してきて、複数の exe とダイナミックリンクライブラリ dll と各種の設定ファイルというスタイルになってきました。当時、NeXT Step はアプリケーションフォルダを作って、その配下を不可視にするという「インストール」という方法を提示していたのですが、Windows 3.1 あたりは適当に Windows フォルダあたりにばら撒かれていたわけです。

このばらばらのアプリケーションを(個人がインストールしたものも含む)、ちまちまとエクスプローラーを起動して exe をダブルクリックするか、適当なショートカットをデスクトップに貼りつけておいてアイコンをダブルクリックするか、というスタイルを取っていたわけです。そう、ここでアプリケーションを起動するのは主に「マウス操作」だったのですよ。

そうそう、ファイルマネージャは Mac の真似です。Mac のフォルダにアイコンが自由に配置できる、というスタイルを真似しています。

■「Program Files」とスタートメニュー登場

スタートメニューが登場したのは Windows 95 で、この最大の理由かつ宣伝文句が「ちらかった実行ファイルをひとつのところから起動できる」というところでした。先に書いた通り、あらゆるフォルダに散ってしまった exe ファイルは、いちいちエクスプローラーを開かないと起動ができなかった訳です(賢い人は、ショートカットなりプロンプトから起動できるようにしていましたが)。

この大混乱の状態を解決するのが「スタート」ボタンという訳です。
で、この「スタート」の意味は他にも理由があって、

  • デスクトップには、Excel ファイルやら Word ファイルやらが散っている状態になっていた。
  • 実行ファイルのショートカットもデスクトップに散らばって、混乱していた。

という「デスクトップ」の混乱もその要因です。これは、デスクトップに散らばってしまった「Microsoft 製品」のアプリケーションを「スタート」に押し込めることで、目立つ位置に置くことができ、というメリットがあった訳ですよ。当時「Program Files」という空白付きのフォルダ名はいったいなんなのだ、という混乱もありました。Unix の場合は /user/bin や /user/local/bin に置く習慣があるのに、それでも良かったわけですが。ええ、スラッシュとバックスラッシュのようなビルゲイツの「意地」(あるいは反骨精神)だったような気がします。

スタートメニューからの階層は、ぽちぽちとマウスで追うことになります。Win キーで開いて(当時は Ctrl+ESC )、カーソルキーで探すこともできるのですが、主にマウス操作ですね。

■「プログラムの検索」の登場

さて、無事「スタート」メニューの下にアプリケーションを押し込めていたわけですが、フリーソフトも含めて、あらゆるアプリがこのスタートメニューにアイコンを置くようになりました。色々とつも込むわけで、長く使っているうちに階層構造も深くなって、3段階もあるツリーを辿って行かないとアプリが起動できないという、本末転倒なことが起こってきたわけです。

また、HDD の値段が安くなり、ローカルストレージに大量のファイルを置けるようになってきました。そうすると、写真やら仕様書やら何やらかにやらと、フォルダで整理していたものの、何が何処にあるのか分からなくなっている状態になったわけです。更に云えば、「マイドキュメント」のフォルダを C ドライブに置くようにしたために、空き容量の多い D ドライブは全く使われないという、なんともまぁ「ほむほむ」な現象も起こっていたわけです。このあたりは、Microsoft の技術力…というか「先見の無さ」が露呈されるところです(ええ、わざとやっているのかな?という時もありますが)。

で、写真などのファイルを全文検索するという google のツールが登場しました。google はウェブ上の情報を検索することを目的に使っていたし、Microsoft も安泰と思っていたわけですが、突如ローカルの PC の分野にまで踏み込んで来たわけです。そこで、慌てた Microsoft 社は、ローカルの検索をするための「プログラムの検索」を「標準」という形で追加したわけです。Mac にも Finder がありますが、これも似たような機能です。

さて、プログラムを実行するときに階層構造になっているのはいままでと同じでマウスを使います。しかし「プログラムの検索」をするときはキーボードを使うわけです。実は、Win キーを押したときに「プログラムの検索」にカーソルが置かれるので、続けてキーを打てばアプリを検索することもできるのですが、なかなかそういう使い方をしている人はいませんよね。順序が逆で、アプリをマウスで探した結果、どこにあるか分からないので「プログラムの検索」を使うという順序になっています。
なので、再びキーボード+マウスの3本腕が必要になってきたわけです。

■一方で iPad が発売されて、アイコンで起動する

iPad が発売されたのが二年前ですが、突如としてアプリ起動の概念が変わります。実際は iPhone の継承となるのですが、「スタート画面」あるいは「メニュー画面」というのを複数枚用意して、アイコンを並べるという方法です。一見、デスクトップにアイコンが並んだ混乱状態に見えますが、Apple はひと工夫ほどこしました。

  • たくさんのアイコンがある場合は、複数の画面をスライドさせる。
  • アイコンは固定位置になる。

という2点です。Windows のデスクトップは、1枚しかないのでたくさんのアイコンで溢れてしまうそれだけ混乱してしまいます。マルチデスクトップのフリーツールもあるのですが、結局 Microsoft から提供されることはありませんでした。また、あえて「固定位置にアイコンが整列」されることによって、アイコンが「綺麗に並んでいる」というスタイルに戻しました。

あとは、アプリケーションが動作するときは必ず「全画面で動く」というスタイルに戻しました。これは、Apple の iOS 上「戻さざるを得なかった」という制約なのですが、この「戻さざるを得ない」という制約を、「全画面で動くようにして、アプリを自由に使えるようにしました」というのが、ジョブズらしい発想の転換(というか営業方法)ですね。見習いたいところです。

■Windows 8 のスタートボタンをなくした

さて、やっとこさ Windows 8 の「スタート」ボタンに話が戻りますが、タブレット PC への本格的な参入として、Microsoft はスタートボタンを消してスタート画面(メニュー画面)にしました。あの極めて狭い画面の Windows CE の Phone ですらスタートボタンが付いていた(非常に使いづらかった)ものを消したわけです。ちなみに、MS-Office にも「スタート」ボタンが付いていたのですが、Office 2010 あたりからスタートボタンは無くなっています(苦笑)。

タブレット PC の場合は、iPad と同じように、スタート画面で画面をスライドしながらアイコンを探します。先の記事に書いたようにおそらく「意匠」の関係から iPhone/iPad と似たものにしませんでした。なので、デザイン上は非常にダサいのですが、これで用途は足ります。

アプリケーションはタイルに文字や画像を貼りつけられるのですが、見て分かる通り、どのアイコン/タイルが、何をするアプリなのか分かりませんよね(苦笑)。似たようなタイルを作ると何が何やらわからなくなるという混乱は、これでも発生します。まあ、タブレット PC の場合は、ぽちぽちと指でタップするのであまりストレスにはならないかと。所詮、ブラウジングの範疇ですからね。

が、デスクトップを使う場合=仕事で使う場合には、それらのイライラはできるだけ排除したいものです。デスクトップ(作業場所)とアプリの起動とは、すばやく交互に行き来したいですよね。Windows 7 までは、そのための「スタート」ボタンであり、「プログラムの検索」だったわけです。

で、いくつか Windows 8 で作業をしていくと、アプリ起動をするときのストレスが Windows 7 の時よりも減っていることがわかります。

Windows 7 の場合は、

  1. キーボードからマウスに手を移す。
  2. スタートメニューをマウスでクリックする。
  3. 「すべてのプログラム」をマウスでクリックする。
  4. 目的のプログラムをマウスで探し出す。
  5. アプリが起動したら、マウスで大きさを変える。
  6. キーボードで作業を始める。

という具合です。勿論、「プログラムの検索」を使えば

  1. Win キーを押す
  2. キーワードを入力して、目的のアプリを探し出す。
  3. アプリを実行する。

という手順に減ります。この手順は短くてよいのですが、何故かプログラムの検索結果が出るのが非常に遅いんですよね。と言うのも、アプリの検索もファイルの検索も一括で行ってしまっているために、「アプリを起動したい」のに「目的以外のファイルまで出てくる」まで待たされるというストレスがあるのです。なので、大抵の場合は「すべてのプログラム」からマウスで選択しています。

これが Windows 8 の場合には、「スタート」ボタンがないので、

  1. 「迷わず」 Win キーを押す。
  2. キーワードを入力して、目的のアプリを探し出す。
  3. アプリを実行する。

という「迷い」がない様態になるのです。しかも、全てキーボードを使って実行できますよね(実際は、最大化するためにマウスが必要ですが)。やり方が少ないために、ストレスが少なくなると好例です。これを Microsoft 社が「意図」したかどうかは別ですが、少なくとも「すべてのプログラム」や「プログラムの検索」のストレスは減ります。

実は「プログラムの検索」をアプリに限って結果を出せばいいんですけどね。実際 Windows 8 の検索はアプリに限っているので(笑)。

Windows 8 では、デスクトップとスタート画面の切り替えを Win キーのみで行えます。また、アプリ検索の画面も Win キーを2回押せばデスクトップに戻れます。アプリ検索に対しては、Win+Q とうショートカットも用意されていますが、まあ、Win キーを押した後に、直接キーワードを入力するというのがストレスが無い方法です(覚えるショートカットキーが減りますからね)。

という訳で、Windows 8 のデスクトップには「スタート」ボタンが要らなくて、キーボードだけを使って作業ができるというストレスの少ない環境が実現されつつあるという、主にデスクトップ PC に限った環境ですが、なかなか MS-DOS のコマンドラインに戻った雰囲気で快適です。

カテゴリー: windows 8 | 8件のコメント

[C#] Thumbコントロールを継承したユーザコントロールを作る

[C#] WPFのThumbコントロールを使ってドラッグを実装する | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3701

どうせなので続きをちょっと書いておきます。

Thumb コントロールを使うと、コントロールのドラッグが簡単にできるようになったのですが、汎用的に使うにしては色々な処理をいれないと駄目なのが難点です。特にドラッグに必要な、

  • DragStarted
  • DragCompleted
  • DragDelta

の3つを実装する必要があるのです。なので、これをユーザーコントロールでくるんでしまいます。ユーザコントロールは UserControl を継承していますが、直接 Thumb コントロールを継承するように変更します。

<Thumb x:Class=&quot;SampleWpfDragBitmap.ThumbDrag&quot;
             xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
             xmlns:mc=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot; 
             xmlns:d=&quot;http://schemas.microsoft.com/expression/blend/2008&quot; 
             mc:Ignorable=&quot;d&quot; 
             d:DesignHeight=&quot;50&quot; d:DesignWidth=&quot;50&quot;
            DragCompleted=&quot;mark_DragCompleted&quot;
            DragStarted=&quot;mark_DragStarted&quot;
            DragDelta=&quot;mark_DragDelta&quot; 
       >
    <Thumb.Template>
        <ControlTemplate TargetType=&quot;Thumb&quot;>
            <Ellipse Fill=&quot;{TemplateBinding Background}&quot; Width=&quot;50&quot; Height=&quot;50&quot;/>
        </ControlTemplate>
    </Thumb.Template>
</Thumb>

内部のテンプレート記述はそのままです。ルートとなるタグが「Thumb」に変えるのがミソですね。
あと、このコントロールを使う方から背景画像(Background)を指定できるように、TemplateBinding を指定しておきます。これで Thumb コントロールには Background プロパティがないのですが、この ThumbDrag コントロールでは背景を指定できるようになります。

/// <summary>
/// ThumbDrag.xaml の相互作用ロジック
/// </summary>
public partial class ThumbDrag : System.Windows.Controls.Primitives.Thumb
{
	public ThumbDrag()
	{
		InitializeComponent();
	}

	private void mark_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
	{
	}
	private void mark_DragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)
	{
	}
	private void mark_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
	{
		Canvas.SetLeft(this, Canvas.GetLeft(this) + e.HorizontalChange);
		Canvas.SetTop(this, Canvas.GetTop(this) + e.VerticalChange);
	}
}

ユーザーコントロールのほうで実装するのは、DragDelta イベントだけです。

<Window x:Class=&quot;SampleWpfDragBitmap.MainWindow&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        Title=&quot;MainWindow&quot; Height=&quot;350&quot; Width=&quot;525&quot; xmlns:my=&quot;clr-namespace:SampleWpfDragBitmap&quot;>
    <Canvas >
        <my:ThumbDrag  Canvas.Left=&quot;32&quot; Canvas.Top=&quot;36&quot; Height=&quot;58&quot; x:Name=&quot;thumbDrag1&quot; Width=&quot;56&quot; BorderThickness=&quot;2&quot; BorderBrush=&quot;green&quot; Background=&quot;#FFB72323&quot; />
        <Button Canvas.Left=&quot;428&quot; Canvas.Top=&quot;0&quot; Content=&quot;Color&quot; Height=&quot;23&quot; Name=&quot;button1&quot; Width=&quot;75&quot; Click=&quot;button1_Click&quot; />
        <Button Canvas.Left=&quot;428&quot; Canvas.Top=&quot;40&quot; Content=&quot;Bitmap&quot; Height=&quot;23&quot; Name=&quot;button2&quot; Width=&quot;75&quot; Click=&quot;button2_Click&quot; />
    </Canvas>
</Window>

この ThumbDrag コントロールを使う場合は、上記のように my:ThumbDrag とします。一度コンパイルして、ツールバーからドロップすれば自動で作られます。

ボタンを2つ付けておいて、背景の色を変えるテストと、背景にビットマップを貼りつけるテストをいれています。

public partial class MainWindow : Window
{
	public MainWindow()
	{
		InitializeComponent();
	}

	private void button1_Click(object sender, RoutedEventArgs e)
	{
		this.thumbDrag1.Background = Brushes.Green;
	}

	private void button2_Click(object sender, RoutedEventArgs e)
	{
		var bmp = new BitmapImage(new Uri("images/snap.png",UriKind.Relative));
		var br = new ImageBrush(bmp);
		this.thumbDrag1.Background = br;
	}
}

こんな風に、Background プロパティに Brush を指定すれば良いのです。背景画像の場合には、ImageBrush を作成して設定します。

こうやって、コントロールにしておくと複数コピーするのが簡単なんですよね。これは、XAML のコードを3つコピーしたものです。

さて、これを動的に増やしたり減らしたりする場合はどうするかというと…まじめに VisualTreeHelper を使ってもよいのですが、数がさほど多くないのであれば、あらかじめ XAML 上で固定で追加しおいて見えないようにしておく、というのがコード的に楽ですね。「最大値」は決まってしまいますが、業務アプリの場合はこれで十分かと。

カテゴリー: C#, XAML | [C#] Thumbコントロールを継承したユーザコントロールを作る はコメントを受け付けていません

[C#] WPFのThumbコントロールを使ってドラッグを実装する

WPFでコントロールをドラッグ(1) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/709
Thumbコントロールでドラッグ | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/698

なところで、WPF のドラッグコントロールを探していたのですが、どうやら Thumb コントロールを使って template で形状を変えるのがよさそうです。ここでは、Thumb.Template を使って形状を変えていますが、まぁ、これを static resource に書いても良いし、それは色々ってところでしょう。

<Window x:Class=&quot;SampleWpfDrag.MainWindow&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        Title=&quot;MainWindow&quot; Height=&quot;350&quot; Width=&quot;525&quot;>
    <Canvas>
        <TextBlock Canvas.Left=&quot;0&quot; Canvas.Top=&quot;0&quot; Height=&quot;22&quot; Name=&quot;textPos&quot; Width=&quot;75&quot; Text=&quot;x:0 y:0&quot; />
        <Thumb Canvas.Left=&quot;37&quot; Canvas.Top=&quot;30&quot; Height=&quot;30&quot; Name=&quot;mark&quot;  Width=&quot;30&quot; Background=&quot;LightBlue&quot;
               DragCompleted=&quot;mark_DragCompleted&quot;
               DragStarted=&quot;mark_DragStarted&quot;
               DragDelta=&quot;mark_DragDelta&quot;
               >
            <Thumb.Template>
                <ControlTemplate TargetType=&quot;Thumb&quot;>
                    <Ellipse Fill=&quot;LightBlue&quot; Width=&quot;30&quot; Height=&quot;30&quot; />
                </ControlTemplate>
            </Thumb.Template>
        </Thumb>
    </Canvas>
</Window>

ドラッグイベント自体は、前の記事に書いた通りで、複数のコントロールを利用する場合には、別途イベントの追加の仕方を考えたほうがよいかなと。ただし、ドラッグイベント自体は基本的に変わらないので、なんらかの形で Thumb コントロールを包んでやるほうが実装的には良い気がします。画面上からはみだすとか、そんな感じの動きとか。

public partial class MainWindow : Window
{
	public MainWindow()
	{
		InitializeComponent();
	}

	private void printPos(UIElement el)
	{
		int x = (int)Canvas.GetLeft(el);
		int y = (int)Canvas.GetTop(el);
		textPos.Text = string.Format(&quot;x:{0} y:{1}&quot;, x, y);
	}

	/// <summary>
	/// ドラッグ開始
	/// </summary>
	/// <param name=&quot;sender&quot;></param>
	/// <param name=&quot;e&quot;></param>
	private void mark_DragStarted(object sender,
		System.Windows.Controls.Primitives.DragStartedEventArgs e)
	{
		mark.Background = new SolidColorBrush(Colors.Orange);
	}
	/// <summary>
	/// ドラッグ終了
	/// </summary>
	/// <param name=&quot;sender&quot;></param>
	/// <param name=&quot;e&quot;></param>
	private void mark_DragCompleted(object sender,
		System.Windows.Controls.Primitives.DragCompletedEventArgs e)
	{
		mark.Background = new SolidColorBrush(Colors.Purple);
	}

	/// <summary>
	/// ドラッグ中
	/// </summary>
	/// <param name=&quot;sender&quot;></param>
	/// <param name=&quot;e&quot;></param>
	private void mark_DragDelta(object sender,
		System.Windows.Controls.Primitives.DragDeltaEventArgs e)
	{
		printPos(mark);
		Canvas.SetLeft(mark, Canvas.GetLeft(mark) + e.HorizontalChange);
		Canvas.SetTop(mark, Canvas.GetTop(mark) + e.VerticalChange);
	}
}

 

カテゴリー: C#, XAML | [C#] WPFのThumbコントロールを使ってドラッグを実装する はコメントを受け付けていません