Archive : 2009-03

image 買っちゃいましたShuttleのベアボーン。26日に新丸ビルで未明4時から研究会の手伝いで作業していたのですが、昼間少しあった空き時間に秋葉原まで足を延ばした折、T-ZONEでShuttle XPCフェアというのをやっているのを見つけ、店員に色々聞いているうちに買ってしまっていました(笑)。hiromasaさんがSN78SH7?を買われているのを見て以来、ShuttleのCubeが欲しい欲しい!と思っていて、最近色々テストのためにVMを快適に動かせるPCが欲しくなってきていた勢いでつい…。寝不足の頭は危険ですねぇ。

構成は以下の通り。

  • ケース:XPC SG45H7
  • CPU:Core 2 Quad Q9400
  • メモリ:DDR 800 2GB*2
  • HDD:500GB(Seagate Barracuda 7200.12)
  • ドライブ:DVD Multiドライブ

これをBenQのモニタに繋ぎ、とりあえず適当にUSBマウスとキーボードをつけて使っています。え?OSがない?それはほらあれです、Windows 7 Beta1(笑)。あとUbuntu。これで7万円後半。良い時代です。

 

…がトラブルが一つ。接続性がない!買ったときは全く考えていなかったのですが、台所にあるブロードバンドルータへの経路が確保できていないという…。今使っているラップトップは無線LAN機能を内蔵していたので良かったのですが、今度のは当然自分で用意する必要があるわけで…。これまで他のPCで使っていたPCカード型の無線LAN子機は使えず、頼みの綱のUSB型無線LAN子機は64bitOS未対応…。とりあえず無線イーサネットコンバータかPLCでも買ってきます…。

MAR
28

ベルギービールが旨すぎる

Published:2009-03-28 01:22:09 UTC

image おととい、丸の内のTOKIAのBelgian Beer Cafeで研究室の先生に奢ってもらったのですが、ヒューガルデンホワイトって旨いですねぇ。どうも自分はあまりビールは好きになれなくて、最初に一杯飲んで後は別のお酒を頼むことが多いですが、このビールは日本のビールにはないスッキリとした味で、舌触りがよくてとても飲みやすく、少し感動すら覚えました。こんなにお酒って美味しいものだったのかと。

そのあとレフブラウンなども飲みましたが、ベルギーのビールってどれも美味しいですね。泡まで美味しいと感じたのは初めてです。ベルビュークリークも飲みました。クリークとはフラマン語でスミミザクラのサクランボらしく、これはビールというよりは果実酒ですね。とても甘くて、ちょっとすっぱくて、やっぱり美味しかったです。柄にもなく久しぶりにお酒を買って家で飲みたくなってきました。

MAR
25

バイブルみたいな何か

Published:2009-03-25 22:45:38 UTC

あす未明からの研究室の手伝いで日本橋のAPAホテルに今泊まっているのだけど…。

ベッド脇のデスクの上で見つけた。
IMAGE_179IMAGE_180 IMAGE_181 

置くか普通?

内容の是非以前に、あれだけ騒がれてなお全室に配るAPAホテルの経営者のワンマンぶりに驚いた。大したもんだなぁ。

MAR
24

WindowsMobieでの開発ねぇ…。

Published:2009-03-24 01:33:10 UTC

「Windows Mobile のアプリケーションプラットフォームは古すぎる」

という点である。

確かにOSのバージョンもFramework のバージョンもあがっているが、できることはほぼまったく変わっていない。比較的作りやすくなっただけでできることは変わっていない。特にUI周りは壊滅的である。

高橋 忍のブログ : 古すぎる Windows Mobile アプリケーション環境

