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

Source: https://habr.com/ru/post/J176869/


All Articles