WPFのMVVMでイベントを処理する方法いろいろ
最近PrismやらReactive Propertyやら勉強中なので忘れないように書いていきます
- コードビハインド
- Prism
- Reactive Property
を使ってイベントを処理するサンプルを作成しました
サンプルコードはこちら github.com
開発環境はVisual Studio Community 2019で対象のフレームワークは.NET Core 3.0です
サンプルの中身
サンプルアプリはこんな感じの画面になっていて
- Textboxに入力したものと同じ内容をTextBlockにコピーする
- ボタンを押したらTextBlockの内容をすべて消去する
という動作になっています
サンプルコードではButtonのClickイベントとTextBoxのPreviewTextInputイベントを処理しています
TextBox クラス (System.Windows.Controls) | Microsoft Docs
Button クラス (System.Windows.Controls) | Microsoft Docs
コードビハインドでのイベント処理
MVVMじゃなくなっちゃいますがコードビハインドを使うことでWinFormっぽくイベントを処理することができます
xamlでViewの部品それぞれに名前とイベントの処理先を書いて、コードビハインドに処理を実装します
- CodeBehind.xaml (UIのレイアウト部分のみ)
<StackPanel Margin="5, 5" > <TextBlock Text="CodeBehind"/> <Button x:Name="button" Content="ボタンです" Click="button_Click"/> <TextBox x:Name="textBox" PreviewTextInput="textBox_PreviewTextInput"/> <TextBlock x:Name="textBlock" Text="TextBoxに入力した文字をここに表示します ボタンを押すと文字をすべて消去します"/> </StackPanel>
- CodeBehind.xaml.cs
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void button_Click(object sender, RoutedEventArgs e) { textBlock.Text = ""; } private void textBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e) { textBlock.Text += e.Text; } }
Prismでのイベント処理
PrismはMVVMを実現するためのサポートライブラリです
サンプルプログラムにはPrism.Unityパッケージが使われています
Prism.xamlでViewを作成し、PrismViewModelでイベントの処理や入力されたテキストの制御を行います
ソースコードは以下になります
- Prism.xaml
<UserControl x:Class="EventSample.Views.Prism" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" prism:ViewModelLocator.AutoWireViewModel="True"> <StackPanel Margin="5, 5"> <TextBlock Text="Prism"/> <Button Content="ボタンです" Command="{Binding ButtonClickCommand}"/> <TextBox Text="{Binding InputText}"> <i:Interaction.Triggers> <i:EventTrigger EventName="PreviewTextInput"> <prism:InvokeCommandAction Command="{Binding PreviewTextInputCommand}"/> </i:EventTrigger> </i:Interaction.Triggers> </TextBox> <TextBlock Text="{Binding OutputText}"/> </StackPanel> </UserControl>
- PrismViewModel.cs
public class PrismViewModel : BindableBase { private string inputText = ""; public string InputText { get { return inputText; } set { SetProperty(ref inputText, value); } } private string outputText = "TextBoxに入力した文字をここに表示します\r\nボタンを押すと文字をすべて消去します"; public string OutputText { get { return outputText; } set { SetProperty(ref outputText, value); } } private DelegateCommand<RoutedEventArgs> buttonClickCommand; public DelegateCommand<RoutedEventArgs> ButtonClickCommand => buttonClickCommand ?? (buttonClickCommand = new DelegateCommand<RoutedEventArgs>(ExecuteButtonClickCommand)); private DelegateCommand<TextCompositionEventArgs> previewInputTextCommand; public DelegateCommand<TextCompositionEventArgs> PreviewTextInputCommand => previewInputTextCommand ?? (previewInputTextCommand = new DelegateCommand<TextCompositionEventArgs>(ExecutePreviewInputText)); public PrismViewModel() { } void ExecuteButtonClickCommand(RoutedEventArgs e) { OutputText = ""; } void ExecutePreviewInputText(TextCompositionEventArgs e) { OutputText += e.Text; } }
ソースコードのポイントは以下のような感じ
- Prism.xaml 5行目
// 5: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
イベントの処理に使用するためのライブラリをi:のプレフィックスに割り当てています
- Prism.xaml 9行目
// 9: <Button Content="ボタンです" Command="{Binding ButtonClickCommand}"/>
ボタンが押された時に呼び出すViewModel側のButtonClickCommand関数を呼び出すようにしています
- Prism.xaml 10~17行目
// 10: <TextBox Text="{Binding InputText}"> <i:Interaction.Triggers> <i:EventTrigger EventName="PreviewTextInput"> <prism:InvokeCommandAction Command="{Binding PreviewTextInputCommand}"/> </i:EventTrigger> </i:Interaction.Triggers> </TextBox> <TextBlock Text="{Binding OutputText}"/>
ポイント
- 10行目、17行目:TextBox, TextBlockに表示するテキストをViewModelのInputText, OutputTextプロパティにバインドしています
- 12行目:TextBoxで処理したいイベントの設定をしています(今回はPreviewTextInputイベント)
- 13行目:イベントが発生したときに呼び出すViewModelの関数を設定しています(PreviewTextInputCommand関数が呼び出されます)
PrismViewModel.cs 13~25行目
// 13: private string inputText = ""; public string InputText { get { return inputText; } set { SetProperty(ref inputText, value); } } private string outputText = "TextBoxに入力した文字をここに表示します\r\nボタンを押すと文字をすべて消去します"; public string OutputText { get { return outputText; } set { SetProperty(ref outputText, value); } }
TextBox, TextBlockのバインド先を定義しています
※Visual Studioの拡張機能にPrism Template Packをインストールしていてスニペットの設定もできていれば"propp" + Tabで自動入力できます
- PrismViewModel.cs 27~33行目
// 27: private DelegateCommand<RoutedEventArgs> buttonClickCommand; public DelegateCommand<RoutedEventArgs> ButtonClickCommand => buttonClickCommand ?? (buttonClickCommand = new DelegateCommand<RoutedEventArgs>(ExecuteButtonClickCommand)); private DelegateCommand<TextCompositionEventArgs> previewInputTextCommand; public DelegateCommand<TextCompositionEventArgs> PreviewTextInputCommand => previewInputTextCommand ?? (previewInputTextCommand = new DelegateCommand<TextCompositionEventArgs>(ExecutePreviewInputText));
ボタンが押された時とテキストが入力された時のイベントのバインド先を定義しています
※こちらも"cmdg" + Tabで自動入力できます
- PrismViewModel.cs 41~49行目
// 41: void ExecuteButtonClickCommand(RoutedEventArgs e) { OutputText = ""; } void ExecutePreviewInputText(TextCompositionEventArgs e) { OutputText += e.Text; }
- ポイント
- 43行目:ボタンが押された時の処理を書いています。OutputTextはViewのTextBlockにバインドされているのでプロパティの変更と同時に表示が更新されます
- 48行目:TextBoxに文字が入力された時の処理を書いています。入力された文字はe.Textに入っているのでそれをOutputTextに設定しています
Reactive Propertyでのイベント処理
Reactive PropertyはReactive ExtensionsをベースとしたMVVMのサポートライブラリです
Reactive Extensionsの知識があると理解しやすいですが、なくても簡単にイベントを処理することができます
NugetパッケージにReactivePropertyを追加すれば使用可能です
Prismと同様にReactiveProperty.xamlでViewを作成し、ReactivePropertyViewModel.csでイベントの処理や入力されたテキストの制御を行います
ソースコードはこちら
- ReactiveProperty.xaml
<UserControl x:Class="EventSample.Views.ReactiveProperty" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:rp="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.NETCore" prism:ViewModelLocator.AutoWireViewModel="True"> <StackPanel Margin="5, 5"> <TextBlock Text="ReactiveProperty" /> <Button Content="ボタンです" Command="{Binding ButtonClickCommand}"/> <TextBox Text="{Binding InputText.Value}"> <i:Interaction.Triggers> <i:EventTrigger EventName="PreviewTextInput"> <rp:EventToReactiveCommand Command="{Binding PreviewTextInputCommand}"/> </i:EventTrigger> </i:Interaction.Triggers> </TextBox> <TextBlock Text="{Binding OutputText.Value}"/> </StackPanel> </UserControl>
- ReactivePropertyViewModel.cs
public class ReactivePropertyViewModel : BindableBase { public ReactivePropertySlim<string> InputText { get; } = new ReactivePropertySlim<string>(""); public ReactiveProperty<string> OutputText { get; } = new ReactiveProperty<string>("TextBoxに入力した文字をここに表示します\r\nボタンを押すと文字をすべて消去します"); public ReactiveCommand<RoutedEventArgs> ButtonClickCommand { get; } = new ReactiveCommand<RoutedEventArgs>(); public ReactiveCommand<TextCompositionEventArgs> PreviewTextInputCommand { get; } = new ReactiveCommand<TextCompositionEventArgs>(); public ReactivePropertyViewModel() { this.ButtonClickCommand.Subscribe(e => { this.OutputText.Value = ""; }); this.PreviewTextInputCommand.Subscribe(e => { this.OutputText.Value += e.Text; }); } }
以下ポイント
- ReactiveProperty.xaml 5行目
// 5: xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
prismと同じようにイベントの処理に使用するためのライブラリをi:のプレフィックスに割り当てています
ただし、prismと参照するライブラリが違うので注意
参照するライブラリ
- Prism:xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
- ReactiveProperty:xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
ReactiveProperty.xaml 6行目
// 6: xmlns:rp="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.NETCore"
ReactivePropertyをrp:のプレフィックスに割り当てています(イベントの処理をバインドするときに使う)
- ReactiveProperty.xaml 10行目
// 10: <Button Content="ボタンです" Command="{Binding ButtonClickCommand}"/>
ボタンが押された時に呼び出すViewModel側のButtonClickCommand関数を呼び出すようにしています(Prismと同じ)
- Prism.xaml 11~18行目
// 11: <TextBox Text="{Binding InputText.Value}"> <i:Interaction.Triggers> <i:EventTrigger EventName="PreviewTextInput"> <rp:EventToReactiveCommand Command="{Binding PreviewTextInputCommand}"/> </i:EventTrigger> </i:Interaction.Triggers> </TextBox> <TextBlock Text="{Binding OutputText.Value}"/>
ポイント
- 11行目、18行目:表示するテキストをViewModelのInputText, OutputTextプロパティにバインド。Prismと違って.Valueを付ける必要があるので注意
- 13行目:TextBoxで処理したいイベントの設定をしています(Prismと同じ)
- 14行目:イベントが発生したときに呼び出すViewModelの関数を設定しています(PreviewTextInputCommand関数が呼び出されます)
ReactivePropertyViewModel.cs 14~16行目
// 14: public ReactivePropertySlim<string> InputText { get; } = new ReactivePropertySlim<string>(""); public ReactiveProperty<string> OutputText { get; } = new ReactiveProperty<string>("TextBoxに入力した文字をここに表示します\r\nボタンを押すと文字をすべて消去します");
TextBox, TextBlockのバインド先を定義しています
※ReactivePropertyのスニペットの設定もできていれば"rprop" + Tabで自動入力できます
- ReactivePropertyViewModel.cs 18~20行目
// 18: public ReactiveCommand<RoutedEventArgs> ButtonClickCommand { get; } = new ReactiveCommand<RoutedEventArgs>(); public ReactiveCommand<TextCompositionEventArgs> PreviewTextInputCommand { get; } = new ReactiveCommand<TextCompositionEventArgs>();
ボタンが押された時とテキストが入力された時のイベントのバインド先を定義しています
※こちらも"rcommg" + Tabで自動入力できます
- ReactivePropertyViewModel.cs 25~33行目
// 25: this.ButtonClickCommand.Subscribe(e => { this.OutputText.Value = ""; }); this.PreviewTextInputCommand.Subscribe(e => { this.OutputText.Value += e.Text; });
- ポイント
- 定義したコマンドをSubScribeするとイベントが発生したときに、ラムダ式内の処理が呼び出されるようになります
- SubScribeの中で処理をしているだけで27行目、32行目の処理はPrismとほとんど同じです
まとめ
以上、WPFのMVVMでイベントを処理する方法いろいろでした
個人的にはReactive Propertyが一番ViewModelをすっきり書ける気がするので自分はReactivePropertyをよく使っています
おわり
Self hostedのVS Codeに接続できなくなったときの対策
前記事で作った環境を使ってiPadでVS Code使って遊ぶぞー
と思ったらこんなエラーが出て繋がらなくなった
どうやってVSCode復帰させればいいのか調べてたら公式のTroubleshootingのページにあった
セルフホスト環境が使えなくなったら、リストアを試してみてね
環境を右クリックして「Restore Local Environment」を選んでね
と書いてあるので
左メニューの「リモートエクスプローラー」を開いて、Self-hosted environmentの作成した環境を右クリックして「Restore Local Environment」を実行
リストア実行中のダイアログが出てくるので結構長く待つ
(自分の環境では2,3分待った)
これで再びVS Codeに繋がるようになりました
PCの電源入れるだけで環境立ち上がるようにできないかなあ...
Visual Studio OnlineでiPadからVSCodeを使えるようにした
iPadでコーディングをしたかったのでVS Codeを使えるようにした。
以下手順。
〇必要なもの
・Microsoftのアカウント
・Azureのアカウント(無料アカウントでOK)
1.以下のURLを開いて「Sign in」を押してサインイン
https://online.visualstudio.com/login
※Microsoftアカウントの名前に漢字が使われているとログインページで一生ループする現象が起こるようです。自分はローマ字表記に修正しました。
https://github.com/MicrosoftDocs/vsonline/issues/184#issuecomment-554621731
2.「Create environment」を押して環境作成。設定はこんな感じ。
次のCreate Environmentはキャンセルでおk。
3.VS Codeを立ち上げて拡張機能「Visual Studio Online」をインストール
Ctrl+Shift+xを押して、検索してこれをInstall
4.自分のPCをSelf Hostedに登録
Ctrl+Shift+pを押して、「VS Online: Register Local Environment」を選択。
Azureのサインインを促されるのでサインインする。
プランの選択はさっき作成したプランを選択。
ワークフォルダは自PC内の適当なフォルダを選択。
環境名は適当な名前を入力(デフォルトでおk)
ここまでやってRegisterが終われば準備完了。
環境ができたので今度はiPadのほうでVisual Studio Onlineにサインイン。
さっき作った環境が表示されている。(画質悪くてスンマセン...)
「あんたのブラウザサポートされてないよ!でもすぐサポートするからね!」ってメッセージが出てるけどキニシナイ。
右下のハンバーガーを押して「Connect」をタップ。
ちゃんとVSCodeの画面が表示された。しゅごい。
2020/2/27
PCシャットダウンしたら繋がらなくなったので、対策を追記
iPadをサブディスプレイ化したぞい
プライバシーポリシー
当サイトに掲載されている広告について
当サイトでは、第三者配信の広告サービス(Googleアドセンス)を利用しています。
このような広告配信事業者は、ユーザーの興味に応じた商品やサービスの広告を表示するため、当サイトや他サイトへのアクセスに関する情報 『Cookie』(氏名、住所、メール アドレス、電話番号は含まれません) を使用することがあります。
またGoogleアドセンスに関して、このプロセスの詳細やこのような情報が広告配信事業者に使用されないようにする方法については、こちらをクリックしてください。
当サイトが使用しているアクセス解析ツールについて
当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。
このGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。
このトラフィックデータは匿名で収集されており、個人を特定するものではありません。
この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。
この規約に関して、詳しくはこちら、またはこちらをクリックしてください。
当サイトへのコメントについて
当サイトでは、スパム・荒らしへの対応として、コメントの際に使用されたIPアドレスを記録しています。
これはブログの標準機能としてサポートされている機能で、スパム・荒らしへの対応以外にこのIPアドレスを使用することはありません。
また、メールアドレスとURLの入力に関しては、任意となっております。
全てのコメントは管理人であるjagapokoが事前にその内容を確認し、承認した上での掲載となりますことをあらかじめご了承下さい。
加えて、次の各号に掲げる内容を含むコメントは管理人の裁量によって承認せず、削除する事があります。
- 特定の自然人または法人を誹謗し、中傷するもの。
- 極度にわいせつな内容を含むもの。
- 禁制品の取引に関するものや、他者を害する行為の依頼など、法律によって禁止されている物品、行為の依頼や斡旋などに関するもの。
- その他、公序良俗に反し、または管理人によって承認すべきでないと認められるもの。
免責事項
当サイトで掲載している画像の著作権・肖像権等は各権利所有者に帰属致します。権利を侵害する目的ではございません。記事の内容や掲載画像等に問題がございましたら、各権利所有者様本人が直接メールでご連絡下さい。確認後、対応させて頂きます。
当サイトからリンクやバナーなどによって他のサイトに移動された場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。
当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。
当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。
プライバシーポリシーの変更について
当サイトは、個人情報に関して適用される日本の法令を遵守するとともに、本ポリシーの内容を適宜見直しその改善に努めます。
修正された最新のプライバシーポリシーは常に本ページにて開示されます。
運営者:jagapoko
Vue.jsでアプリを作成してFirebaseにデプロイする
最近やったので忘れないうちにメモ。環境はwindows 10です。
Vue-cliのインストール
Node.jsのインストールが完了したらコマンドプロンプトを起動して以下を入力。
$ npm i vue-cli -g
とりあえずインストールの確認。
$ npm list --depth=0 -g vue-cli@2.9.6
Vue.jsアプリの作成
コマンドプロンプトでどこか適当なフォルダを指定(とりあえず"D:\Vue"に)
$ D: $ mkdir Vue $ cd Vue
そして以下のコマンドを実行。初期設定をいろいろ聞かれますがとりあえず何も考えずに全部Enterで大丈夫そうです。
$ vue init webpack hellovue ? Project name hellovue ? Project description A Vue.js project ? Author XXXXXXX ? Vue build standalone ? Install vue-router? Yes ? Use ESLint to lint your code? Yes ? Pick an ESLint preset Standard ? Set up unit tests Yes ? Pick a test runner jest ? Setup e2e tests with Nightwatch? Yes ? Should we run `npm install` for you after the project has been created? (recommended) npm
いろいろ初期化された後、こんな感じの画面が出ると思います。指示通りにコマンドを打ってみましょう。
$ cd hellovue
$ npm run dev
すると、http://localhost:8081でアプリケーションが動いてるよ!とメッセージが出ます。
(普通の人はhttp://localhost:8080かも?)
開いてこんなページが出れば成功です。
Firebaseのプロジェクトを作成する
以下のページ右上からGoogleアカウントでログインしてコンソールへ移動。
firebase.google.com
新しくプロジェクトを追加する。下の画像のような感じでいいと思います。
Firebase CLIのインストールとログイン
せっかくコンソールまで開いたところですが、コマンドプロンプトに戻ります。
以下のコマンドからFirebase CLIをインストール。
$ npm install -g firebase-tools
インストールが終わったらログイン。Googleアカウントへの許可を求めてくるので許可します。
$ firebase login
無事にログインできました。
Vueプロジェクトのビルド
特にいじっていなければ、コマンドプロンプトのカレントのフォルダ構成は以下みたいな感じだと思います。
この状態から、まずはデプロイするためにVueプロジェクトをビルドします。
$ npm run build
そうすると、distというフォルダが生成されると思います。
このdistフォルダをFirebaseにデプロイしていきます。
Firebaseの初期化とデプロイ
まずはFirebaseの初期化を行います。以下のコマンドを入力。
$ firebase init hosting
Are you ready to proceed? と聞かれるのでEnter。
次にプロジェクトを選択するように言われるので先ほど作成したプロジェクトを選択。
(hellovueと書かれたやつがあると思います)
そして、"What do you want to use as your public directory?"と聞かれるので
先ほどビルドしたdistフォルダを指定します。そしてEnter。
後の質問はすべてEnterで大丈夫です。
完了したら以下のコマンドでデプロイしましょう。
$ firebase deploy
ページの確認
あとはコンソールに表示されているURLにアクセスして、作成したVue.jsアプリの画面が表示されれば完了です。
デプロイされたアプリはFirebaseコンソールのHostingメニューから確認できます。
用が済んだら以下のコマンドでアプリを停止できます。
$ firebase hosting:disable
とっても簡単