ですよねぇ。この前のPDCでのSilverlight2 for Mobileのセッションでの発表で、ついにWMでも高機能なUIコントロールが利用できるようになるのか!と心踊らされたものの、Mixでも新しい発表がなく、前に発表されたロードマップはまったくあてになりそうにない。救いとしては、最近の流行のタッチ操作に最適化されたコントロールとしてFluid – Windows Mobile .NET Touch Controls – HomeというのがCodePlexにあがってはいることだけれど、本来はコントロール位標準で用意されていないと手間がかかって仕方がない。Fluidにはデザイナのサポート、整備されたドキュメントは当然期待できないし、実際試してみたところどうも日本語入力が出来ないようでもある。なまじWPFの自由なUIデザインの世界を知っているだけに、なんでWindowsMobileは…と思わずにはいられない。(ついでに言うとVS2010でのスマートデバイス開発がどうなるかも結構気になっている。C#4.0やら出てくるわけだけど、Compact Frameworkはどう進むのか。VS2010の次のCTPだかβリリースだかで情報が出てこないかなぁとちょっと期待しているとこ。)

しかしまぁ何にせよ時間がかかりそうだなぁ…。最近じゃSilverlight(1) for MobileのリリースとWindows Mobile 6.5の発表位で、WM関係では小粒なニュースばかりだから、(まさか携帯端末市場を捨てた訳でなければ)そろそろ大きな花火を打ち上げて欲しいんですけどねぇ…。そうでないと、それこそWM上でリッチなアプリケーションを手軽に作る方法が、.net CFではなく、FennecのアドインとしてXUL+JavaScriptで何か作る、という話になりかねない。

hiromasaさんが開発されたWordPress Related Post for Japanese 1.50、このサイトでも導入してみました。WordPress Related Post for Japaneseは、Yahoo! Japanの形態素解析APIを利用して関連エントリを取得・表示するプラグインです。実はver1.1のころから導入はしていて、裏で走らせてはいたのですが、そのころはBlogのエントリ数自体がまだ少なく、関連エントリを表示しようにも表示するエントリがないため精度が微妙だったので、表示するためのタグは張らずに遊んでいる状態でした。

でも今なら大丈夫!しっかり関連エントリが表示されています!

image

原理上短いエントリの場合、どうしても精度が出ない場合もありますが、それは努めて長文エントリ書くようにする、という運用でカバーしていければと思いますw

image以前公開したiKnow ItemBankPanel for VisualStudioをM-V-VMパターンを使って書き直しています。まだ途中ですが、一応辞書を引いて結果を表示するとこまでは出来たので、M-V-VMパターンの参考までに公開してみます。WPF自体よく分かっていなかった前のコードと比べると見違えるような綺麗さになりましたw

 

SharpLab.IKnow.ItemBankPanes.zip

 

添付ビヘイビアでTextBoxにCommandを実装してみた – SharpLab.の添付ビヘイビアは上の検索ツールバーで、添付ビヘイビア試してみた – SharpLab.で紹介した添付ビヘイビアは検索結果を表示しているListBoxで実際に使用しています。

M-V-VMパターンの威力

埋め草までに、ViewのXAMLコードを貼っておきます。このアプリではUIロジックに複雑なところがなかったので、実際、コードビハインドにイベントハンドラを書かずに済みました。ボタンクリックやTextBoxでのEnterキー押下時の処理は、全てCommandを使ってViewModelに移譲しています。

<UserControl x:Class="SharpLab.IKnow.ItemBankPane.ItemBankPaneView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:SharpLab.IKnow.ItemBankPane"
	>
    <UserControl.Resources>
    	<ResourceDictionary>
			<local:ItemBankPaneViewModel x:Key="viewModel" />
		</ResourceDictionary>
	</UserControl.Resources>
	<UserControl.DataContext>
		<Binding Mode="OneTime" Source="{StaticResource viewModel}" />
	</UserControl.DataContext>
	<DockPanel>
		<ToolBar DockPanel.Dock="Top" Height="30" VerticalAlignment="Top">
			<TextBox local:TextBoxBehavior.Command="{Binding Mode=OneTime, Path=StartSearchCommand}"  HorizontalAlignment="Left" Text="{Binding Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Path=Keyword}" VerticalAlignment="Top" Width="184" />
			<Button Command="{Binding Mode=OneTime, Path=StartSearchCommand}" Content="Search"></Button>
		</ToolBar>
		<ContentControl>
				<local:ResultPageView DataContext="{Binding Mode=OneTime, Path=ResultPage}" />
		</ContentControl>
	</DockPanel>
</UserControl>
<UserControl x:Class="SharpLab.IKnow.ItemBankPane.ResultPageView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:local="clr-namespace:SharpLab.IKnow.ItemBankPane"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="300"
    >
	<UserControl.Resources>
		<ResourceDictionary>
			<ResourceDictionary.MergedDictionaries>
				<ResourceDictionary Source="Resources/ResultPageResources.xaml"/>
				<ResourceDictionary Source="Resources/VocabularyViewerStyle.xaml"/>
			</ResourceDictionary.MergedDictionaries>
		</ResourceDictionary>
	</UserControl.Resources>
	<Grid DockPanel.Dock="Bottom">
		<Grid.RowDefinitions>
			<RowDefinition />
			<RowDefinition Height="35" />
		</Grid.RowDefinitions>
		<Grid.ColumnDefinitions>
			<ColumnDefinition />
			<ColumnDefinition Width="40" />
			<ColumnDefinition />
		</Grid.ColumnDefinitions>
		<ListBox Name="VocabularyViewer" Grid.Row="0" Grid.ColumnSpan="3" ItemsSource="{Binding Mode=OneWay, NotifyOnTargetUpdated=true, Path=Items}" Style="{StaticResource VocabularyViewerStyle}" />
		<Button Grid.Row="1"  Grid.Column="0" Margin="6,6,6,6" Command="{Binding Mode=OneTime, Path=NavigatePrevPageCommand}" Content="Prev"/>
		<Button Grid.Row="1" Grid.Column="2" Margin="6,6,6,6" Command="{Binding Mode=OneTime, Path=NavigateNextPageCommand}" Content="Next"/>
		<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Mode=OneWay, Path=PageCounter}" HorizontalAlignment="Center" VerticalAlignment="Center" />
	</Grid>
