皆さんはPrismを使って製造をする場合、入力項目名等はどのように表示していますか?
私はPrismのバインド機構の便利さに感動してWPF+Prismを用いての開発に移行しました。
しかし、業務システム等でよくありがちな画面構成として以下のような画面があると思います。
よくあるヘッダーと入力コントロールを並べていく画面です。
使いまわしのできるViewを作るためにはヘッダー名もView上に保持せず、ViewModel側に保持する必要が出てくると思います。
NGコード
<Window x:Class="BindingExtender.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="{Binding Title}" Height="350" Width="525" >
<Grid>
<StackPanel>
<DockPanel>
<TextBlock Text="コード" Width="100" TextAlignment="Center" />
<TextBox Text="{Binding Code}"/>
</DockPanel>
<DockPanel>
<TextBlock Text="名前" Width="100" TextAlignment="Center" />
<TextBox Text="{Binding Name}"/>
</DockPanel>
</StackPanel>
</Grid>
</Window>
先ほど例題に挙げたフォームのコードです。
TextBlockのTextプロパティに、View上でそのまま値を設定しています。
個人的には似たような画面項目のViewについては、再利用したいと考えているのですが、上記のコードでは再利用が難しくなってしまいます。
問題を解決するためにはViewModel側にヘッダ用のプロパティを用意する必要がありますが、1つ1つのヘッダ用のプロパティを実装するのは保守性や製造の面から厳しいものがあるかと思われます。
解決策
解決するためには「DynamicObject」を利用します。
PrismではViewのバインドで、DynamicObjectのプロパティもバインドしてくれます。
DynamicObjectについては下記のヤスノート様のブログにて詳細に説明されていたので掲載させていただきます。
https://yaspage.com/prog/csharp/cs-dynamicobject/
今回は、専用クラスは用意せず「ExpandoObject」を用いて紹介していきます。
最初にViewModelを修正します。
using Prism.Mvvm;
namespace BindingExtender.ViewModels
{
public class MainWindowViewModel : BindableBase
{
private string _title = "Prism Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
private string _code;
public string Code
{
get { return _code; }
set { SetProperty(ref _code, value); }
}
private string _name;
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
public System.Dynamic.ExpandoObject DynamicObject { get; }
public MainWindowViewModel()
{
dynamic obj = new System.Dynamic.ExpandoObject();
obj.Code = "コード";
obj.Name = "名前";
this.DynamicObject = obj;
}
}
}
ViewModelにExpandoObject型の「DynamicObject」プロパティを用意しました。
コンストラクタにてExpandObjectの生成及び、「Code」「Name」プロパティの登録を行っています。
次にViewを修正します。
<Window x:Class="BindingExtender.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="{Binding Title}" Height="350" Width="525" >
<Grid>
<StackPanel>
<DockPanel>
<TextBlock Text="{Binding DynamicObject.Code}" Width="100" TextAlignment="Center" />
<TextBox Text="{Binding Code}"/>
</DockPanel>
<DockPanel>
<TextBlock Text="{Binding DynamicObject.Name}" Width="100" TextAlignment="Center" />
<TextBox Text="{Binding Name}"/>
</DockPanel>
</StackPanel>
</Grid>
</Window>
各TextBlockにて「DynamicObject」のプロパティをバインドするようにしました。
このように、PrismのバインドはDynamicObjectにも対応しています。
今回はViewModelのコンストラクタで生成していますが、Modelに保持させたりすることで様々な利用ができると思います。
まとめ
まだWPFとPrismの勉強を始めて間もないですが、バインド機構やデザインの自由さにすごく感動を覚えています。
ただし、今回のような痒いところに手が届かない部分もありますので、備忘録としても記事を掲載していこうと思います。
ちなみにですが、実際の製造ではModelのプロパティにヘッダ名用のAttributeを付与し、Reflectionで自動的にヘッダ用のDynamicObjectを生成したりしています。
時間があればそちらについても掲載していこうと思います。
また、よりスマートな方法や記事の内容に誤りがありましたらご連絡いただけると幸いです。
2022/03/14 追記
現実的な実装方法について投稿いたしました。
https://qiita.com/jakucho0926/items/070d43d015312a6042ee