- 1. Bootstrap
- 2. ViewModelLocator
- 3. Module
- 4. MVVM の基本クラス
- 5. InteractionRequest
- 6. Navigation
- 7. EventAggregator
- 8. ModuleLoadSeq
- 9. RegionBehavior
- 10. ModuleCatalog
- まとめ
- 参考
前回まで Prism の公式サンプルを確認しました。補足として、Microsoft のokazukiさんもサンプルを公開してくれていたので、こちらも見ていこうと思います。
サンプルは 2017 年 1 月公開のものなので、少し古いです。注意しましょう。
1. Bootstrap
これはサンプル1の内容で、完全にカバーできていると思います。
2. ViewModelLocator
モジュール化していませんが、View と ViewModel を紐づけるミニマムなサンプルです。
Dependency
属性が解決できないかもしれませんが、現在と名前空間が異なると思います。
using Microsoft.Practices.Unity; using ViewModelLocatorSampleApp.Models; namespace ViewModelLocatorSampleApp.ViewModels { class ShellViewModel { [Dependency] public MessageProvider MessageProvider { get; set; } } }
using Unity; using ViewModelLocatorSampleApp.Models; namespace ViewModelLocatorSampleApp.ViewModels { class ShellViewModel { [Dependency] public MessageProvider MessageProvider { get; set; } } }
3. Module
これはサンプル2の内容です。
4. MVVM の基本クラス
7.1 Modules.AppConfig のやり方でモジュールを実装したやり方です。コマンドは、11 DelegateCommand でフォローしています。
新しい要素は、ErrorsContainer です。
<StackPanel> <Label Content="入力" Target="{Binding ElementName=TextBoxInput}" /> <TextBox x:Name="TextBoxInput" Text="{Binding Input, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> <TextBlock x:Name="TextBlockErrorMessage" Text="{Binding ElementName=TextBoxInput, Path=(Validation.Errors)/ErrorContent}"/> </StackPanel>
class ErrorsContainerSampleViewModel : BindableBase, INotifyDataErrorInfo { public string HeaderText { get; } = "ErrorContainerSample"; private string input; [Required(ErrorMessage = "入力してください")] public string Input { get { return this.input; } set { this.SetProperty(ref this.input, value); } } public ErrorsContainerSampleViewModel() { this.ErrorsContainer = new ErrorsContainer<string>( x => this.ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(x))); } #region Validation private ErrorsContainer<string> ErrorsContainer { get; } public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; protected override bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) { if(!base.SetProperty<T>(ref storage, value, propertyName)) { return false; } var context = new ValidationContext(this) { MemberName = propertyName }; var errors = new List<ValidationResult>(); if (!Validator.TryValidateProperty(value, context, errors)) { this.ErrorsContainer.SetErrors(propertyName, errors.Select(x => x.ErrorMessage)); } else { this.ErrorsContainer.ClearErrors(propertyName); } return true; } public bool HasErrors { get { return this.ErrorsContainer.HasErrors; } } public IEnumerable GetErrors(string propertyName) { return this.ErrorsContainer.GetErrors(propertyName); } #endregion }
Prism には、INotifyDataErrorInfo の実装を補助する ErrorsContainer が追加されていて、これを利用すると簡単に入力値の検証をすることができるようになる。
には、INotifyDataErrorInfo は以下のインターフェースを実装する必要がある:
- bool HasErrors { get; }
- event EventHandler
ErrorsChanged; - IEnumerable GetErrors(string propertyName);
DataAnnotations は前からある機能なので詳細を割愛しますが、以下のようにインターフェースの実装を ErrorsContainer に対応をおまかせできる。
<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <TextBox x:Name="TextBoxInput" Grid.Row="0" Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <TextBlock Grid.Row="1" Text="{Binding ElementName=TextBoxInput, Path=(Validation.Errors)/ErrorContent}" Foreground="Red" Margin="10"/> </Grid>
[Required(ErrorMessage = "入力してください")] public string Text { get => _text; set { CheckErrors(value); SetProperty(ref _text, value); } } public MainWindowViewModel() { ErrorsContainer = new ErrorsContainer<string>( p => this.ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(p)) ); } private ErrorsContainer<string> ErrorsContainer { get; } public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; public bool HasErrors => ErrorsContainer.HasErrors; public IEnumerable GetErrors(string propertyName) => ErrorsContainer.GetErrors(propertyName); public void CheckErrors(string value, [CallerMemberName] string propertyName = null) { var context = new ValidationContext(this) { MemberName = propertyName }; var errors = new List<ValidationResult>(); if (!Validator.TryValidateProperty(value, context, errors)) { ErrorsContainer.SetErrors(propertyName, errors.Select(x => x.ErrorMessage)); } else { ErrorsContainer.ClearErrors(propertyName); } }
ErrorsContainer
にまとめつつ、プロパティ名でさらにまとめてあるので、Text
プロパティのエラー検知に加えて、Text2
, Text3
... と検知したいプロパティを増やしても、INotifyDataErrorInfo インターフェースの実装はもちろん、CheckErrors も修正なしで運用することができる。
CheckErrors は、以下のように SetProperty を override してやってしまうことも。
- protected override bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null)
5. InteractionRequest
おそらくこれは、公式サンプルから消された 25. Interactivity - NotificationRequest に相当する機能だと思います。
IInteractionRequestAware
というインターフェースは存在していませんし、INotification
というインターフェースも存在しません。(おそらく)
現在は非推奨ということでよいと思います。
6. Navigation
- Navigation と同じ内容だけど、コーディングされている内容がバージョン違いでちょっと異なっています。あと、KeepAlive も含まれていたりするので、まとめてある。
7. EventAggregator
- UsingEventAggregator と同じ内容。書き方としては、公式サンプルのほうがキレイになっているんで、見るべき点はないと思います。
8. ModuleLoadSeq
7.4 Modules - LoadManual のような内容。コーディングテクニックというよりも、そういうものなので、理解だけしておけばよいと思います。
9. RegionBehavior
IRegionBehavior
は現在も存在しているインターフェースです。WPF の Behavior を Region でやるための機能です。サンプルでは IDispose
を ViewModel で実行するようなものになっています。
これは公式サンプルにはないものなので、知っておいてもよいと思います。
10. ModuleCatalog
7.1 Modules - AppConfig のような内容です。コーディングテクニックというよりも、そういうものなので、理解だけしておけばよいと思います。
まとめ
2と9が、参考になる可能性があると思います。