</UserControl>
<UserControl x:Class="SharpLab.IKnow.ItemBankPane.ItemView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:SharpLab.IKnow.ItemBankPane"
	>

	<UserControl.Resources>
		<ResourceDictionary>
			<ResourceDictionary.MergedDictionaries>
				<ResourceDictionary Source="Resources/PlusButton.xaml"/>
				<ResourceDictionary Source="Resources/PlayButton.xaml"/>
				<ResourceDictionary Source="Resources/SentenceListViewerStyle.xaml"/>
			</ResourceDictionary.MergedDictionaries>
		</ResourceDictionary>
	</UserControl.Resources>

	<DockPanel>
		<Expander x:Name="Sentences" DockPanel.Dock="Bottom" TabIndex="3" Visibility="{Binding Mode=OneTime, Path=SentencesVisility}" >
			<Expander.Header>
				<TextBlock Text="Example Sentences:" />
			</Expander.Header>
			<ItemsControl Name="SentenceListViewer" ItemsSource="{Binding Mode=OneTime, Path=Sentences}" Style="{StaticResource SentenceListViewerStyle}"  />
		</Expander>
		<!--DropDown="{DynamicResource VocabularyViewerItemContextMenu}"-->
		<local:DropDownButton DockPanel.Dock="Right" Width="24" Height="24" Style="{StaticResource PlusButtonStyle}"  TabIndex="1" Margin="0,0,8,0" />
		<DockPanel DockPanel.Dock="Top" LastChildFill="true">
			<MediaElement x:Name="mp3Player" LoadedBehavior="Manual" Source="{Binding Mode=OneTime, Path=CueSound}" Width="0"  Height="0"  />
			<Button DockPanel.Dock="Left" Width="18" Height="18" Margin="2,0,4,0" TabIndex="0" Style="{StaticResource PlayButtonStyle}" Command="{Binding Mode=OneTime, Path=PlaySoundCommand}" CommandParameter="{Binding ElementName=mp3Player}" />
			<ContentControl Content="{Binding Mode=OneTime, Path=CueText}" IsTabStop="False" />
		</DockPanel>
		<TextBlock DockPanel.Dock="Top" FontSize="12" Margin="20,0,0,0" Text="{Binding Mode=OneTime, Path=Meaning}" TextWrapping="Wrap" />
	</DockPanel>
</UserControl>
<UserControl x:Class="SharpLab.IKnow.ItemBankPane.SentenceView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:local="clr-namespace:SharpLab.IKnow.ItemBankPane"
	>
	<UserControl.Resources>
		<local:StringToTextBlockConverter x:Key="StringToTextBlockConverter" />
	</UserControl.Resources>

	<UserControl.Content>
		<Binding Path="Text" Mode="OneTime"  Converter="{StaticResource StringToTextBlockConverter}" />
	</UserControl.Content>
</UserControl>
MAR
14

TextBox内でEnterキーが押下された時に、割り当てたCommandが実行されたりすると便利ですよね。ちょっと必要になったので添付ビヘイビアを利用して実装してみました。ちなみにKeyBindingも試しましたが、あれCommandプロパティが依存関係プロパティになっていないのでバインドが出来ないんですね…。不便。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows.Controls;
using System.Windows;

namespace SharpLab.IKnow.ItemBankPane {
	public class TextBoxBehavior {

		#region Command

		public static ICommand GetCommand(TextBox textBox) {
			return (ICommand)textBox.GetValue(CommandProperty);
		}

