ラベル C# の投稿を表示しています。 すべての投稿を表示
ラベル C# の投稿を表示しています。 すべての投稿を表示

2011年10月22日土曜日

MVVMを図にしてみた

MVVMに則ったクラスの機能分担
ユーザ入力によるイベント発生時の動作
Modelの処理中にプロパティ変更イベントが発生した場合の動作

最近真面目にプログラム書いてるので久々の更新。

WPFで書くにはMVVMというデザインパターン(?)で書くのが定説らしい。で、色々調べてみましたが、MVVM人気ないんですかね。要る要らないとかの宗教論争が多いです。要るか要らないかは使う側が考えるから、提供側はバシッと情報提供頼むよ(To マイクロソフト)。提供元がフラフラしてるからMVVMの考え方もフラフラしてるみたい。その辺は追々書いてみます(やる気の神が降りてきたら…)。

で、勉強も兼ねてMVVMの役割分担を図にしてみました。合ってるかどうかは不明。合ってなくてもいいんだ。まだ考え方がフラフラしてるんだし。MVVMは基本的に3つのクラスで構成されます。VMを複数のクラスに分けたりVを入れ替えたりするかもしれませんが、考え方的にはM-V-VMの3つ。適当に解釈してこれらの役割を書くと以下のとおり。

  • Model: 目に見えない処理をするクラス
  • View: 目に見える処理するクラス
  • ViewModel: Modelを目に見える形に変換するクラス

語弊がありすぎる気がしますが、細かく書くときりがないのでこの程度に。なおViewは目に見える処理しか行わないので、基本的にはXAMLだけ記述することになります。だけどXAMLだけでは書き切れない処理はコードビハインドと言ってC#で書いてもいいそうです。もっとも、コードビハインドの是非こそがMVVMの最大の争点な気もしますけど。その辺はおいおい(やる気の神がry)

図はModel、ViewModel、Viewが横並びになっていますが、ModelはViewModelやViewを気にする必要はありません。ViewModelもViewを意識する必要はありません。「全く意識しないわけではない」と説明されることもありますが、ありません。あってはならないのです。多分ないです。その説明は追々(やる気のry)。MVVMは各クラスで役割分担を明確にするために用いられるため、不要な参照はしません。ViewModelがViewを全く知らなくても成り立ってしまうなんて。マイクロソフトに、そしてデータバインディングに感謝。

ViewModelはModelのインスタンスを、ViewはViewModelのインスタンスを持ちます。なお、図には書いていませんがViewModelが持つModelのインスタンスは非公開ですのでお間違いなく。ViewがModelを触れるなんてことはありません。また、ViewModelもプロパティとコマンドしか公開しないので、ViewがViewModelを操作することもできません。Viewは飽くまでも表示だけです。

で、MVVMにすると何がいいのかってことですが、言わなきゃわからん奴は使わなくていいよ。データバインドとコマンドバインドのおかげで表示部(View)と演算部(ModelとひょっとしたらViewModel)を完全に分離できてるんだぞ。View取り替えるだけで表示部を変えられるなんてステキじゃないか。

VはMやVMと切り離されている(灰色は壁です)ので、V1をV2に取り替えてもOK(V2も同じVMに対応している前提)

「そんなのデータテンプレートでもできるだろ?」って思ったあなた、Viewを完全に独立させるには↓みたいにイベントハンドラ内でViewを参照してはダメなんですが大丈夫でしょうか。

public void button1_Click(object sender, EventArgs e)
{
  // ボタンが押されたらTextBox1の文字列で検索実行
  string word = TextBox1.Text; // ←TextBox1というViewの名前を使用しているのでMVVM的にはNG
  this.search(word);
}

「イベントハンドラでViewの値取れないとか無理ゲーじゃね?」と思ったあなた、そもそもその発想がMVVM的ではないのですよ。MVVMではModelが演算データの原本で、ViewModelが表示データの原本なんですよ。Viewはそれを表示してるだけなんだから、ViewModel(=コマンド規定側≒イベントハンドラ側)にはコマンド(≒イベント)を処理するためのデータは全てあるのですよ。なきゃおかしいのです。

「じゃあウィンドウの状態とかドラッグアンドドロップされたファイルのパスみたいにView操作契機で生じるデータ知りたいときどうするの?」と思ったあなた、長くなるので続きはまた今度(やる気がry)。端的に言うとTriggerやらActionやらBehaviorやらMessengerやらを使うとなんでもできるらしいです。

で、このTrigger、Action、Behavior、MessengerがMVVMの気に食わないところなんですよね。せっかくの美しいMVVMの概念をややこしくしている気がして。

久々の長文だったから疲れた。

Posted by Picasa

2009年4月6日月曜日

Word文書のチェックツールを作ってみました→障害だらけ。。。

仕事で作成しているWordの文書は日本語は「MS Pゴシック」英数字は「Arial」じゃなきゃダメという書式(フォント)の決まりがあります。そのため、提出前にちゃんと指定されているフォントで書かれているか、ディスプレイをにらみながらチェックします。文字にアンチエイリアシング(or Clear Type)を使っていればパッと見てわかるんですが、そうでない人(何でそうしない??)は、300%ぐらいに拡大してにらみつけています。

まーこんなのちゃんとWordのスタイル機能使って作成していれば、チェックするまでもないんですが、たまにどこぞからコピーしてきた個所があると、コピー元のダメダメな書式が反映されてしまい、検閲に引っ掛かります。

さて、こんな作業に労力も掛けていられないので自動化してしまおうと企みました。VBAでちょちょいとできそうなので。私の場合はVBAではなくC# + COMですが。

要は文書内の「文字スタイル」を頭から走査して、そのフォントを確認すればいいわけです。何の複雑さもないですよねー :)

Word文書の操作を学ぶ

ヘルプを見ると、Word文書(Documentオブジェクト)に対する操作の多くは、操作範囲を表すRangeオブジェクトに対して行うと書いてあります。Excelと同じですね。また、そのRangeを移動するためのNextメソッドというのがあることも分かりました(他にも移動する手立てはたくさんあります)。これはExcelにはないWord独自のものですね。Nextメソッドの引数には、どういう単位で移動するかを表すWdUnits型の引数があります。例えば行単位ならWdUnits.wdRow、段落単位ならWdUnits.wdParagraphといった具合です。具体的なコードは以下の通り。

object wdUnit = WdUnits.wdParagraph;
object value = 1;
while ((range = range.Next(ref wdUnit, ref value)) != null)
{
  ErrorList.Items.Add(range.Text);
}

さらにヘルプを見ると(実際にはVisual StudioのInteliSenseが教えてくれたんですが)、WdUnits.wdCharacterFormattingという値があるじゃないですか。これが文字書式単位で移動するというパラメータのようです。もうこれで8割ぐらいできたも同然です :)

8割できたはずが、大きな壁

ところがどうにもうまく動きません!:o 上記のソースでいえば、range.Textで取ってきた値の一覧を見ると、文字書式毎の一覧になりません。というか、そもそも欠落だらけで、何単位で取ってきたのかもよく分かりません! wdCharacterFormattingがうまく動かない(=バグ)なのかと思い、試しにwdRowを指定してみると、実行時に「引数が違います」と怒られてしまいます。もうわけが分からない!!

WordのVBAに関する情報は少なく、この状況を打開できる術がありません。。純正ヘルプとMSDN以外では、日本語の情報はほとんどありません。

困った困った。。。