C#Programing Archive

SEP
18

Webkit.NETを触ってみた

Published:2009-09-18 01:52:13 UTC

image 「C#凄い使いやすくて良い言語なんだけど、WebBrowserコントロールは残念だよなー、Trident最近頑張ってるんだけどWebkitとかに比べるとまだまだだよなー」とか常日頃から感じていたのですが、ふと「WebkitをC#から扱うためのラッパーとか探せばあるんじゃね?」という思いつきで「Webkit .net」とググってみると、ありましたありました、「Webkit.NET」というまんまなタイトルのプロジェクトが。オープンソース恐るべし。コンパイル済みのバイナリと共に、サンプルアプリがあったので実行してみました。

image

・・・なにこれ凄い。日本語も全く普通に通りますし、普通に使えそうです。というわけで自分でもテスト用にWinFormsプロジェクトを作って動かすテストをしてみたので、メモを残しておきます。

まず、WebKit.NETライブラリをWebKit .NET | Get WebKit .NET at SourceForge.netからDLしてきます。その中のbinフォルダ内のファイルがライブラリを構成しています。WebKitBrowser.dllがC#から参照するラッパーです。

まず、WebKitBrowserをツールボックスに追加してやります。ツールボックスのコンテキストメニューの「アイテムの選択」という項目をクリックします。

image

この際、ダイアログが表示されるまで、めちゃ重たいかもしれませんが、ぐっとこらえましょう。

image

そして参照ボタンをクリックして、WebKitBrowser.dllを選択してやれば、ツールボックスにWebKitBrowserが追加されます。

image

あとは普通にコントロールをポトベタしておわり…でもありません。。

まず、プロジェクトのプロパティから、プラットフォームターゲットをx86に設定します。Webkitは、現在、x86版のみ提供されているそうです。
image