		public static void SetCommand(
		  TextBox textBox, bool value) {
			textBox.SetValue(CommandProperty, value);
		}

		public static readonly DependencyProperty CommandProperty =
			DependencyProperty.RegisterAttached(
			"Command",
			typeof(ICommand),
			typeof(TextBoxBehavior),
			new UIPropertyMetadata(null, OnCommandPropertyChanged));

		static void OnCommandPropertyChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) {
			TextBox textBox = depObj as TextBox;
			if (textBox == null)
				return;

			if (e.NewValue is ICommand == false)
				return;

			ICommand command = (ICommand)e.NewValue;

			textBox.KeyDown += new KeyEventHandler(OnTextBoxKeyDown);
		}

		static void OnTextBoxKeyDown(object sender, KeyEventArgs e) {
			TextBox textBox = (TextBox)e.OriginalSource;
			ICommand command = TextBoxBehavior.GetCommand(textBox);
			if (e.Key == Key.Enter && command != null && command.CanExecute(TextBoxBehavior.GetCommandParameter(textBox))) {
				command.Execute(TextBoxBehavior.GetCommandParameter(textBox));
			}
		}

		#endregion

		#region CommandParameter

		public static object GetCommandParameter(TextBox textBox) {
			return textBox.GetValue(CommandParameterProperty);
		}

		public static void SetCommandParameter(
		  TextBox textBox, object value) {
			textBox.SetValue(CommandParameterProperty, value);
		}

		public static readonly DependencyProperty CommandParameterProperty =
			DependencyProperty.RegisterAttached(
			"CommandParameter",
			typeof(object),
			typeof(TextBoxBehavior),
			new UIPropertyMetadata(null));
		
		#endregion
	}
}

利用する側はこんな感じ。スマート!

<TextBox v:TextBoxBehavior.Command="{Binding Path=StartSearchCommand}" />
MAR
14

添付ビヘイビア試してみた

Published:2009-03-14 00:55:12 UTC
WPF

ひとつ前のエントリJosh Smithさんの添付プロパティの解説記事を訳したのですが、実際に自分でもコードを書いて試してみようということで、少し変えて、添付されたListBoxのItemsSourceが更新された時にListBoxのスクロール位置を自動的にトップに巻き戻す添付ビヘイビアを作成してみました。ItemsSourceが更新されてもスクロール位置が変わらないというListBoxの挙動は、一つづつ項目の追加削除をする場合は便利なのですが、丸ごと全部項目を入れ替える場合、例えば非常に多くの項目をページに分割して表示しているときにページを行き来する時などには、ページを移った先でスクロール位置が保持されているのはかえって困ることになります。
で、書いてみたのはこんな感じ。

using System.Windows;
using System.Windows.Controls;

namespace SharpLab.IKnow.ItemBankPane {
	public class ListBoxBehavior {

		//添付されたListBoxのItemsSourceが更新された時にListBoxのスクロール位置を自動的に巻き戻すかを設定します。
		#region AutoRewind

		public static bool GetAutoRewind(ListBox listBox) {
			return (bool)listBox.GetValue(AutoRewindProperty);
		}

		public static void SetAutoRewind(
		  ListBox listBox, bool value) {
			listBox.SetValue(AutoRewindProperty, value);
		}

		public static readonly DependencyProperty AutoRewindProperty =
			DependencyProperty.RegisterAttached(
			"AutoRewind",
			typeof(bool),
			typeof(ListBoxBehavior),
			new UIPropertyMetadata(false, OnAutoRewindChanged));

		static void OnAutoRewindChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) {
			ListBox listBox = depObj as ListBox;
			if (listBox == null)
				return;

			if (e.NewValue is bool == false)
				return;

			bool scrollToTop = (bool)e.NewValue;

			if (scrollToTop) {
				listBox.TargetUpdated += OnListBoxTargetUpdated;
			}
			else {
				listBox.TargetUpdated -= OnListBoxTargetUpdated;
			}
		}

		static void OnListBoxTargetUpdated(object sender, System.Windows.Data.DataTransferEventArgs e) {
			if (e.Property == ListBox.ItemsSourceProperty) {
				ListBox listBox = e.OriginalSource as ListBox;
				if (listBox.Items.Count > 0) {
					listBox.ScrollIntoView(listBox.Items[0]);
				}
			}
		}

		#endregion
	}
}

使う時はこんな感じ。

<ListBox v:ListBoxBehavior.AutoRewind="true" ItemsSource="{Binding NotifyOnTargetUpdated=true, Path=<適当に>}"/>

