Prism開発者ガイド-パート6、高度なMVVMスクリプト

目次

  1. はじめに
  2. Prismアプリケーションの初期化
  3. コンポーネント間の依存関係の管理
  4. モジュラーアプリケーション開発
  5. MVVMパターンの実装
  6. 高度なMVVMシナリオ
  7. ユーザーインターフェイスの作成

    1. ユーザーインターフェイスのガイドライン
  8. ナビゲーション
    1. ビューベースのナビゲーション
  9. 疎結合コンポーネント間の相互作用


前の章では、ユーザーインターフェイス、プレゼンテーションロジック、ビジネスロジックを別々のクラス(プレゼンテーション、プレゼンテーションモデル、モデル)に分けて、データ、コマンド、データ検証インターフェイスをバインドすることで相互作用を実装することで、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



, , .



   SaveAll.



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



, .



 EditRegion,    Tab control.



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



 EditRegion,    Tab control.



, Prism IActiveAware



. IActiveAware



IsActive



, true



, , IsActiveChanged



, , .



IActiveAware



. , , , . , , . , TabControl



, , , .



DelegateCommand



IActiveAware



. true



monitorCommandActivity



, CompositeCommand



, DelegateCommand



( CanExecute



). true



, CompositeCommand



DelegateCommand



, CanExecute



Execute



.



monitorCommandActivity



true



, CompositeCommand



:



, , . 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



, .



  ButtonClick  IComman.



, , 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







































All Articles