目次
前の章では、ユーザーインターフェイス、プレゼンテーションロジック、ビジネスロジックを別々のクラス(プレゼンテーション、プレゼンテーションモデル、モデル)に分けて、データ、コマンド、データ検証インターフェイスをバインドすることで相互作用を実装することで、MVVMパターンの主要な要素を作成する方法について説明しました、作成とカスタマイズを整理します。
これらの基本要素を使用してMVVMパターンを実装することは、アプリケーションのほとんどのシナリオに適合する可能性があります。 ただし、MVVMパターンの拡張、またはより高度な方法の使用を必要とする、より複雑なシナリオに遭遇する可能性があります。 これは、アプリケーションが大規模または複雑な場合に発生する可能性が最も高くなりますが、これは多くの小規模なアプリケーションで見られます。 Prismライブラリは、これらのメソッドの多くを実装するコンポーネントを提供し、アプリケーションで簡単に使用できるようにします。
この章では、いくつかの複雑なシナリオと、MVVMパターンがそれらをサポートする方法について説明します。 次のセクションでは、チームを子ビューにチェーンまたはリンクして、カスタム要件をサポートするように拡張する方法を示します。 次のセクションでは、非同期データリクエストの処理方法とその後のユーザーインターフェイスとのやり取りの処理方法、およびビューとビューモデル間のやり取り要求の処理方法について説明します。
「高度な作成とカスタマイズ」セクションでは、Unity Application Block(Unity)やManaged Extensibility Framework(MEF)などの依存性注入コンテナーを使用する場合のコンポーネントの作成およびカスタマイズ方法の概要を説明します。 最後のセクションでは、MVVMアプリケーションのテスト方法について説明し、単体テストモデルクラスとプレゼンテーションモデル、および動作テストの概要を説明します。
チーム
UI. , . , «» 5, , , , : ,
Command
, .
. WPF.
H , , , MVVM WPF , ( Silverlight ). WPF UI. , UI , . , UI, , . WPF , code-behind , .
, , , , . , , .
, , , , . , Save All
Save
, , .

Prism
CompositeCommand
.
CompositeCommand
, . , . , UI, -, , .
,
CompositeCommand
Stock Trader RI,
SubmitAllOrders
, Submit All buy/sell . Submit All,
SubmitCommand
, buy/sell .
CompositeCommand
(
DelegateCommand
).
Execute
CompositeCommand
Execute
.
CanExecute
CanExecute
, - ,
CanExecute
false
. , ,
CompositeCommand
, .
RegisterCommand
UnregisterCommand
. Stock Trader RI, ,
Submit
Cancel
buy/sell
SubmitAllOrders
CancelAllOrders
, (.
OrdersController
).
commandProxy.SubmitAllOrdersCommand.RegisterCommand( orderCompositeViewModel.SubmitCommand); commandProxy.CancelAllOrdersCommand.RegisterCommand( orderCompositeViewModel.CancelCommand);
commandProxy
Submit
Cancel
, . ,StockTraderRICommands.cs
.
, UI, , , , . , , , . ,
DelegateCommand
CompositeCommand
Prism.
Prism ( , «» 7) UI. , UI. , . ,
EditRegion
, UI
TabControl
, .

, , . , , , Save All, . , . , , . , Zoom , , .

, Prism
IActiveAware
.
IActiveAware
IsActive
,
true
, ,
IsActiveChanged
, , .
IActiveAware
. , , , . , , . ,
TabControl
, , , .
DelegateCommand
IActiveAware
.
true
monitorCommandActivity
,
CompositeCommand
,
DelegateCommand
(
CanExecute
).
true
,
CompositeCommand
DelegateCommand
,
CanExecute
Execute
.
monitorCommandActivity
true
,
CompositeCommand
:
CanExecute
.true
, . , , .Execute
. . , , .
, , .
IActiveAware
, , . , . ,
Zoom
, .
, , — UI , ( ).
, , ,
ListBox
. , , ,
Delete
, .