添付プロパティの値の変更時のイベントハンドラ内で、添付された要素のインスタンスのイベントを購読するのがキモで、後はどのように要素のふるまいを変えるかはお好み次第、といったとこでしょうか。同じ処理をコードビハインド内にイベントハンドラとして書く場合と異なり、即再利用可能ですから便利ですね。

 

ところでこれを書いているときに少しはまりかけたのが、FrameworkElement.SourceUpdated イベント (System.Windows)FrameworkElement.TargetUpdated イベント (System.Windows)の使い分け。バインディングソースが変更された結果、ListBoxが更新されるのを監視したいのだから、SourceUpdatedイベントを購読すればいいのかと思ったらTargetUpdatedイベントだったという罠。Binding.SourceUpdated アタッチされるイベント (System.Windows.Data)Binding.TargetUpdated アタッチされるイベント (System.Windows.Data)のエイリアスだというから、データバインディングの結果、どちらが影響を受けたかで決まっているのでしょうか??よくわかりません。

このエントリは、M-V-VMパターンについてのエントリを訳してみる試みの四本目です。今回は添付ビヘイビアというテクニックを解説した、CodeProject: Introduction to Attached Behaviors in WPFという記事を訳してみました。著者はMSDN MagazineのWPF のための MODEL-VIEW-VIEWMODEL (MVVM) デザイン パターンという記事も書かれたJosh Smithさん。Crack.NETの作者の方でもあります。

-訳ここから-

AttachedBehavior.jpg

導入 Introduction

この記事は添付ビヘイビアが何であるか、WPFアプリケーションでどう実装できるのかを解説します。この記事の読者はWPF、XAML、添付プロパティそしてModel-View-ViewModel(MVVM)パターンに多少親しんでいる必要があります。私の‘Simplifying the WPF TreeView by Using the ViewModel Pattern’という記事も読まれることを強くお勧めします。なぜなら、ここでの題材はその記事での題材の延長だからです。

This article explains what an attached behavior is, and how you can implement them in a WPF application. Readers of this article should be somewhat familiar with WPF, XAML, attached properties, and the Model-View-ViewModel (MVVM) pattern. I highly recommend that you also read my ‘Simplifying the WPF TreeView by Using the ViewModel Pattern’ article, because the material here is an extension of the material presented in it.

背景 Background

2008年の五月頃、私は‘Simplifying the WPF TreeView by Using the ViewModel Pattern’という記事を公開しました。その記事はMVVMパターンにフォーカスしていました。今朝、私は起きるとPascal Binggeliという方が記事のコメント欄に素晴らしい質問をされているのに気づきました。

Back in May of 2008, I published an article called ‘Simplifying the WPF TreeView by Using the ViewModel Pattern’. That article focused on the MVVM pattern. This morning I woke up to find that a fellow by the name of Pascal Binggeli had asked an excellent question on that article’s message board.

Pascalは、TreeViewと連携するViewModelオブジェクトがTreeViewItemを選択したときにどうやってそれをTreeViewコントロールの表示範囲にスクロールさせればよいかを知りたがっていました。この問いは十分にシンプルに見えますが、検討を重ねると、最初に想像していた程容易ではないのがわかるでしょう。目標、そして問題は、MVVMパターンの原則が破られないよう、選択されたTreeViewItemBringIntoView()メソッドを呼ぶコードを配置する適切な場所を見つけることです。

Pascal wanted to know how to scroll a TreeViewItem into the viewable area of the TreeView control when its associated ViewModel object selects it. That seems simple enough, but upon further examination, it is not quite as straightforward as one might initially expect. The objective, and problem, is to find the proper place to put code that calls BringIntoView() on the selected TreeViewItem, such that the principles of the MVVM pattern are not violated.

例えば、ユーザーが、TreeView内の、ユーザーが決定した検索文字列にマッチするテキストを表示する項目を探す場合を考えてみてください。検索ロジックがマッチする項目を見つけた場合、マッチするViewModelオブジェクトはそのIsSelectedプロパティをtrueに設定します。すると、データバインディングの魔術によってそのViewModelオブジェクトと連携するTreeViewItemは選択状態になります(即ち、そのIsSelectedプロパティもtrueにセットされます)。しかしながら、そのTreeViewItemは必ずしも表示範囲内に来るという訳ではありません。これは、ユーザーは検索文字列に一致する項目を見ることが出来ないということを意味します。PascalはViewModelがそれが選択状態であるかを決定するときにTreeViewItemが見える範囲に来ることを望んでいました。