続いて、デバッグのタブにある、「Visual Studio ホスティング プロセスを有効にする」という項目のチェックを外します。Visual Studioホスティングプロセスは、Visual Studioでのデバッグを効率化させるためのもののようですが、今回のようにアンマネージドコードを連携する場合は上手く働かず、エラーの原因になることがあるようです(Visual Studioホスティングプロセスの解説:VSホスト・プロセス(*.vshost.exe)とは何か?[VS 2005のみ、C#、VB] - @IT)。これが原因のエラーにぶつかって、@JZ5さんに助けを求めたところ、このオプションを教えていただけました。ありがとうございました。

image

こうしてビルドした上で、最後に仕上げに、ビルドして出来た実行ファイルやWebKitBrowser.dllと同じディレクトリに、Webkit本体であるWebkit.dllや、その依存するdll群を放り込みます。ビルド後のイベントでコピーするコマンドを発行してやると良いようです。

image

というわけで出来ました。よいよい。素晴らしい。DOM周りのメンバの実装が未だらしく、出来ることは結構限定されていますが、Monoでも動かせるようにしたいよねー、とREADMEに書かれていたりと、これからの進展に期待したいプロジェクトです。

CardSafe/EはMVVMパターンに則って作るように心掛けたのですが、WPFと同じ感覚でSilverlightにMVVMパターンを適用しようとすると、色々と不都合が出てきて苦労させられました。Silverlightではオミットされている機能が結構あるので、そこに引っかかるとしばしば手戻りが発生します。このエントリではCardSafe/Eを書いていて気付いた点をとりあえず二点ほど。

SilverlightにはDataTemplate.Datatypeが存在しない

地味に痛いですこの仕様。ItemsControlから派生したコントロール(ListBoxなど)のItemをItemsSourceにセットしたINotifyPoropertyChangedを実装したコレクションから生成する際に、コレクションが列挙するインスタンスの型によってDataTemplateを切り替える場合、WPFではDataTemplate.Datatypeがあるので重宝するのですが、Silverlightでは3 RTWでもサポートされていません。DataTemplateSelectorもサポートされていないので、インスタンスの型や状態によってDataTemplateを切り替えることはできないようです。とりあえず逃げ道として自分はDataContextにセットされたインスタンスをみてContentを書き換えるロジックを書いたUserControlを用意しているのですが、面倒ですね。XAMLで宣言的に書きたいのでDataTemplate.Datatypeは是非サポートしてもらいたいものです。

TabcotrolのItemsSourceはTabItemのコレクションしか受け付けない

なんでー!?これまたあんまりな仕様。WPFではItemsControlから派生したコントロールのItemsSourceにINotifyPoropertyChangedを実装したコレクションをセットすると、その列挙する内容をListBoxItemやTabItemなど適当なコンテナに包んで表示してくれるので、あとはDataTemplateで表示内容を好きなようにカスタマイズしてやればよかったのですが、SilverlightのTabControlのItemsSourceはTabItemのコレクションしか受けてつけてくれないという。。これではTabItemに対応するViewModelのインスタンスをセットし、DataTemplateでViewを設定して表示内容をカスタマイズするというM-V-VMでよく知られたパターンが使えません。SilverlightでもListBoxではListBoxItem以外も受け付けてくれたのですけどねぇ。。ではどうするか。自分の場合はViewModelのコレクションをラップしてTabItemのコレクションとして公開するラッパークラスとConverterを用意することで凌ぎました。

ラッパークラスはこんな感じです。ViewModelのコレクションとViewModelからTabItemへの変換を行うFuncをコンストラクタの引数として取ります。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows.Controls;

namespace SharpLab.CredentialsLockerSilverlight.ViewModels {
	public class ReadOnlyObservableTabItemCollection<TViewModel, TViewModelCollection> : ICollection<TabItem>, INotifyCollectionChanged where TViewModelCollection : ICollection<TViewModel>, INotifyCollectionChanged {

		private TViewModelCollection _viewModels;
		private ObservableCollection<TabItem> _tabItems;
		private Func<TViewModel, TabItem> _converter;

		public ReadOnlyObservableTabItemCollection(TViewModelCollection viewModels, Func<TViewModel, TabItem> converter) {
			_converter = converter;
			_viewModels = viewModels;
			_tabItems = new ObservableCollection<TabItem>();

			foreach (var item in _viewModels) {
				_tabItems.Add(_converter(item));
			}

			_viewModels.CollectionChanged += new NotifyCollectionChangedEventHandler(_viewModels_CollectionChanged);

			_tabItems.CollectionChanged += new NotifyCollectionChangedEventHandler(_tabItems_CollectionChanged);
		}

		void _viewModels_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
			switch (e.Action) {
				case NotifyCollectionChangedAction.Add:
					_tabItems.Insert(e.NewStartingIndex, _converter((TViewModel)e.NewItems[0]));
					break;
				case NotifyCollectionChangedAction.Remove:
					_tabItems.RemoveAt(e.OldStartingIndex);
					break;
				case NotifyCollectionChangedAction.Replace:
					_tabItems.RemoveAt(e.OldStartingIndex);
					_tabItems.Insert(e.NewStartingIndex, _converter((TViewModel)e.NewItems[0]));
					break;
				case NotifyCollectionChangedAction.Reset:
					_tabItems.Clear();
					foreach (var item in _viewModels) {
						_tabItems.Add(_converter(item));
					}
					break;
			}
		}

		void _tabItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
			if (CollectionChanged != null) {
				CollectionChanged(this, e);
			}
		}


		public event NotifyCollectionChangedEventHandler CollectionChanged;

		#region ICollection<TabItem> メンバ

		public void Add(TabItem item) {
			throw new NotSupportedException();
		}

		public void Clear() {
			throw new NotSupportedException();
		}

		public bool Contains(TabItem item) {
			throw new NotImplementedException();
		}

		public void CopyTo(TabItem[] array, int arrayIndex) {
			throw new NotSupportedException();
		}

		public int Count {
			get {
				throw new NotImplementedException();
			}
		}

		public bool IsReadOnly {
			get {
				return true;
			}
		}

		public bool Remove(TabItem item) {
			throw new NotSupportedException();
		}

		#endregion

		#region IEnumerable<TabItem> メンバ

		public IEnumerator<TabItem> GetEnumerator() {
			foreach (var item in _tabItems) {
				yield return item;
			}
		}

		#endregion

		#region IEnumerable メンバ

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
			return GetEnumerator();
		}

		#endregion
	}
}

これを以下のようなConverterから利用します。

using System;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using System.Windows.Data;
using SharpLab.CredentialsLockerSilverlight.ViewModels;

namespace SharpLab.CredentialsLockerSilverlight.Views.Converter {
	public class CardVMCol2TabItemColConverter : IValueConverter{
		#region IValueConverter メンバ