Delete
, ,
Delete
UI ,
Delete
, . - ,
ListBox
,
Delete
.
– ,
ElementName
, , . XAML .
<Grid x:Name="root">
<ListBox ItemsSource="{Binding Path=Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Path=Name}"
Command="{Binding ElementName=root,
Path=DataContext.DeleteCommand}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Name
.
Delete
. .
CommandParameter
, , , , (
CollectionView
).
Silverlight 3 , Silverlight , .
ICommand
,
Command
,
ICommand
. MVVM Silverlight 3, Prism ( 2.0) , Silverlight , . WPF, Silverlight, WPF.
, Prism , , .
<Button Content="Submit All"
prism:Click.Command="{Binding Path=SubmitAllCommand}"
prism:Click.CommandParameter="{Binding Path=TickerSymbol}" />
Silverlight 4
Command
Hyperlink
ButtonBase
, WPF.
Command
"Commands" 5, "Implementing the MVVM Pattern". , Prism , .
. , , , . Microsoft Expression Blend ,
InvokeCommandAction
CallMethodAction
, , «Invoking Command Methods from the View» 5, "Implementing the MVVM Pattern", Expression Blend Behaviors SDK . Expression Blend , . Expression Blend, . "Creating Custom Behaviors" MSDN.
Silverlight 4 Expression Blend Behaviors SDK Prism, , .
. , , , . : . . , .
,
Click
ButtonBase
- ,
ButtonBaseClickCommandBehavior
.
ButtonBase
,
ButtonBaseClickCommandBehavior
,
ICommand
, .