For example, suppose that the user searches through a TreeView for an item whose display text matches a user-defined search string. When the search logic finds a matching item, the matching ViewModel object will have its IsSelected property set to true. Then, via the magic of data binding, the TreeViewItem associated with that ViewModel object enters into the selected state (i.e. its IsSelected property is set to true, too). However, that TreeViewItem will not necessarily be in view, which means the user will not see the item that matches their search string. Pascal wanted a TreeViewItem brought into view when the ViewModel determines that it is in the selected state.

ViewModelオブジェクトはTreeViewItemが存在するか、そしてそれにバインドされいるかについて感知しません。そのため、ViewModelオブジェクトにTreeViewItemを見える範囲に持ってこさせるのを望むのは筋違いです。この問題は今や、ViewModelがそれを選択状態においた時に、誰がTreeViewItemを見える範囲に持ってくる責任を負うのかという問題になりました。

The ViewModel objects have no idea that a TreeViewItem exists and is bound to them, so it does not make sense to expect the ViewModel objects to bring TreeViewItems into view. The question becomes, now, who is responsible for bringing a TreeViewItem into view when the ViewModel forces it to be selected?

ViewModelオブジェクトと視覚要素を連結するのは不自然さをもたらし、また、余計ですので、私達は確かにそのコードをViewModelに入れたくはありません。私達はそのコードをViewModelにバインドされたTreeViewのコードビハインドのあらゆる場所に入れたくありません。なぜなら、そうすることは、我々が初めにViewModelを使うことによって避けた問題のいくつかを再び引き起こすからです。私達は選択された時に自分自身を見える範囲に持っていくビルトインサポートを持ったTreeViewItemサブクラスを作成することは出来ますが、WPFの世界において、それは疑いようもなく、簡単な問題に対する重量級の解決策です。

We certainly do not want to put that code into the ViewModel because it introduces an artificial, and unnecessary, coupling between a ViewModel object and a visual element. We do not want to put that code in the code-behind of every place a TreeView is bound to a ViewModel, because it reintroduces some of the problems that we avoid by using a ViewModel in the first place. We could create a TreeViewItem subclass that has built-in support for bringing itself into view when selected, but, in the WPF world, that is definitely a heavy-handed solution to a lightweight problem.

私達は、この問題を簡単で再利用可能なやり方でどのようにエレガントに解決することが出来るでしょうか?

How can we elegantly solve this problem in a lightweight and reusable way?

添付ビヘイビア Attached Behaviors

今まで説明した問題の解決策は添付ビヘイビアを使うことです。オブジェクトに対する添付ビヘイビアはオブジェクトに何かそれ自身ではしようとしないことをさせることを単に意味しています。ここに私が‘Working with CheckBoxes in the WPF TreeView’という記事で書いた添付ビヘイビアの説明があります。

添付プロパティを公開するクラスから要素へのアクセスを得ることが出来るように、添付プロパティを要素にセットするというアイデアです。クラスが要素へのアクセスを得さえすれば、クラスは要素のイベントをフックすることができ、それらのイベント発生への反応において要素に普段はしないことをさせることが出来ます。それはサブクラスを作成して使うのに代わる非常に便利な選択肢であり、また非常にXAMLフレンドリでもあります。

あの記事では、デモアプリケーションは添付ビヘイビアを複雑な方法で使っていますが、この記事では、私達は添付ビヘイビアの使い方をシンプルに保ちました。背景と理論の話はこのぐらいにして、我らが友、Pascalが提示した問題を解決する添付ビヘイビアの作り方をみていきましょう。

The solution to the problem explained above is to use an attached behavior. Attaching a behavior to an object simply means making the object do something that it would not do on its own. Here is the explanation of attached behaviors that I wrote in my ‘Working with CheckBoxes in the WPF TreeView’ article:

The idea is that you set an attached property on an element so that you can gain access to the element from the class that exposes the attached property. Once that class has access to the element, it can hook events on it and, in response to those events firing, make the element do things that it normally would not do. It is a very convenient alternative to creating and using subclasses, and is very XAML-friendly.

In that article, the demo application uses attached behaviors in complicated ways, but in this article, we will keep it simple. Enough with the background and theory, let’s see how to create an attached behavior that solves the problem posed by our friend Pascal.

デモンストレーション Demonstration