		public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
			return new ReadOnlyObservableTabItemCollection<CardViewModel, ObservableCollection<CardViewModel>>((ObservableCollection<CardViewModel>)value,
				(each) => {
					var newTabItem = new TabItem() {
						DataContext = each,
						Header = new CardTabHeaderView(),
						Content = new SharpLab.CredentialsLockerSilverlight.Views.CardView()
					};
					return newTabItem;

				});
		}

		public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
			throw new NotImplementedException();
		}

		#endregion
	}
}

こうしてやることでTabControlのItemsSourceにViewModelのコレクションをバインドできるようになります。Converterは無理を通すのに便利な機能ですね。。

 ItemsSource="{Binding Path=CardViewModels, Converter={StaticResource CardVMCol2TabItemColConverter}}"
JUL
13

Silverlight 3のRTWがリリースされましたね。Silverlight Tools for Visual Studio 2008やExpression BlendのRCもリリースされています。version3から本気出すというMSの本領発揮とばかりに、結構便利になってきました。このエントリでは、自分が弄ってみて気づいたSilverlight 3 Betaからの変更点を書いてみたいと思います。

Silverlight 3 SDK Betaに含まれていたコントロールの一部がSilverlight Toolkitに移動

DataForm, DockPanel, WrapPanel, Expander, HeaderedContentControl, ViewboxがToolkitに移動したようです。それに伴って所属する名前空間が変わっているので注意が必要です。

Interactivity.dllの名前空間の変更

Expressionでは、3 MixPreviewからBehaviorというパターンがサポートされました。3 MixPreviewではプロジェクトでMicrosoft.Expression.Interactivity.dllというアセンブリを参照した上で、Behavior<T>クラスなどを継承して自分でBehaviorを作成するか、Microsoft Expression Community Galleryなどからダウンロードして使うという流れでした。今回リリースされたExpression 3RCでは、Microsoft.Expression.Interactivity.dllがSystem.Windows.Interactivity.dllと改名され、名前空間も変更されているので注意が必要です。この改名はBlend SDKの絡みでしょうか。また、BehaviorやTriggerActionの組み込み実装として、StoryBoardActionなどがMicrosoft.Expression.Interactions.dll内で提供されていますので、Microsoft Expression Community Galleryと共に活用していくよいでしょう。

DataGridのItemsSourceへのBindingが上手くいかない問題が修正

Silverlight 3 Betaの頃はDataGridのItemsSourceへのBindingが上手く動かないという問題がありました(DataGrid — binding to ItemsSource is broken : The Official Microsoft Silverlight Site)。今は修正されています。(これはむしろSilverlight Toolkitの話?)

Silverlight Tools for VS2008のSilverlightデザイナ削除

Silverlight 3 Tools for VS2008ではSilverlightデザイナが削除されています。まぁプレビューが上手く出来ない場合も多かったので、VS2010に期待ですね。今のうちにBlend 3に習熟しておきたいものです。

JUL
11

CardSafe/E公開!

Published:2009-07-11 22:49:49 UTC

CardSafeEverywhere 

ここ一か月ほど、週末やらに暇を見つけては作っていたアプリケーションが完成しました。CardSafe/Eというタイトルで、いわゆるパスワードマネージャなのですが、特長としてはMesh enabled webアプリケーションとして実装していることが挙げられます。Mesh enabled webアプリケーションとは、Microsoftが開発中のLive Frameworkを利用したアプリケーションの一形態です。Mesh enabled webアプリケーションは、Live Meshという、MicrosoftによるDropBox的なデータ同期サービス上に配置され、複数デバイス・サーバー間でのアプリケーションとそのデータの同期が自動的に行われるという特長があり、開発に用いる技術がAjaxやSilverlightであることから、デスクトップアプリケーションとして動かすだけでなく、Webブラウザ上の通常のWebアプリケーションとしても動かすことが出来ます。CardSafe/Eはこれを利用し、データ同期機能を持ったパスワードマネージャを実現しています。

利用方法