, ,
Click
ButtonBase
, , , , , . / .
Prism
CommandBehaviorBase , , ICommand
. CanExecuteChanged
, Silverlight, WPF.
, , CommandBehaviorBase , . , . , . ButtonBaseClickCommandBehavior
.
public class ButtonBaseClickCommandBehavior : CommandBehaviorBase<ButtonBase> {
public ButtonBaseClickCommandBehavior(ButtonBase clickableObject) : base(clickableObject) {
clickableObject.Click += OnClick;
}
private void OnClick(object sender, System.Windows.RoutedEventArgs e) {
ExecuteCommand();
}
}
CommandBehaviorBase , . , , . , , , , , CanExecute
, .
, . XAML. . . Prism , , , . , . , , , Click
, Command
. Click.Command
, .
. .
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached(
"Command",
typeof(ICommand),
typeof(Click),
new PropertyMetadata(OnSetCommandCallback));
private static readonly DependencyProperty ClickCommandBehaviorProperty =
DependencyProperty.RegisterAttached(
"ClickCommandBehavior",
typeof(ButtonBaseClickCommandBehavior),
typeof(Click),
null);
Command
ButtonBaseClickCommandBehavior
, OnSetCommandCallback
, .
private static void OnSetCommandCallback(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e) {
ButtonBase buttonBase = dependencyObject as ButtonBase;
if (buttonBase != null) {
ButtonBaseClickCommandBehavior behavior = GetOrCreateBehavior(buttonBase);
behavior.Command = e.NewValue as ICommand;
}
}
private static void OnSetCommandParameterCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) {
ButtonBase buttonBase = dependencyObject as ButtonBase;
if (buttonBase != null) {
ButtonBaseClickCommandBehavior behavior = GetOrCreateBehavior(buttonBase);
behavior.CommandParameter = e.NewValue;
}
}
private static ButtonBaseClickCommandBehavior GetOrCreateBehavior(ButtonBase buttonBase ) {
ButtonBaseClickCommandBehavior behavior =
buttonBase.GetValue(ClickCommandBehaviorProperty) as ButtonBaseClickCommandBehavior;
if ( behavior == null ) {
behavior = new ButtonBaseClickCommandBehavior(buttonBase);
buttonBase.SetValue(ClickCommandBehaviorProperty, behavior);
}
return behavior;
}
, Attached Properties Overview MSDN.
. , Silverlight, -, , , . , .
, ( ) , , . UI , UI.
-
- , IAsyncResult
. , , , GetQuestionnaire
, : BeginGetQuestionnaire
EndGetQuestionnaire
. , BeginGetQuestionnaire
. , , EndGetQuestionnaire
.
.NET Framework 4.5 await
async
*Async
. "Asynchronous Programming with Async and Await (C# and Visual Basic)".
, EndGetQuestionnaire
, , () BeginGetQuestionnaire
. , , EndGetQuestionnaire
, .
IAsyncResult asyncResult = this.service.BeginGetQuestionnaire(
GetQuestionnaireCompleted,
null // ,
);
private void GetQuestionnaireCompleted(IAsyncResult result) {
try {
questionnaire = this.service.EndGetQuestionnaire(ar);
}
catch (Exception ex) {
// - .
}
}
, End*
( , EndGetQuestionnaire
), , . , , UI. , , .
UI, -, UI, UI, Dispatcher
SynchronizationContext
. WPF Silverlight, Dispatcher
.
Questionnaire
, QuestionnaireView
. Silverlight CheckAccess
, , UI. , BeginInvoke
, UI.
var dispatcher = System.Windows.Deployment.Current.Dispatcher;
if (dispatcher.CheckAccess()) {
QuestionnaireView.DataContext = questionnaire;
}
else {
dispatcher.BeginInvoke(() => { Questionnaire.DataContext = questionnaire; });
}
MVVM RI , , IAsyncResult
, . , . , .
this.questionnaireRepository.GetQuestionnaireAsync(
result => {
this.Questionnaire = result.Result;
});
result
, , , , . , .
this.questionnaireRepository.GetQuestionnaireAsync(
result => {
if (result.Error == null) {
this.Questionnaire = result.Result;
...
}
else {
// Handle error.
}
})
, . , , . , , , .
, MVVM, , . , -MVVM , MessageBox
code-behind UI, . MVVM , .
MVVM, , . , , , , .
MVVM. , , , , . , , , . .
. , . , . .
, , , . , . , . , WPF Silverlight, .
, MessageBox
, , , .
var result =
interactionService.ShowMessageBox(
"Are you sure you want to cancel this operation?",
"Confirm",
MessageBoxButton.OK);
if (result == MessageBoxResult.Yes) {
CancelRequest();
}
, - , , Silverlight. . . .
interactionService.ShowMessageBox(
"Are you sure you want to cancel this operation?",
"Confirm",
MessageBoxButton.OK,
result => {
if (result == MessageBoxResult.Yes) {
CancelRequest();
}
});
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .
, , – , , – . , , , UI .
MVVM, , , . , , , .
Prism IInteractionRequest
InteractionRequest . IInteractionRequest
, . , . InteractionRequest IInteractionRequest
Raise
, , , , .
InteractionRequest . Raise
( T
) , , . , . , . , , .
public interface IInteractionRequest {
event EventHandler<InteractionRequestedEventArgs> Raised;
}
public class InteractionRequest<T> : IInteractionRequest {
public event EventHandler<InteractionRequestedEventArgs> Raised;
public void Raise(T context, Action<T> callback) {
var handler = this.Raised;
if (handler != null) {
handler(
this,
new InteractionRequestedEventArgs(context, () => callback(context)));
}
}
}
Prism , . Notification
. , . – Title
Content
, . , , , , .
Confirmation
Notification
– Confirmed
, , , . Confirmation
, MessageBox
, / . , Notification
, , .
InteractionRequest , InteractionRequest , . , Raise
, , , .
public IInteractionRequest ConfirmCancelInteractionRequest {
get {
return this.confirmCancelInteractionRequest;
}
}
this.confirmCancelInteractionRequest.Raise(
new Confirmation("Are you sure you wish to cancel?"),
confirmation => {
if (confirmation.Confirmed) {
this.NavigateToQuestionnaireList();
}
});
}
MVVM Reference Implementation (MVVM RI) , IInteractionRequest
InteractionRequest , (. QuestionnaireViewModel.cs
).
, . , . UI .
. Microsoft Expression Blend Behaviors Framework . , , .
EventTrigger
, Expression Blend, , , , . , Prism EventTrigger
, InteractionRequestTrigger
, Raised
IInteractionRequest
. XAML .
, , InteractionRequestTrigger
. Silverlight Prism PopupChildWindowAction
, . , . ContentTemplate
PopupChildWindowAction
, , UI, Content
. Title
.
, , PopupChildWindowAction
, . Notification
NotificationChildWindow
, Confirmation
– ConfirmationChildWindow
. NotificationChildWindow
, , ConfirmationChildWindow
OK Cancel, . , , ChildWindow
PopupChildWindowAction
.
, InteractionRequestTrigger
PopupChildWindowAction
, RI MVVM.
<i:Interaction.Triggers>
<prism:InteractionRequestTrigger
SourceObject="{Binding ConfirmCancelInteractionRequest}">
<prism:PopupChildWindowAction
ContentTemplate="{StaticResource ConfirmWindowTemplate}"/>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
<UserControl.Resources>
<DataTemplate x:Key="ConfirmWindowTemplate">
<Grid MinWidth="250" MinHeight="100">
<TextBlock TextWrapping="Wrap" Grid.Row="0" Text="{Binding}"/>
</Grid>
</DataTemplate>
</UserControl.Resources>
ContentTemplate
, UI Content
. Content
, , TextBlock
Content
.
, , , . , , , , . , RI MVVM, Confirmed
Confirmation
true
, OK.
, . Prism InteractionRequestTrigger
PopupChildWindowAction
.
MVVM, , , , . ( , , , ). , , , .
, . Managed Extensibility Framework (MEF) Unity Application Block (Unity) , , .
, , , , ( ) . , , . , , .
, MEF
MEF, , Import
, , , Export
. , .
, QuestionnaireView
RI MVVM, , Import
. , MEF . set
, .
[Import]
public QuestionnaireViewModel ViewModel {
set { this.DataContext = value; }
}
, .
[Export]
public class QuestionnaireViewModel : NotificationObject {
...
}
, .
public QuestionnaireView() {
InitializeComponent();
}
[ImportingConstructor]
public QuestionnaireView(QuestionnaireViewModel viewModel) : this() {
this.DataContext = viewModel;
}
MEF, Unity. , , . , Visual Studio Expression Blend, , , . , , , , InitializeComponent
.
, Unity
Unity, , MEF. , . , . , .
, , , . , , .
public QuestionnaireView() {
InitializeComponent();
}
public QuestionnaireView(QuestionnaireViewModel viewModel) : this() {
this.DataContext = viewModel;
}
, , Visual Studio Expression Blend.
, , . Unity set
, .
public QuestionnaireView() {
InitializeComponent();
}
[Dependency]
public QuestionnaireViewModel ViewModel {
set { this.DataContext = value; }
}
Unity.
container.RegisterType<QuestionnaireViewModel>();
, .
var view = container.Resolve<QuestionnaireView>();
,
, , . , MEF Unity, .
. , UI, .
, RI MVVM , , . , . ShowView
UI.
private void NavigateToQuestionnaireList() {
// "questionnaire list".
this.uiService.ShowView(ViewNames.QuestionnaireTemplatesList);
}
UI UI . UI. ShowView
UIService
(, ), , .
public void ShowView(string viewName) {
var view = this.ViewFactory.GetView(viewName);
this.MainWindow.CurrentView = view;
}
Prism . , , , . , , "View-Based Navigation" 8, "Navigation".
MVVM
MVVM . - mocking . , , .
INotifyPropertyChanged
INotifyPropertyChanged
, . , ; , , , , .
, , PropertyChanged
, . , ChangeTracker
, MVVM, , . . , .
var changeTracker = new PropertyChangeTracker(viewModel);
viewModel.CurrentState = "newState";
CollectionAssert.Contains(changeTracker.ChangedProperties, "CurrentState");
, , INotifyPropertyChanged
, , , .
, , set
, . , , , , .
var changeTracker = new PropertyChangeTracker(viewModel);
var question = viewModel.Questions.First() as OpenQuestionViewModel;
question.Question.Response = "some text";
CollectionAssert.Contains(changeTracker.ChangedProperties, "UnansweredQuestions");
INotifyPropertyChanged
, PropertyChanged
, , , , . , .
INotifyDataErrorInfo
, , , , IDataErrorInfo
, ( Silverlight) INotifyDataErrorInfo
. INotifyDataErrorInfo
, , , , , .
INotifyDataErrorInfo
: , , , , ErrorsChanged
, GetErrors
, .
, , . , GetErrors
, , , . , , , , . , .
// .
var notifyErrorInfo = (INotifyDataErrorInfo)question;
question.Response = -15;
Assert.IsTrue(notifyErrorInfo.GetErrors("Response").Cast<ValidationResult>().Any());
// .
var notifyErrorInfo = (INotifyDataErrorInfo)question;
question.Response = 15;
Assert.IsFalse(notifyErrorInfo.GetErrors("Response").Cast<ValidationResult>().Any());
, , .
INotifyDataErrorInfo
GetErrors
, INotifyDataErrorInfo
, ErrorsChanged
GetErrors
. , HasErrors
, .
INotifyDataErrorInfo
. , , , - . , INotifyDataErrorInfo
(, ).
, , :
-
HasErrors
. , .
-
ErrorsChanged
, , GetErrors
. ( , ) , . GetErrors
ErrorsChanged
.
INotifyPropertyChanged
, , NotifyDataErrorInfoTestHelper
MVVM, INotifyDataErrorInfo
, . , , , . .
var helper =
new NotifyDataErrorInfoTestHelper<NumericQuestion, int?>(
question,
q => q.Response);
helper.ValidatePropertyChange(
6,
NotifyDataErrorInfoBehavior.Nothing);
helper.ValidatePropertyChange(
20,
NotifyDataErrorInfoBehavior.FiresErrorsChanged
| NotifyDataErrorInfoBehavior.HasErrors
| NotifyDataErrorInfoBehavior.HasErrorsForProperty);
helper.ValidatePropertyChange(
null,
NotifyDataErrorInfoBehavior.FiresErrorsChanged
| NotifyDataErrorInfoBehavior.HasErrors
| NotifyDataErrorInfoBehavior.HasErrorsForProperty);
helper.ValidatePropertyChange(
2,
NotifyDataErrorInfoBehavior.FiresErrorsChanged);
MVVM, , . , , .
, , , . , , , , , IAsyncResult
, - , , UI.
, , , . , . , UI, , , , , " UI."
, , . , , mock-, , , , , .
UI, , . , , , . , . , , .
questionnaireRepositoryMock
.Setup(
r =>
r.SubmitQuestionnaireAsync(
It.IsAny<Questionnaire>(),
It.IsAny<Action<IOperationResult>>()))
.Callback<Questionnaire, Action<IOperationResult>>(
(q, a) => callback = a);
uiServicemock
.Setup(svc => svc.ShowView(ViewNames.QuestionnaireTemplatesList))
.Callback<string>(viewName => requestedViewName = viewName);
submitResultMock
.Setup(sr => sr.Error)
.Returns<Exception>(null);
CompleteQuestionnaire(viewModel);
viewModel.Submit();
// UI .
callback(submitResultMock.Object);
// – .
Assert.AreEqual(ViewNames.QuestionnaireTemplatesList, requestedViewName);
, .
, . "Trees in WPF" MSDN: http://msdn.microsoft.com/en-us/library/ms753391.aspx
, . "Attached Properties Overview" MSDN: http://msdn.microsoft.com/en-us/library/cc265152(VS.95).aspx
MEF, ."Managed Extensibility Framework Overview" MSDN: http://msdn.microsoft.com/en-us/library/dd460648.aspx.
Unity, ."Unity Application Block" MSDN: http://www.msdn.com/unity.
DelegateCommand
, .Chapter 5, "Implementing the MVVM Pattern."
Microsoft Expression Blend, ."Working with built-in behaviors" MSDN: http://msdn.microsoft.com/en-us/library/ff724013(v=Expression.40).aspx.
Microsoft Expression Blend, ."Creating Custom Behaviors" MSDN: http://msdn.microsoft.com/en-us/library/ff724708(v=Expression.40).aspx.
Microsoft Expression Blend, ."Creating Custom Triggers and Actions" MSDN: http://msdn.microsoft.com/en-us/library/ff724707(v=Expression.40).aspx.
WPF Silverlight, ."Threading Model" "The Dispatcher Class" MSDN: http://msdn.microsoft.com/en-us/library/ms741870.aspx
http://msdn.microsoft.com/en-us/library/ms615907(v=VS.95).aspx.
unit- Silverlight, ."Unit Testing with Silverlight 2": http://www.jeff.wilcox.name/2008/03/silverlight2-unit-testing/.
, . "View-Based Navigation" in Chapter 8, "Navigation."
, , ."Event-based Asynchronous Pattern Overview" MSDN: http://msdn.microsoft.com/en-us/library/wewwczdw.aspx
IAsyncResult
, ."Asynchronous Programming Overview" MSDN: http://msdn.microsoft.com/en-us/library/ms228963.aspx