この記事のデモアプリは、このページの頭でダウンロード可能ですが、‘Simplifying the WPF TreeView by Using the ViewModel Pattern’の記事で使用した検索デモを使っています。私はTreeViewにより多くの項目を追加したり、フォントサイズを大きくしたり、添付ビヘイビアを加えるなどの幾つかの変更をしました。添付ビヘイビアはTreeViewItemBehaviorという新しい静的クラス内に存在します。そのクラスは、TreeViewItemにセット出来るIsBroughtIntoViewWhenSelectedというBoolean型の添付ビヘイビアを公開します。これがTreeViewItemBehaviorクラスです:

This article’s demo app, which is available for download at the top of this page, uses the Text Search demo provided by the ‘Simplifying the WPF TreeView by Using the ViewModel Pattern’ article. I made a few changes, such as adding more items to the TreeView, increasing the font size, and adding an attached behavior. The attached behavior is in a new static class called TreeViewItemBehavior. That class exposes a Boolean attached property that can be set on a TreeViewItem, called IsBroughtIntoViewWhenSelected. Here is the TreeViewItemBehavior class:

 /// <summary>
/// Exposes attached behaviors that can be
/// applied to TreeViewItem objects.
/// </summary>
public static class TreeViewItemBehavior
{
    #region IsBroughtIntoViewWhenSelected

    public static bool GetIsBroughtIntoViewWhenSelected(TreeViewItem treeViewItem)
    {
        return (bool)treeViewItem.GetValue(IsBroughtIntoViewWhenSelectedProperty);
    }

    public static void SetIsBroughtIntoViewWhenSelected(
      TreeViewItem treeViewItem, bool value)
    {
        treeViewItem.SetValue(IsBroughtIntoViewWhenSelectedProperty, value);
    }

    public static readonly DependencyProperty IsBroughtIntoViewWhenSelectedProperty =
        DependencyProperty.RegisterAttached(
        "IsBroughtIntoViewWhenSelected",
        typeof(bool),
        typeof(TreeViewItemBehavior),
        new UIPropertyMetadata(false, OnIsBroughtIntoViewWhenSelectedChanged));

    static void OnIsBroughtIntoViewWhenSelectedChanged(
      DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        TreeViewItem item = depObj as TreeViewItem;
        if (item == null)
            return;

        if (e.NewValue is bool == false)
            return;

        if ((bool)e.NewValue)
            item.Selected += OnTreeViewItemSelected;
        else
            item.Selected -= OnTreeViewItemSelected;
    }

    static void OnTreeViewItemSelected(object sender, RoutedEventArgs e)
    {
        // Only react to the Selected event raised by the TreeViewItem
        // whose IsSelected property was modified.  Ignore all ancestors
        // who are merely reporting that a descendant's Selected fired.
        if (!Object.ReferenceEquals(sender, e.OriginalSource))
            return;

        TreeViewItem item = e.OriginalSource as TreeViewItem;
        if (item != null)
            item.BringIntoView();
    }

    #endregion // IsBroughtIntoViewWhenSelected
}

上の添付ビヘイビアは基本的にTreeViewItemSelectedプロパティをフックし、イベントが発生した時に項目のBringIntoView()を呼ぶ手の込んだ方法です。このパズルの最後のピースはTreeViewItemBehaviorクラスがTreeViewの各TreeViewItemへの参照を得る方法です。我々は以下のようにSetterTreeView内のTreeViewItemの各項目に適用されたStyleに加えることで実現しました:

The attached behavior seen above is basically just a fancy way of hooking the Selected property of a TreeViewItem and, when the event is raised, calling BringIntoView() on the item. The final piece of this puzzle is seeing how the TreeViewItemBehavior class gets a reference to every TreeViewItem in the TreeView. We accomplish that by adding a Setter to the Style applied to every item in the TreeView, as seen below:

 <TreeView.ItemContainerStyle>
  <Style TargetType="{x:Type TreeViewItem}">
    <!--
    This Setter applies an attached behavior to all TreeViewItems.
    -->
    <Setter 
      Property="local:TreeViewItemBehavior.IsBroughtIntoViewWhenSelected" 
      Value="True" 
      />

    <!-- 
    These Setters bind a TreeViewItem to a PersonViewModel.
    -->
    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
    <Setter Property="FontWeight" Value="Normal" />
    <Style.Triggers>
      <Trigger Property="IsSelected" Value="True">
        <Setter Property="FontWeight" Value="Bold" />
      </Trigger>
    </Style.Triggers>
  </Style>
</TreeView.ItemContainerStyle>