残念なことに、このLive Frameworkという技術はまだCTP版という扱いであり、通常のLive Mesh Beta版ではMesh enabled webアプリケーションであるCardSafe/Eは利用できません。CardSafe/Eを利用するには、https://developer.mesh-ctp.com/という開発者用のLive Meshを利用する必要があります。開発者用Live Meshは、テスト用ということもあり、Mesh enabled webアプリが使えるほかは、Live Mesh Beta版に比べて機能がかなり省かれており、CardSafe/E共々常用には向かないかと思います。それでもLive Frameworkを触ってみたいという場合は、

  1. Microsoft ConnectのAzure Services Invitation ProgramからLive ServicesのInvitation Codeを取得
  2. そのInvitation Codeを利用してAzure Services Developer Portalに登録
  3. 開発者用Live Meshにサインイン
  4. 開発者用Live MeshのDeviceタブからLive Framework Clientをインストール
  5. MeshにCardSafe/Eをインストール←このURLを踏んでMeshにCardSafe/Eをインストール

という手順になります。開発者用Live Meshへの登録手順などは、使ってみよう! Live Framework:第3回 はじめようLive Framework CTP|gihyo.jp … 技術評論社という記事が非常に分かりやすいので是非参照して進めてください。

new CloudApp()

ところで。このアプリケーションはnew CloudApp()というMicrosoftのコンテストに出しています。気に入って頂けた場合は、new CloudApp(): The Azure™ Services Platform Developer Challenge – Voteという投票ページでCardSafe/Eにご投票頂けると励みになります。CardSafe/Eに清き一票を!(ぇ

CardSafe/Eの公開ページ

CardSafe/E – SharpLab.

追補エントリを書きました。こちらもあわせてご参照ください:
UnhandledExceptionイベントは、サブスレッドの例外も捕捉可能 – SharpLab.

 

先日C# .NETアプリケーション開発 徹底攻略という本を購入しました。C#での業務アプリケーション開発におけるノウハウをまとめた本で、プロファイリングの方法など、普段、趣味グラマをしていると考えない部分について色々と触れているので大変参考になる本なのですが、一点、マルチスレッド処理における例外処理についての記述でおかしなところがありました。

2章6節4項の、サブスレッドで発生した例外の捕捉の仕方についての部分です。まず、93~95ページで、集約例外ハンドラではサブスレッドで発生した例外が捕捉出来ない、という部分があります。このようなコードです。

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Threading;

namespace ThreadExceptionBehavior {
	static class Program {
		/// <summary>
		/// アプリケーションのメイン エントリ ポイントです。
		/// </summary>
		[STAThread]
		static void Main() {

			Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
			Thread.GetDomain().UnhandledException += new UnhandledExceptionEventHandler(Program_UnhandledException);

			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);
			Application.Run(new Form1());
		}

		static void Program_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
			Exception ex = e.ExceptionObject as Exception;
			MessageBox.Show(ex.Message);
		}

		static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) {
			MessageBox.Show(e.Exception.Message);
		}
	}
}

Form1.cs

using System;
using System.Windows.Forms;

namespace ThreadExceptionBehavior {
	public partial class Form1 : Form {
		public Form1() {
			InitializeComponent();
		}

		private delegate void SampleDelegate();

		private void button1_Click(object sender, EventArgs e) {
			//非同期スレッドの実行
			SampleDelegate a = new SampleDelegate(ThreadMethod);
			a.BeginInvoke(null, null);
		}

		private void ThreadMethod() {
			//意図的に起こした例外
			throw new Exception("非同期スレッドで起こった例外です。");
		}
	}
}

ここは良いのです。

そして続いての96~97ページで例外をメインスレッドへの通知して捕捉出来るように書き換える方法が書かれているのですが、このようになっています。

Form1.cs

using System;
using System.Windows.Forms;

namespace ThreadExceptionBehavior {
	public partial class Form1 : Form {
		public Form1() {
			InitializeComponent();
		}

		private delegate void SampleDelegate();

		private void button1_Click(object sender, EventArgs e) {
			//非同期スレッドの実行
			SampleDelegate a = new SampleDelegate(ThreadMethod);
			a.BeginInvoke(null, null);
		}

		private void ThreadMethod() {
			if (this.InvokeRequired) {
				//メインスレッドに呼び戻して実行
				this.BeginInvoke(new SampleDelegate(ThreadMethod));
				return;
			}
			throw new Exception("非同期スレッドで起こった例外です。");
		}
	}
}

19~23行目が加わった部分です。FormのInvokeRequiredプロパティがtrueの場合、つまりメインスレッド(UIスレッド)で実行されていない場合は、非同期にUIスレッドで実行しなおすようになっているのですが、これではサブスレッドでの例外処理の仕方の説明になっていません。ボタンをクリックした後直ぐに処理が帰ってくるという意味では非同期ではありますが、結局UIスレッド上で、ThreadMethodメソッドは動いているわけで、ThreadMethodメソッドに重たい処理を書いた場合は、UIがブロックされてしまうので無意味です。

