引き続き、Microsoft.TeamFoundation.MVVM
名前空間を使って作る、WPFアプリの話です。
例えば、こんな仕様があったということにします。
- 入庫と出庫で同じ画面構成
- 画面に表示する項目名や、在庫数を加減するロジックだけが異なる
その例として、
- View: 入庫/出庫で共通の一つ
- ViewModel
- 入庫/出庫で共通している部分のViewModelBase
- 入庫/出庫ごとに、上のViewModelBaseを継承したViewModel
- データバインディングではこちらのViewModelを使用
をやってみました。
2015/12/10 追記 ここから
公式Blogに以下の記事が掲載されましたので、Microsoft.TeamFoundation.MVVM
の使用前に記事を確認してみてください。
Microsoft.TeamFoundation.MVVM 名前空間の利用について - Visual Studio サポート チーム blog - Site Home - MSDN Blogs
2015/12/10 追記 ここまで
仕様
- メニュー画面に2つのボタン置く
- 片方のボタンでは入庫画面を、もう一方のボタンでは出庫画面を表示する
- WindowのタイトルとDataGridのDataGridTextColumn.Headerが異なるだけという手抜きをして、在庫数を加減するロジックは実装せず
画面イメージ
メニュー画面
入庫画面
出庫画面
メニュー画面
MenuViewのViewやViewModelは今までやってきたことと同じです。
View (MenuView.xaml)
<Window.Resources> <mvvm:RegisterWindow x:Key="Receiving" Type="local:SharedView" /> <mvvm:RegisterWindow x:Key="Shiping" Type="local:SharedView" /> </Window.Resources>
のようなWindow.Resourcesを用意しておきます。
ViewModel (MenuViewModel.cs)
ボタンのコマンドに紐づくExecuteReceivingCommand()メソッドなどに
WindowDisplayService.ShowDialog("Receiving", new ReceivingViewModel());
のような感じで、Window.Resourcesで定義したKeyと、入庫/出庫画面向けのViewModelを渡して表示します。
入庫/出庫画面
使いまわすView (SharedView.xaml)
Windowのタイトルへのデータバインディング
通常のデータバインディングします。
<Window x:Class="SharedViews.SubWindowView" ... Title="{Binding Path=WindowTitle}">
DataGridのDataGridTextColumn.Headerへのデータバインディング
こちらは、以下を参考にしてデータバインディングします。
- [WPF]DataGridColumnへのバインディング | OITA: Oika's Information Technological Activities
- c# - Bind datagrid column visibility MVVM - Stack Overflow
ただ、このままだと忘れそうなので、自分の理解を残しておきます。
まず、使うところ(今回はDataGrid)のResourcesとして、FrameworkElementのDataContextに、現在のDataContextのオブジェクト(ViewModel)をセットします。
<DataGrid.Resources> <FrameworkElement x:Key="proxyElement" DataContext="{Binding}"/> </DataGrid.Resources>
なお、ここではBindingのPathを省略していますが、この書き方の場合でDataContextのオブジェクトがデータバインディングされていることになります。
上の例では、空のバインディング構文 ({Binding}) を使用しています。 この場合、ListBox は、親の DockPanel 要素から DataContext を継承します (この例には示されていません)。 パスを指定しない場合、既定でオブジェクト全体にバインドされます。 つまり、この例では、ItemsSource プロパティをオブジェクト全体にバインドしているため、パスが省略されたことになります
データ バインドの概要 - MSDN
次に、画面には見えないContentControlのContentとして、DataGrid.Resourcesを持っておきます。
<ContentControl Visibility="Collapsed" Content="{StaticResource ResourceKey=proxyElement}"/>
最後に、DataGridTextColumn.Headerへデータバインディングします。
<DataGridTextColumn Header="{Binding Path=DataContext.QuantityTitle, Source={StaticResource ResourceKey=proxyElement}}"/>
入庫/出庫共通のViewModel (SharedViewModelBase.cs)
データバインディング向けのプロパティを用意します。
また、WindowのタイトルやDataGridTextColumn.Header向けのプロパティは継承先のクラスでオーバーライドできるようにしておきます。
public class SharedViewModelBase : ViewModelBase { public ObservableCollection<Slip> SlipSource { get; set; } // 継承先のクラスで実装 public virtual string WindowTitle { get { throw new System.NotImplementedException(); } } public virtual string QuantityTitle { get { throw new System.NotImplementedException(); } } }
あとは、継承先のクラス(ReceivingViewModel.cs, ShipingViewModel.cs)でWindowTitle・QuantityTitleのプロパティを実装します。
感想など
作ってみたものの、
- MVVMの作法では、複数のViewModelで1個のViewを使いまわしていいのか
- 使いまわして良い場合、ベースとなるViewModelから継承したViewModelを、ViewのDataContextとして使う方法でいいのか
というあたりが分かりませんでした。
ソースコード
GitHubに上げました。
CSharp-Sample/MVVMApp/SharedViewMVVM at master · thinkAmi/CSharp-Sample · GitHub