Prism for Windows Runtimeを使ったODataの更新アプリサンプル
ReactivePropertyのNotifier系クラスのサンプルを書きました
コードレシピにアップしてます。
ReactivePropertyのEventToReactiveのサンプル
表題のものをコードレシピにあげました。
ReactivePropertyとPrismでOData読み込み・更新のサンプル
コードレシピ日和ですね。ということで、アップしました。
Windows FormsでもReactiveProperty(同じ要領でXamarinでも使えます)
いやまさか、この年になってWindows Formsのコード書くことになるとは思わなかった…。
本当はXamarinでサンプル書きたかったんですが、Xamarinのライセンスが切れててダメだったので泣く泣くWindows Formsにしました。コードは以下から参照できます。
FlyoutをMVVMのViewModelから制御したい
ということありますよね?
そういう時に便利なBehaviorのコードサンプルをアップしました。
SettingsFlyoutをViewModelから制御したい
SettingsFlyoutをViewModelから表示したいときにつかえるBehaviorのサンプルをコードレシピにアップしました。
ReactiveProperty v1.1リリースしました
今回はちょっと変更してます。
v1.0.xで追加した以下のプロパティを消しました。
- ObserveErrors
- ObserveHasNoError
かわりに以下の変更・追加が行われています。
ObserveErrorChangedプロパティの動作変更
Subscribeしたときに、現在のエラー情報を発行するようにしました。これまではSubscribeしても何か変化があるまで値を発行しませんでした。
過去と同じ動きにしたい場合は以下のようにSkip(1)をしてください。
rp.ObserveErrorChanged.Skip(1)
CombineLatestValuesAreAllFalseメソッドの追加
ObserveHasNoErrorが消えたかわりにObserveNoErrorから簡単にReactiveCommandが作れるようにCombineLatestValuesAreAllFalseメソッドを追加しました。(今まではAreAllTrueしかなかった)
以下のような感じで使えます。
// rp1, rp2, rp3全てエラーがなかったら押せるコマンド ReactiveCommand command = new[] { rp1.ObserveHasError, rp2.ObserveHasError, rp3.ObserveHasError } .CombineLatestValuesAreAllFalse() .ToReactiveCommand();
インストール
NuGetから行ってください。
PM > Install-Package ReactiveProperty
Prism for Windows RuntimeでFileOpenPickerを出す方法
といってもPrismは、FileOpenPickerを出す機能を提供してくれないので自前で作る必要があります。今回は、その自前で作る例として。
ユニバーサルWindowsアプリでPrismとReactivePropetyを使った状態で中断処理へ対応する
Json.NET使えば意外と簡単にできました。Json.NET偉大。
AppBarがあることを教えてくれるバーの作り方
ReactivePropertyを持つVMのコレクションをWindowsFormsのDataGridViewに表示する
遊びです。本気ではないので気を付けてください。
xin9leさんとチャットしながら作っててこんな拡張メソッドを用意しておけば。
publicstaticclass DataGridViewBindingExtensions { publicstaticvoid BindTo<TCollectionItem>( this IEnumerable<TCollectionItem> self, DataGridView dgv, params Func<TCollectionItem, DataGridViewCell>[] createCells) { foreach (var item in self) { var row = new DataGridViewRow(); row.Cells.AddRange(createCells.Select(x => x(item)).ToArray()); dgv.Rows.Add(row); } dgv.CellEndEdit += (_, e) => { var cell = dgv.Rows[e.RowIndex].Cells[e.ColumnIndex]; IReactiveProperty rp = (IReactiveProperty)cell.Tag; rp.Value = cell.Value; }; } publicstatic TCell ToCell<TCell>(this IReactiveProperty self) where TCell : DataGridViewCell, new() { var cell = new TCell(); cell.Value = self.Value; cell.Tag = self; return cell; } }
こんな感じにDataGridViewに表示するコードが書けることがわかった。
var vm = new Form1ViweModel(); vm.People.BindTo( this.dataGridView1, x => x.Name.ToCell<DataGridViewTextBoxCell>(), x => x.Age.ToCell<DataGridViewTextBoxCell>());
DataGridViewには、あらかじめCellに対応するColumnを定義しておくこと。
本当に使えるかは置いておいて、こういうのを考えるのは楽しい。
SQLiteで最後に挿入したときに採番されたIDの値を取得する
auto incrementに設定してるIDの値をとりたい!ってことは往々にしてありますよね。そういうときは、こんなSQL発行すればいいみたいです。
select last_insert_rowid()
ReactivePropertyのコレクション
正直自分しか使ってないんじゃないかというReadOnlyReactiveCollectionのサンプルをのっけました。
Prism for Windows Runtimeのライフサイクル
ここらへん押さえておかないと大変ですよね。
WPF4.5入門のPDFを更新しました
WPF4.5入門で書き続けてるWordファイルをPDF化してSlideShareにアップしました。 前回アップしたときよりも80ページちょい増えています。
残りあと少しの予定ですが、とりあえず今の進捗ということで。
WPF4.5入門 その56「コレクションのバインディング」
データバインディングでは、ここまで説明してきた単一項目のデータバインディングの他に、コレクションをバインディングすることができます。コレクションのデータバインディングは、IEnumerableを実装したコレクションなら、どれでも対象になります。その中でも、INotifyCollectionChangedインターフェースを実装したコレクションは、追加・削除などの変更操作をデータバインディングのターゲットと同期をとることが出来ます。
INotifyCollectionChangedインターフェースの実装は大変な作業なので、デフォルトでObservableCollection
NameとAgeプロパティを持つPersonクラスのコレクションをListBoxにデータバインディングする例を以下に示します。DataContextにPersonクラスのコレクションをMainWindowクラスのコンストラクタで設定します。
using System.Collections.ObjectModel; using System.Linq; using System.Windows; namespace CollectionBindingSample01 { /// <summary>/// MainWindow.xaml の相互作用ロジック/// </summary>publicpartialclass MainWindow : Window { private ObservableCollection<Person> people; public MainWindow() { InitializeComponent(); // DataContextにPersonのコレクションを設定するthis.people = new ObservableCollection<Person>(Enumerable.Range(1, 100) .Select(x => new Person { Name = "tanaka" + x, Age = (30 + x) % 50 })); this.DataContext = people; } } }
XAMLでは、DataContextのコレクションをItemsSourceにBindingして、ItemTemplateで整形して出力します。
<Window x:Class="CollectionBindingSample01.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow"Height="350"Width="525"><Grid><ListBoxItemsSource="{Binding}"><ListBox.ItemTemplate><DataTemplate><StackPanel><TextBlock Text="{Binding Name}" /><TextBlock Text="{Binding Age}" /></StackPanel></DataTemplate></ListBox.ItemTemplate></ListBox></Grid></Window>
表示は以下のようになります。
コレクションのデータバインディングは、基本的に、このようにItemsSourceプロパティにバインドして、ItemTemplateで見た目を整える形になります。詳しくは、ListBoxやDataGridの解説の箇所を参照してください。
コレクションの変更通知
このサンプルプログラムは、ObservableCollection
privatevoid MenuItemAdd_Click(object sender, RoutedEventArgs e) { // コレクションに要素を追加する。this.people.Insert(0, new Person { Name = "追加したtanaka", Age = 100 }); } privatevoid MenuItemClear_Click(object sender, RoutedEventArgs e) { // 全削除this.people.Clear(); }
コレクションへの要素の追加と、コレクションから全要素の削除処理を行っています。サンプルプログラムを起動して追加メニューをクリックすると以下のようにコレクションに要素が追加され、それにあわせて表示も更新されます。
クリアメニューをクリックすると、peopleコレクションの中身がクリアされ、それにあわせて表示もクリアされます。
CollectionView
コレクションのデータバインディングを行うと、内部的には、直接コレクションがバインドされるのではなく、間にCollectionViewというものが暗黙的に生成されます。これをデフォルトのCollectionViewといいます。このデフォルトのCollectionViewを取得するにはCollectionViewSourceクラスのGetDefaultViewメソッドを呼んで取得します。
CollectionViewは、コレクション本体を操作することなく、要素の並び替えやフィルター、グルーピングなどを行うことが出来るコレクションに対するビューの役割を担います。Filter処理を行うには、CollectionViewのFilterプロパティにPredicate
var collectionView = CollectionViewSource.GetDefaultView(this.people); collectionView.Filter = x => { // 年齢が偶数の人だけにフィルタリング var person = (Person)x; return person.Age % 2 == 0; };
ソート処理を行うには、SortDescritptionsプロパティにSortDescriptionを追加することでソートができます。SortDescriptionは、ソートのキーになるプロパティ名と、昇順・降順を表すListSortDirection列挙体を渡します。Ageプロパティをキーにして昇順にソートするコード例を以下に示します。
var collectionView = CollectionViewSource.GetDefaultView(this.people); // Ageプロパティで昇順にソートする collectionView.SortDescriptions.Add(new SortDescription("Age", ListSortDirection.Ascending));
グルーピングを行うには、GroupDescriptionsプロパティにPropertyGroupDescriptionを追加することでグルーピングが出来ます。PropertyGroupDescriptionには、グルーピングするプロパティ名と、必要に応じてプロパティ値を変換するためのIValueConverterを指定出来ます。Ageプロパティの10の位の値をもとにグルーピングを行うコード例を以下に示します。
class AgeConverter : IValueConverter { publicobject Convert(objectvalue, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return (int)value / 10; } publicobject ConvertBack(objectvalue, Type targetType, object parameter, System.Globalization.CultureInfo culture) { thrownew NotImplementedException(); } } // -------------------------------------------------------------------------------------- var collectionView = CollectionViewSource.GetDefaultView(this.people); // Ageプロパティの10の位の値でグルーピングする collectionView.GroupDescriptions.Add(new PropertyGroupDescription("Age", new AgeConverter()));
グルーピングした結果を表示するには、ListBoxコントロールなどのGroupStyleプロパティにGroupStyleを設定する必要があります。GroupStyleのHeaderTemplateにDataTemplateを指定することで、グループ単位のヘッダー要素を指定出来ます。コード例を以下に示します。
<ListBox.GroupStyle><GroupStyle><GroupStyle.HeaderTemplate><DataTemplate><TextBlock Text="{Binding Name, StringFormat={}{0}0代}" /></DataTemplate></GroupStyle.HeaderTemplate></GroupStyle></ListBox.GroupStyle>
GroupStyleのHeaderTemplateでは、Nameプロパティでグルーピングするときにつかわれた値(今回の例では、10代なら1、20代なら2)が取得できます。そして、BindingのStringFormatプロパティを使ってデータバインディングの値の書式が指定できるため、”X0代”と表示するようにしています。
実行してグルーピング処理を走らせた結果の画面を以下に示します。
CollectionViewSource
ここまで、デフォルトのCollectionViewを使ってきましたが、CollectionViewを明示的に作成することもできます。CollectionViewを作成するには、CollectionViewSourceクラスを使用します。CollectionViewSourceクラスのSourceプロパティにコレクションを設定すると、その型に応じて自動的にCollectionViewが内部で生成されます。CollectionViewの取得はCollectionViewSourceクラスのViewプロパティで行えます。CollectionViewSourceは、通常以下のようにXAMLのResourcesに定義します。Resourcesに定義したCollectionViewSourceは、BindingのSourceに指定して使います。
<Window x:Class="CollectionBindingSample03.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow"Height="350"Width="525"><Window.Resources><CollectionViewSourcex:Key="Source"Source="{Binding}" /></Window.Resources><Grid><DataGrid ItemsSource="{Binding Source={StaticResource Source}}" /></Grid></Window>
CollectionViewSourceには、CollectionViewと同様にソート、フィルタ、グルーピング機能があります。CollectionViewSourceでは、宣言的にこれらの機能を定義することが出来ます。
年齢の降順でソートして、年齢ごとにグルーピングする場合の定義は以下のようになります。
<CollectionViewSourcex:Key="Source"Source="{Binding}"><CollectionViewSource.GroupDescriptions><PropertyGroupDescription PropertyName="Age"/></CollectionViewSource.GroupDescriptions><CollectionViewSource.SortDescriptions><ComponentModel:SortDescription Direction="Descending"PropertyName="Age"/></CollectionViewSource.SortDescriptions></CollectionViewSource>
フィルタは、この例では示していませんが、Filterイベントを定義することで実現できます。
リアルタイムソート・フィルター・グルーピング
CollectionViewとCollectionViewSourceを使ったフィルタ・ソート・グルーピングは、適用した直後のコレクションの状態を元にして処理が行われます。フィルタ・ソート・グルーピングを行った後に、コレクションが変更されたらグルーピングなどの状態は更新されません。コレクションの変更に追随して、フィルタ・ソート・グルーピングも更新されるようにするには、CollectionViewSourceクラスで以下のプロパティにTrueを設定します。
- IsLiveFilteringRequested:リアルタイムにフィルタリングをサポートしているCollectionViewの場合はリアルタイムにフィルタリングを行う。
- IsLiveSortingRequested:リアルタイムにソートをサポートしているCollectionViewの場合はリアルタイムにソートを行う。
- IsLiveGroupingRequested:リアルタイムにグルーピングをサポートしているCollectionViewの場合はリアルタイムにグルーピングを行う。
上記のプロパティでリアルタイムの情報更新を有効にしたうえで、LiveFilteringProperties、LiveSortingProperties、LiveGroupingPropertiesで、フィルタ・ソート・グルーピングを有効にするプロパティ名を指定します。Ageプロパティでリアルタイム機能を有効にする場合のXAMLを以下に示します。
<CollectionViewSourcex:Key="Source"Source="{Binding}"IsLiveFilteringRequested="True"IsLiveGroupingRequested="True"IsLiveSortingRequested="True"><CollectionViewSource.LiveSortingProperties><System:String>Age</System:String></CollectionViewSource.LiveSortingProperties><CollectionViewSource.LiveGroupingProperties><System:String>Age</System:String></CollectionViewSource.LiveGroupingProperties><CollectionViewSource.LiveFilteringProperties><System:String>Age</System:String></CollectionViewSource.LiveFilteringProperties></CollectionViewSource>
これに、通常のソート機能・グルーピング機能などを追加します。今回の例ではAgeプロパティのみがリアルタイムに反映されます。
<CollectionViewSource.GroupDescriptions><PropertyGroupDescription PropertyName="Age"/></CollectionViewSource.GroupDescriptions><CollectionViewSource.SortDescriptions><ComponentModel:SortDescription Direction="Descending"PropertyName="Age"/></CollectionViewSource.SortDescriptions>
現在の選択項目をバインディングする方法
CollectionViewは、現在の選択項目を管理しているので、同じCollectionViewをバインドしているもの同士では、選択項目を同期することが可能です。選択項目を同期するには、Selectorコントロール(ListBoxなどの親クラス)のIsSynchronizedWithCurrentItemをTrueにする必要があります。選択中の項目をバインドするには、”/”を使ってコレクションとバインドします。たとえば、Categoriesというコレクションにバインドしていて、それの選択中の項目のNameプロパティをバインドするためには”Categories/Name”のようにバインディングのPathを指定します。この”/”を使ったバインディングは、親子だけでなく孫やその先までコレクションがある限り指定できます。例えば、Peopleコレクションの選択中の項目のChildrenプロパティ(これもコレクション)の選択中の項目のNameプロパティは“People/Children/Name”のように指定できます。
例として以下のようなPersonクラスをバインドしたケースを示します。
using System.ComponentModel; using System.Runtime.CompilerServices; namespace CollectionBindingSample04 { publicclass Person : INotifyPropertyChanged { publicevent PropertyChangedEventHandler PropertyChanged; privatevoid SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null) { field = value; var h = this.PropertyChanged; if (h != null) { h(this, new PropertyChangedEventArgs(propertyName)); } } privatestring name; publicstring Name { get { returnthis.name; } set { this.SetProperty(refthis.name, value); } } privateint age; publicint Age { get { returnthis.age; } set { this.SetProperty(refthis.age, value); } } } }
このクラスのコレクションを持ったDataContextに格納するクラスを以下に示します。
using System.Collections.ObjectModel; namespace CollectionBindingSample04 { publicclass MainWindowViewModel { private ObservableCollection<Person> people; public ObservableCollection<Person> People { get { return people; } set { people = value; } } } }
MainWindowのコンストラクタでDataContextに設定します。
public MainWindow() { InitializeComponent(); this.DataContext = new MainWindowViewModel { People = new ObservableCollection<Person>( Enumerable.Range(1, 100) .Select(x => new Person { Name = "tanaka" + x, Age = (30 + x) % 50 })) }; }
このMainWindowViewModelクラスのPeopleをDataGridにバインドします。バインド時に、IsSynchronizedWithCurrentItemをTrueに設定して、選択項目の同期を有効にします。
<!-- 選択項目を同期するように設定してバインドする --><DataGrid IsSynchronizedWithCurrentItem="True"ItemsSource="{Binding People}"/>
選択項目をバインドするためにPeople/NameのようにPathを指定してデータバインディングを行います。
<!-- 現在選択中の項目をバインドする --><Label Content="名前"/><TextBox TextWrapping="Wrap"Text="{Binding People/Name}"/><Label Content="年齢"/><TextBox TextWrapping="Wrap"Text="{Binding People/Age}"/>
実行すると選択項目がTextBoxに表示されることが確認できます。
別スレッドからのコレクションの操作
データバインディングしたコレクションは、通常UIスレッドから操作する必要がありますが、WPFでは、BindingOperations. EnableCollectionSynchronizationメソッドを呼び出すことで、コレクションをUIスレッド以外から操作できるようになります。EnableCollectionSynchronizationメソッドは、第一引数にコレクションを渡して、第二引数にコレクションを操作するときに使用するロックオブジェクトを指定します。使用例を以下に示します。
publicpartialclass MainWindow : Window { private Timer timer; private ObservableCollection<Person> people; public MainWindow() { InitializeComponent(); // コレクションをDataContextに設定するthis.people = new ObservableCollection<Person>(); this.DataContext = this.people; // コレクションの操作をロックするように設定 BindingOperations.EnableCollectionSynchronization(this.people, newobject()); // 別スレッドからコレクションを操作するthis.timer = new Timer(1000); this.timer.Elapsed += (_, __) => this.people.Add(new Person { Name = "tanaka " + this.people.Count }); this.timer.Start(); } }
XAMLを以下に示します。
<Window x:Class="CollectionBindingSample05.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow"Height="350"Width="525"><Grid><DataGrid ItemsSource="{Binding}" /></Grid></Window>
BindingOperations.EnableCollectionSynchronizationの行をコメントアウトすると、例外が出てアプリケーションが終了することが確認できます。バックグラウンドスレッドでコレクション操作をするときは、自分でUIスレッドで操作をするようにするか、ここで紹介した、BindingOperations.EnableCollectionSynchronizationメソッドを使用しましょう。
過去記事
- WPF4.5入門 その1 「はじめに」
- WPF4.5入門 その2 「WPFとは」
- WPF4.5入門 その3 「Hello world」
- WPF4.5入門 その4 「Mainメソッドはどこにいった?」
- WPF4.5入門 その5 「全てC#でHello world」
- WPF4.5入門 その6 「WPFを構成するものを考えてみる」
- WPF4.5入門 その7 「XAMLのオブジェクト要素と名前空間」
- WPF4.5入門 その8 「オブジェクト要素のプロパティ」
- WPF4.5入門 その9 「コレクション構文」
- WPF4.5入門 その10 「コンテンツ構文」
- WPF4.5入門 その11 「マークアップ拡張」
- WPF4.5入門 その12 「その他のXAMLの機能」
- WPF4.5入門 その13 「簡単なレイアウトを行うコントロール」
- WPF4.5入門 その14 「レイアウトコントロールのCanvasとStackPanel」
- WPF4.5入門 その15 「レイアウトコントロールのDockPanelとWrapPanel」
- WPF4.5入門 その16 「ViewBoxコントロール」
- WPF4.5入門 その17 「ScrollViewerコントロール」
- WPF4.5入門 その18 「Gridコントロール part 1」
- WPF4.5入門 その19 「Gridコントロール part 2」
- WPF4.5入門 その20 「レイアウトに影響を与えるプロパティ」
- WPF4.5入門 その21 「WPFのコンセプトと重要な機能つまみ食い」
- WPF4.5入門 その22 「Buttonコントロール」
- WPF4.5入門 その23 「DataGridコントロール その1」
- WPF4.5入門 その24 「DataGridコントロール その2」
- WPF4.5入門 その25 「TreeViewコントロール その1」
- WPF4.5入門 その26 「TreeViewコントロール その2」
- WPF4.5入門 その28 「Calendarコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その29 「ContextMenuコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その30「Menuコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その31 「ToolBarコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その32 「CheckBoxコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その33 「ComboBoxコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その34 「ListBoxコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その35 「RadioButtonコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その36 「Sliderコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その37 「TabControl」 - かずきのBlog@hatena
- WPF4.5入門 その38 「ファイルダイアログ」 - かずきのBlog@hatena
- WPF4.5入門 その39 「情報を表示するコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その40 「Popup、ToolTip、TextBox、Image、MediaElementコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その41 「DispatcherObject」 - かずきのBlog@hatena
- WPF4.5入門 その42 「WPFのプロパティシステム」 - かずきのBlog@hatena
- WPF4.5入門 その43 「読み取り専用の依存関係プロパティ」 - かずきのBlog@hatena
- 拡張されたプロパティメタデータ - かずきのBlog@hatena
- WPF4.5入門 その45 「添付プロパティ」 - かずきのBlog@hatena
- WPF4.5入門 その46 「WPFのイベントシステム」 - かずきのBlog@hatena
- WPF4.5入門 その47 「コンテンツモデル」 - かずきのBlog@hatena
- WPF4.5入門 その48 「WPFのアニメーション その1」 - かずきのBlog@hatena
- WPF4.5入門 その49 「WPFのアニメーション その2」 - かずきのBlog@hatena
- WPF4.5入門 その50 「Style」 - かずきのBlog@hatena
- WPF4.5入門 その51 「リソース」 - かずきのBlog@hatena
- WPF4.5入門 その52 「コントロールテンプレート」 - かずきのBlog@hatena
- WPF4.5入門 その53 「ユーザーコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その54 「カスタムコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その55 「Binding その1」 - かずきのBlog@hatena
WPF4.5入門 その57「コマンド」
WPFには、ICommandインターフェースというユーザーの操作を抽象化する仕組みがあります。ICommandインターフェースは、以下のように定義されています。
publicinterface ICommand { // コマンドを実行するかどうかに影響するような変更があった場合に発生します。event EventHandler CanExecuteChanged; // 現在の状態でこのコマンドを実行できるかどうかを判断するメソッドを定義します。//// パラメーター:// parameter:// コマンドで使用されたデータ。 コマンドにデータを渡す必要がない場合は、このオブジェクトを null に設定できます。//// 戻り値:// このコマンドを実行できる場合は true。それ以外の場合は false。bool CanExecute(object parameter); // コマンドの起動時に呼び出されるメソッドを定義します。//// パラメーター:// parameter:// コマンドで使用されたデータ。 コマンドにデータを渡す必要がない場合は、このオブジェクトを null に設定できます。void Execute(object parameter); }
コマンドが実行可能かどうかという状態に変化があったことを通知するCanExecuteChangedイベントと、実際にコマンドが実行可能かどうかを返すCanExecuteメソッドがあります。そして、コマンドの処理を実行するためのExecuteメソッドがあります。
ICommandは、ICommandSourceという以下のようなインターフェースを実装したクラスに対して設定することが出来ます。ICommandSourceは以下のように定義されています。
// コマンドを呼び出す方法を認識しているオブジェクトを定義します。publicinterface ICommandSource { // コマンド ソースが呼び出されると実行されるコマンドを取得します。//// 戻り値:// コマンド ソースが呼び出されると実行されるコマンド。 ICommand Command { get; } // コマンドの実行時にコマンドに渡すことのできるユーザー定義データの値を表します。//// 戻り値:// コマンド固有のデータ。object CommandParameter { get; } // コマンドが実行されているオブジェクト。//// 戻り値:// コマンドが実行されているオブジェクト。 IInputElement CommandTarget { get; } }
実行するコマンドを取得するためのCommandプロパティと、コマンドに渡すためのCommandParameterが定義されています。CommandTargetは後述するRoutedCommandのみに適用される特殊なプロパティなのでここでは省略します。ICommandSourceインターフェースは、ButtonBase(ボタン系コントロールの基本クラス)やMenuItemなど、ユーザーがアクションを実行するコントロールに主に実装されています。
WPFでのICommandインターフェースの実装クラスのRoutedCommandクラスは、CommandBindingという仕組みを通じてユーザーのアクションと処理を結びつける機能を持っています。RoutedCommandクラスは、以下のように、クラスの静的メンバーとして定義して使用します。
publicpartialclass MainWindow : Window { publicstatic RoutedCommand AlertCommand = new RoutedCommand(); public MainWindow() { InitializeComponent(); } }
このRoutedCommandと、実際の処理を結びつけるには、UIElementクラスに定義されているCommandBindingsプロパティにCommandBindingを設定して行います。一般的にWindowクラスのCommandBindingsプロパティを使って以下のように定義します。
<Window.CommandBindings><CommandBinding Command="{x:Static local:MainWindow.AlertCommand}"Executed="CommandBinding_Executed"CanExecute="CommandBinding_CanExecute"/></Window.CommandBindings>
CommandBindingクラスのCommandプロパティに、先程定義したCommandのインスタンスを設定します。そして、ExecuteイベントにCommandの実行時の処理のイベントハンドラと、CanExecuteイベントにCommandの実行可否の処理のイベントハンドラを設定します。ここでは、仮にCheckBoxコントロールが画面にあり、CheckBoxコントロールにチェックがされているときだけ実行可能なコマンドを定義します。画面にCheckBoxコントロールと、コマンドを実行するためのButtonを置きます。ButtonのCommandプロパティには、CommandBindingに設定したものと同じ、MainWindowクラスのAlertCommandプロパティを設定します。
<Window x:Class="CommandSample01.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:CommandSample01"Title="MainWindow"Height="350"Width="525"><Window.CommandBindings><CommandBinding Command="{x:Static local:MainWindow.AlertCommand}"Executed="CommandBinding_Executed"CanExecute="CommandBinding_CanExecute"/></Window.CommandBindings><StackPanel><CheckBox x:Name="checkBox"Content="CanExecute"/><Button Content="AlertCommand"Command="{x:Static local:MainWindow.AlertCommand}" /></StackPanel></Window>
そして、コードビハインドでイベントハンドラの処理を記述します。
publicpartialclass MainWindow : Window { publicstatic RoutedCommand AlertCommand = new RoutedCommand(); public MainWindow() { InitializeComponent(); } privatevoid CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("Hello world"); } privatevoid CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = this.checkBox.IsChecked.Value; } }
全体のつながりを説明すると、CommandBindingとButtonのCommandに同じコマンドのインスタンスを設定することで、この2つを繋げます。そして実際の処理は、CommandBindingのイベントハンドラで行います。実行結果を以下に示します。
実行すると、以下のようにボタンが押せない状態で起動します。
CheckBoxコントロールにチェックを入れると、ボタンが押せるようになります。これはCommandBindingに設定したCanExecuteイベントのイベントハンドラで行っている処理でCheckBoxコントロールのチェック状態を見てイベント引数のCanExecuteプロパティに実行可否の値を設定しているためです。
そして、Buttonコントロールをクリックすると、CommandBindingのExecuteイベントのイベントハンドラが実行されてMessageBoxが表示されます。
このように、RoutedCommandクラスと、CommandBindingクラスを使うことで操作を表すコマンドと実際の処理を分離して記述することが出来ます。
コマンドは、InputBindingを使うことで簡単にキーボードショートカットやマウスジェスチャーに対応させることが出来ます。WindowなどのInputBindingsプロパティに、KeyBindingを設定することでキーボードショートカットとコマンドの関連付けを行うことが出来ます。KeyBindingのModifiersプロパティに修飾キーを設定して、Keyプロパティにキーを設定して、Commandプロパティに該当するキーボードが押されたときに実行する処理を表すコマンドを設定します。例として、先程のAlertCommandをCtrl+Alt+Aを押したときに表示するようにするコードを示します。
<Window.InputBindings><KeyBinding Modifiers="Alt+Control"Key="A"Command="{x:Static local:MainWindow.AlertCommand}" /></Window.InputBindings>
実行して、CheckBoxコントロールにチェックを入れた状態でCtrl+Alt+Aを押すとMessageBoxが表示されます。
これまでの例では、自分でRoutedCommandのインスタンスを用意したものを使用しましたが、WPFでは組み込みで、アプリケーションによくあるコマンドがあらかじめ定義されています。コピーやペーストなどの一般的な操作をRoutedCommandで定義する場合は、下記のApplicationCommandsクラスに定義されているものを使用するとよいでしょう。
- ApplicationCommandsクラス: http://msdn.microsoft.com/ja-jp/library/system.windows.input.applicationcommands(v=vs.110).aspx
このように、WPFでは、組み込みのICommandインターフェースの実装が提供されています。しかし、ICommandインターフェースを実装していれば、InputBindingなどの機能は使うことが出来ます。最近のWPFをはじめとするXAMLを使った開発ではICommandインターフェースを実装してExecuteやCanExecuteの処理をデリゲートで受け取るDelegateCommand(RelayCommandという名前の場合もある)という実装が使われるのが一般的です。これらのコマンドの独自実装については後述します。
過去記事
- WPF4.5入門 その1 「はじめに」
- WPF4.5入門 その2 「WPFとは」
- WPF4.5入門 その3 「Hello world」
- WPF4.5入門 その4 「Mainメソッドはどこにいった?」
- WPF4.5入門 その5 「全てC#でHello world」
- WPF4.5入門 その6 「WPFを構成するものを考えてみる」
- WPF4.5入門 その7 「XAMLのオブジェクト要素と名前空間」
- WPF4.5入門 その8 「オブジェクト要素のプロパティ」
- WPF4.5入門 その9 「コレクション構文」
- WPF4.5入門 その10 「コンテンツ構文」
- WPF4.5入門 その11 「マークアップ拡張」
- WPF4.5入門 その12 「その他のXAMLの機能」
- WPF4.5入門 その13 「簡単なレイアウトを行うコントロール」
- WPF4.5入門 その14 「レイアウトコントロールのCanvasとStackPanel」
- WPF4.5入門 その15 「レイアウトコントロールのDockPanelとWrapPanel」
- WPF4.5入門 その16 「ViewBoxコントロール」
- WPF4.5入門 その17 「ScrollViewerコントロール」
- WPF4.5入門 その18 「Gridコントロール part 1」
- WPF4.5入門 その19 「Gridコントロール part 2」
- WPF4.5入門 その20 「レイアウトに影響を与えるプロパティ」
- WPF4.5入門 その21 「WPFのコンセプトと重要な機能つまみ食い」
- WPF4.5入門 その22 「Buttonコントロール」
- WPF4.5入門 その23 「DataGridコントロール その1」
- WPF4.5入門 その24 「DataGridコントロール その2」
- WPF4.5入門 その25 「TreeViewコントロール その1」
- WPF4.5入門 その26 「TreeViewコントロール その2」
- WPF4.5入門 その28 「Calendarコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その29 「ContextMenuコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その30「Menuコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その31 「ToolBarコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その32 「CheckBoxコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その33 「ComboBoxコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その34 「ListBoxコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その35 「RadioButtonコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その36 「Sliderコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その37 「TabControl」 - かずきのBlog@hatena
- WPF4.5入門 その38 「ファイルダイアログ」 - かずきのBlog@hatena
- WPF4.5入門 その39 「情報を表示するコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その40 「Popup、ToolTip、TextBox、Image、MediaElementコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その41 「DispatcherObject」 - かずきのBlog@hatena
- WPF4.5入門 その42 「WPFのプロパティシステム」 - かずきのBlog@hatena
- WPF4.5入門 その43 「読み取り専用の依存関係プロパティ」 - かずきのBlog@hatena
- 拡張されたプロパティメタデータ - かずきのBlog@hatena
- WPF4.5入門 その45 「添付プロパティ」 - かずきのBlog@hatena
- WPF4.5入門 その46 「WPFのイベントシステム」 - かずきのBlog@hatena
- WPF4.5入門 その47 「コンテンツモデル」 - かずきのBlog@hatena
- WPF4.5入門 その48 「WPFのアニメーション その1」 - かずきのBlog@hatena
- WPF4.5入門 その49 「WPFのアニメーション その2」 - かずきのBlog@hatena
- WPF4.5入門 その50 「Style」 - かずきのBlog@hatena
- WPF4.5入門 その51 「リソース」 - かずきのBlog@hatena
- WPF4.5入門 その52 「コントロールテンプレート」 - かずきのBlog@hatena
- WPF4.5入門 その53 「ユーザーコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その54 「カスタムコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その55 「Binding その1」 - かずきのBlog@hatena
- WPF4.5入門 その56「コレクションのバインディング」 - かずきのBlog@hatena
Windows 8とかでMSアカウント使ってるとAzure仮想マシンにRDPで認証で蹴られる
MSCCでMSDN Subscription Ultimate民が量産されたところなので、Azure上に仮想マシン立ててスグにこわせる開発環境として使おうとしてる人もいると思います。
そこで、たまに表題の件でつまずくことがあります。
ログインのときに認証ではじかれた場合は以下のことを試してみてください。
アカウント名を以下のようにして入れる。
.\アカウント名
こうするとうまくいくと思います。
Blend for Visual Studio 2015が事件です
先日DownloadできるようになったVisual Studio 2015 Previewですが、こいつのBlendがやばいです。どうやばいってVisual StudioのShellをベースにBlendが再構築されたみたいな感じになってます。具体的には以下のウィンドウが普通にある。
- Solution Explorer
- Team Explorer
コードエディタがVisual Studioと同じ。インテリセンス、コードスニペット、etc...が全て使える。
テーマを青とかにすると、どう見てもVisual Studioですありがとうございます。
コードエディタ
今までBlendのコードエディタは貧弱なものでした。どれくらい貧弱かというと見るにはいいけど、Visual Studioに比べるとコード補完も何もないというお粗末なものでした。
今回のBlendでは、Visual Studioと同じコード補完機能が備わってるっぽいです。ためしにコードスニペットとか登録してみたりしたら普通に動きました。インテリセンスもがっつり動きます。
XAMLでAlt + F12してコードを参照してる状態。
デザイナ
デザイナはBlend従来通りっぽいです。Assets, State, Data, Document Outlineがあります。
ソリューションエクスプローラー・チームエクスプローラー
ソリューションエクスプローラーがそのまま使えるので、フォルダとかにきちっと対応してくれます。あと、チームエクスプローラーも統合されてるので、がっつりチーム開発にも対応できます。
無くなったもの
新規作成で、WPF, Sliverlight, Universal Windows appなどのアプリが作れるのですが、スケッチフローがなくなってますorz 残念ですが、これからはストーリーボーディングを使おうってことなんですかね。
まとめ
というかVisual Studio 2015にBlendの機能が入ればいいんじゃないかというくらいBlendがVisual Studioちっくになってます。これまでVisual Studioをメインの環境として必要に応じてBlendを開くという形でしたが、Visual Studio 2015からは、クライアント系の開発をするときはBlend for Vsiual Studio 2015だけ開いてれば開発出来るようになってる感じです。
個人的には歓迎。でもVSに統合されるのが一番だと思うなぁ。