本来ならば、以下のように書くべきではないでしょうか?

using System;
using System.Windows.Forms;

namespace ThreadExceptionBehavior {
	public partial class Form1 : Form {
		public Form1() {
			InitializeComponent();
		}

		private delegate void SampleDelegate();

		private void button1_Click(object sender, EventArgs e) {
			//非同期スレッドの実行
			SampleDelegate a = new SampleDelegate(ThreadMethod);
			a.BeginInvoke(null, null);
		}

		private void ThreadMethod() {
			try {
	
				//何らかの重たい処理

				throw new Exception("非同期スレッドで起こった例外です。");
			}
			catch(Exception e){
				this.BeginInvoke((MethodInvoker)delegate() {
					throw new ApplicationException("サブスレッドで例外が発生しました。", e);
				});
			}
		}
	}
}

これならば重たい処理によってUIがブロックされることもなく、またサブスレッドで発生した例外も例外集約ハンドラで集めることが可能です。

中身は

  • Windows 7 ライブラリを含む Windows Shell 名前空間オブジェクト、既知のフォルダーと非ファイルシステム コンテナ
  • Windows Vista と Windows 7 のタスクダイアログ
  • WPF と Windows Forms での Windows 7 Explorer Browser Control のサポート
  • シェル プロパティ システムのサポート
  • Windows 7 タスクバー ジャンプリスト、アイコン オーバーレイ、プログレスバー用のヘルパー
  • Windows Vista と Windows 7 コモン ファイル ダイアログのサポート(カスタム ファイル ダイアログを含む)
  • Direct3D 11.0とDXGI 1.0/1.1 APIのサポート
  • センサー プラットフォーム API
  • 拡張言語サービス API

川西 裕幸のブログ : Windows API Code Pack for Microsoft .NET Framework (v0.85)

だそうです。以下では同梱のサンプル(DirectX以外)を動かしてみた際のメモ。

Explorer Browser Control

image image

Explorerのコントロール。使い場所は色々ありそうですね。左がWPF版、右がWin Forms版。もちろんWPF版の主要なプロパティは依存関係プロパティになっていまして、Thumbnail Sizeというスライダーを動かせば、バインドされたサムネイルの大きさがスルスルと変わっていきました。良いですね。

Shell

KnownFoldersBrowser

image

既知のフォルダ?(knownFolders)の一覧を表示するデモ。一覧はMicrosoft.WindowsAPICodePack.Shell.KnownFoldersクラスで取得しています。knownFoldersはVistaで導入されたものらしい。また、standard foldersとは異なり、ベンダが追加(削除)することができるらしい。knownFoldersについて詳しくはMSDNのknownFoldersについてのページを参照のこと。

PropertyEdit

image

ファイルのプロパティを操作するデモ

ShellHierarchyTreeDemo

image 

階層ツリーのデモ

ShellObjectCFDBrowser

image image

CommonFileDialogのデモ。表示する場所の初期値として設定できるのはフォルダだけではないのが特徴。Windows 7で追加された「ライブラリ」を初期値として設定出来たり、検索条件を初期値として指定して開くことが出来ます。これはちょっと感動ものでした。二枚目のSSは検索条件として「最近使ったファイル」という条件を付加して開いたCommonFileDialog。

StarBackUp

image

いまいちよくわからない。フォルダ選択ダイアログのサンプル?

StockIconsDemo

システムのアイコンを取得するのに使えるMicrosoft.WindowsAPICodePack.StockIconsクラスのデモ

image

TaskBarDemo

imageimage  image

Windows7でのタスクバーでの通知を活用するためのデモ。タスクバー上でのプログレス表示や、ジャンプリストの作成のデモなど。

ThumbnailBrowserDemo

image

選択された要素のサムネイルをMicrosoft.WindowsAPICodePack.Shell.ShellObjectクラスのThumbnailプロパティから取得してPictureBoxに描画するサンプル。

センサー プラットフォーム API

サンプル動かそうとしたら落ちた。まぁ対応するセンサデバイスがないわけだから当然ですね。

拡張言語サービス API