デモアプリケーションが読み込まれると、検索語は自動的に「Y」という文字にセットされます。Findボタンを何度か押すと、そのたびに項目が選択されるのがわかるでしょう。その項目は「Y」を含んでおり、見える範囲にスクロールされます。選択された時に項目が見える範囲にスクロールされるということは添付ビヘイビアがうまく働いているということです。

When the demo application loads up, the search text will be set to the letter Y automatically. Click the Find button a few times, and you will see that each time an item is selected, it will contain the letter Y and will scroll into view. The fact that it scrolls into view upon being selected means that the attached behavior is working properly.

結論 Conclusion

オブジェクトのイベントをフックし、発生したときに何かをするのは、どれだけ想像力を働かせても、確かに飛躍的なイノベーションではありません。その意味で、添付ビヘイビアは同じことをするありきたりの方法に過ぎません。しかしながら、このテクニックの重要性は名前を持ったことであり、これはデザインパターンのもっとも重要な側面かもしれません。加えて、システムの別の部分を変更することなしに、添付ビヘイビアを作成し、どんな要素にも適用することが出来ます。添付ビヘイビアはPascal Binggeliが提起した問題、そして非常に多くの別の問題への綺麗な解決策です。添付ビヘイビアは道具箱に備えておくべき非常に便利な道具です。

Hooking an event on an object and doing something when it fires is certainly not a breakthrough innovation, by any stretch of the imagination. In that sense, attached behaviors are just another way to do the same old thing. However, the importance of this technique is that it has a name, which is probably the most important aspect of any design pattern. In addition, you can create attached behaviors and apply them to any element, without having to modify any other part of the system. It is a clean solution to the problem raised by Pascal Binggeli, and many, many other problems. It’s a very useful tool to have in your toolbox.

参考文献 References

The Attached Behavior Pattern – John Gossman

Simplifying the WPF TreeView by Using the ViewModel Pattern – Josh Smith

Working with CheckBoxes in the WPF TreeView – Josh Smith

改定履歴 Revision History

  • August 30, 2008 – Created the article

ライセンス License

この記事と、関連するソースコードやファイルはThe Code Project Open License (CPOL)の下でライセンスされています。

(訳注:和訳に際してJosh Smithさんから許可を頂いています。)

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

著者について About the Author

Joshはソフトウェア開発者です。C#とXAMLは彼の愛好する表現方法です。

彼はInfragisticsにおいてエクスペリエンスデザインアプリケーションエンジニアとして働いており、.NETのデスクトップ環境をより良き場所にする手助けをしています。

彼はJ.S. バッハの曲をピアノで演奏もします。

とりわけ、彼は彼の素晴らしいガールフレンドと共に過ごすことを愛しています。

Crack.NETという彼のランタイムデバッキング、スクリプティングツールをここから手に入れよう![^].

彼のWPF.JoshSmithライブラリをここからダウンロードしよう[^]

ここから彼のWPFブログをチェックすることが出来ます[^].

ここから彼のWPFツアーに参加することが出来ます[^].

Mole for Visual Studioという彼が手掛けたパワフルなデバッガビジュアライザをここからチェックできます[^].

彼のMicrosoft MVPプロフィールはここで見ることが出来ます[^].

Occupation: 
ソフトウェア開発者 (上級)

Company:

Infragistics, Inc.

Location:

United States アメリカ合衆国

Josh creates software. C# and XAML are his preferred modes of expression.

He works at Infragistics as an Experience Design Application Engineer, helping to make the .NET desktop world a better place.

He also plays the music of J.S. Bach on the piano.

Most of all, he loves being with his wonderful girlfriend.

Get his runtime debugging and scripting tool, called Crack.NET, right here[^].

Download his WPF.JoshSmith library here[^]

You can check out his WPF blog here[^].

You can take his guided tour of WPF here[^].

You can check out a powerful debugger visualizer he worked on called Mole for Visual Studio here[^].

His Microsoft MVP profile can be viewed here[^].

Occupation:

Software Developer (Senior)

Company:

Infragistics, Inc.

Location:

United States United States

MAR
13

我らが社長、まさかのBlogデビュー

Published:2009-03-13 00:47:44 UTC

社長日記

20090306_835451[1]20090306_835452[1][1]20090306_835453[1]

我らが社長がBlogデビューを果たしていたようです。最近撮った写真 – SharpLab.で取り上げたネコ。研究棟内を自由に闊歩し、隙があれば会議室はおろかサーバールームにも侵入を試みる態度の大きさから社長と呼ばれて親しまれています。久しぶりにモフモフしたい~。