多言語ユーザー インターフェイスのサポートと言語サービス

Windows 7 は、多言語ユーザー インターフェイスのサポートを強化し、アプリケーションから言語サービスを利用できるようにすることで、開発者がスタンダードな手法で国際市場に向けたアプリケーションを作成できるようになっています。

Extended Linguistic Services は、小規模の同じAPI 群を使用して、さまざまな先進の言語機能を有効活用できる、Windows 7 の新機能です。開発者は Extended Linguistic Services の API を使用することにより、任意の Unicode テキストから言語を自動検出し、その情報を元にして適切な言語が選択されるような、世界中のユーザーが快適に利用できるしくみを構築できます。 Extended Linguistic Services には、テキストの書記体系を変換する、組み込みの表記変換機能もサポートされています。たとえば、簡体字中国語と繁体字中国語間でテキストを自動的に変換すれば、言語の境界を越えた相互コミュニケーションを促進することが可能です。 Extended Linguistic Services の API を使用することにより、開発者は、既存の Extended Linguistic Services に加え、将来、新しいサービスが公開されたときには、新たにコードを覚え直すことなく、必要なサービスを選ぶだけで済むようになります。

Windows 7 開発者ガイド

この機能を利用するためのものみたい。あんまり弄れてないです。

MAY
21

VS2010でのスマートデバイス開発

Published:2009-05-21 13:47:16 UTC

image

Templateのツリーにスマートデバイスがないのですが、はて、これはどういうことだろう…?

image Visual Studioのアドインは、Managed Extension Frameworkベースになるという話なので、VS2008以前のアドインはどうなるのかと気になっていたのですが、一応サポートされているようです。自作のアドインの構成ファイルをVS2010用に書き換えて試してみたところ、 確かに読み込まれるのが確認できました(まぁiKnowの仕様変更によって今は検索は動作しなくなってしまっているのですけれども)。一応いい感じに読み込まれているように見えるのですけれども、ツールバー周りが多少違和感のあるようになってしまっていますね。まぁそのうち直したものを作れればと思います。

MAY
21

Visual Studio 2010 Beta1

Published:2009-05-21 12:58:22 UTC

image

きましたね、Visual Studio 2010 Beta1。乗るしかないでしょう!このビックウェーブに!ということで、一応インストールしてみました。まだよくわかっちゃいないのですが、やはりWPFベースで再構築されたというUIが目を引きます。これからぼちぼち弄っていければと思います。

MAY
3

Southridge Hands-on-Labが公開されています

Published:2009-05-03 12:26:56 UTC
WPF

image WPF Futuresには、WPF Model-View-ViewModel Toolkitの他にも色々な参考になるサンプルが含まれています。Southridge Hands-on-LabWPF Ribbon ControlとDataGridを使ったリッチなアプリケーション開発のチュートリアルとなっており、PDCのセッションでも出てきていたものがチュートリアルの形式としてまとめられた物のようですね。結構大きなアプリケーションですが、手取り足取り細かく解説してくれるので、参考になるところが多いのではないでしょうか。

ただ、M-V-VMパターンには則っていないところがあり、ViewModelに書くべきロジックをViewのコードビハインドに書いていたりするのでM-V-VMパターンのサンプルとするには注意が必要です。WPF Ribbon Controlのメニュー等は、CommandとしてExecuteメソッドやCanExecuteメソッド以外にも、LabelTitleプロパティなどを持ったオブジェクトを必要とするため、View側でCommandとしてセットするオブジェクトを用意する必要があります。その制約に引きずられてViewのコードビハインドにCommandの処理ロジックを記述しているようです。

では、Ribbon Controlを使用する場合はM-V-VMパターンが使えないかというとそういう訳でもなく、SouthridgeをM-V-VMパターンに則って書き直したバージョンも存在しているようです。自分は詳しく調べられていないのですが、先ほどの問題はNonRoutedRibbonCommandDelegatorというRibbonCommandと同じインターフェイスを備えたプロキシクラスをはさむことで解決しているようです。

・・・とここまで書いたところで気づいたのですが、Ribbon V1 Roadmapをみたところ、Ribbon関係のCommandに設計変更が入るみたいですね。LabelTitleとかまでRibbonCommandに纏めるのをやめて、通常のコントロールと同じようなスタイルに戻るようです。これは素直に新しいバージョンがリリースされるのを待ったほうが楽かもしれません。