📜 ⬆️ ⬇️

Prism Developer Guide - Part 6, Advanced MVVM Scripts

Table of contents
  1. Introduction
  2. Initializing Prism Applications
  3. Manage dependencies between components
  4. Modular Application Development
  5. Implementation of the MVVM pattern
  6. Advanced MVVM scripts
  7. Creating user interface
    1. User Interface Design Recommendations
  8. Navigation
    1. View-Based Navigation (View-Based Navigation)
  9. The interaction between loosely coupled components

The previous chapter described how to create the main elements of the MVVM pattern, dividing the user interface, presentation logic and business logic into separate classes (presentation, presentation model and model), to realize interaction between them (through data binding, commands and data validation interfaces) , organize their creation and configuration.

Implementing the MVVM pattern using these basic elements is likely to fit most scenarios in your application. However, it is possible to meet with more complex scenarios that require the expansion of the MVVM pattern, or the use of more advanced methods. This is most likely to happen if your application is large or complex, but you can meet with this in many small applications. The Prism library provides components that implement many of these methods, allowing you to easily use them in your applications.

This chapter describes some complex scenarios and how the MVVM pattern supports them. The following section shows how commands can be chained or associated with child views, as well as how they can be extended to support user requirements. The following sections describe how to handle asynchronous data requests and the subsequent interaction with the user interface, as well as how to handle interaction requests between the view and the view model.
')
The section “Advanced Creation and Configuration” gives an idea of ​​how to create and configure components when using a dependency injection container, such as the Unity Application Block (Unity), or Managed Extensibility Framework (MEF). The final section describes how to test MVVM applications, and gives an idea about unit testing of model classes and presentation models, as well as testing behaviors.

Teams


Commands provide a way to separate the implementation logic of a command from its presentation in the UI. Binding data or behavior makes it possible to declaratively link elements in the view with the commands provided by the view model. Section “Commands” in Chapter 5 described how commands can be implemented as command objects, or as command methods in a view model, and how they can be called by controls in a view: using behaviors, or the Command property, which have some controls.
The note. WPF routed commands.
It should be noted that commands implemented as command objects or command methods in the MVVM pattern are somewhat different from the built-in implementation of WPF commands called routed commands (Silverlight has no routable command implementations). WPF routed commands transmit a command message through elements in the UI tree. Therefore, messages are sent up or down the UI tree from the element with the focus, or to an explicitly specified target element. By default, they are not passed to components outside the UI tree, such as the view model associated with the view. However, WPF routed commands can use the handler defined in the code-behind view to send a command call to the view model class.

Composite teams


In many cases, the command defined by the view model will be associated with the controls in the associated view so that the user can directly call it from within the view. However, in some cases, it may be necessary to invoke commands from one or more view models from a control in the parent view.

For example, if your application allows the user to edit various elements at the same time, you can allow the user to save all the elements using a single command, represented by a button on the toolbar. In this case, the Save All command will invoke each of the Save commands implemented by an instance of the view model of each element, as shown in the following illustration.

SaveAll.

Prism supports such a script through the CompositeCommand class.

The CompositeCommand class represents a command that is made up of various child commands. When a compound command is invoked, each of its child commands is invoked alternately. This is useful in situations where you want to present a group of commands as a single command in a UI, or somewhere where you want to call several commands, as one logical command.

For example, the CompositeCommand class is used in the Stock Trader RI to implement the SubmitAllOrders command, represented by the Submit All button in the buy / sell view. When the user clicks the Submit All button, each SubmitCommand defined by the buy / sell transaction is executed.

The CompositeCommand class maintains a list of child commands ( DelegateCommand instances). The Execute method of the CompositeCommand class simply calls the Execute method on each of the child commands in turn. The CanExecute method also calls the CanExecute method of each child command, but if any of the child commands cannot be executed, the CanExecute method returns false . In other words, by default, the CompositeCommand can be executed only when all the child commands can be executed.

Register and delete child commands


Child commands are registered or deleted with the RegisterCommand and UnregisterCommand methods. In Stock Trader RI , for example, the Submit and Cancel commands for each buy / sell order are registered with the composite SubmitAllOrders and CancelAllOrders , as shown in the following example (see the OrdersController class).

 commandProxy.SubmitAllOrdersCommand.RegisterCommand( orderCompositeViewModel.SubmitCommand); commandProxy.CancelAllOrdersCommand.RegisterCommand( orderCompositeViewModel.CancelCommand); 

The note
The previous commandProxy object provides an instance access to the commands of the compound object Submit and Cancel , which are defined statically. For more information, see the StockTraderRICommands.cs file.

Execution of commands in active child views

It often happens that your application must show a collection of child views within the UI, where each child view will have a corresponding view model, which in turn can implement one or more commands. Composite commands can be used to represent commands implemented by child views, and can help coordinate how they will be invoked from within the parent view. To support these scripts, the DelegateCommand and CompositeCommand classes were designed to work with the Prism regions.

The Prism regions (described in the section “Regions” in Chapter 7) allow the child views to be associated with regions in the UI. They are often used to separate the layout of child views from their region and its position in the UI. Regions are based on named placeholders that are attached to specific markup controls. The following illustration shows an example where each child view was added to the EditRegion region, and the UI developer wanted to use the TabControl element to place the views in this region.

EditRegion,    Tab control.

Composite commands at the parent view level are often used to coordinate how commands are called at the child view level. In some cases, you will want the commands for all displayed views to be executed, as in the example of the Save All command described earlier. In other cases, you want the command to be executed only on the active view. In this case, the compound command will execute the child commands only on the views that are active. For example, you might want to implement the Zoom command on the toolbar, which only scales the currently active element, as shown in the following diagram.

EditRegion,    Tab control.

To support this scenario, Prism provides an IActiveAware interface. The IActiveAware interface defines the IActiveAware property, which returns true when the control is active, and the IsActiveChanged event, which is generated whenever the active state changes.

You can implement an IActiveAware interface on child views or view models. This is primarily used to track the active state of the child view in the area. Whether a view is active is determined by the region adapter, which controls the views within a particular area control. For example, for the TabControl element shown earlier, there is a region adapter that sets the view on the currently selected tab as active.

The DelegateCommand class also implements the IActiveAware interface. By setting the monitorCommandActivity parameter to monitorCommandActivity in the constructor, the CompositeCommand can be configured to evaluate the active state of the child DelegateCommand element (in addition to the CanExecute state). When these parameters are set to true , the CompositeCommand class will examine the active state of each child DelegateCommand , determining the return value for the CanExecute method and executing the child commands within the Execute method.

When the monitorCommandActivity parameter monitorCommandActivity set to true , the CompositeCommand class shows the following behavior:

You can use this functionality to implement the example described earlier. IActiveAware implementing the IActiveAware interface in your child view models, you will be notified when your child view in the region becomes active or inactive. When the active state of the child view changes, you can update the active state of the child commands. Then, when the user invokes the compound Zoom command, it will be called only on the active child view.

Binding commands within collections


Another common scenario you'll often see when displaying a collection of items in a view is when you need a UI for each item in the collection that will be associated with a command at the parent view level (instead of the item level).

For example, in the application shown in the following illustration, the view shows a collection of items in a ListBox . The data template, used to show each item, defines the Delete button, which allows the user to delete individual items from the collection.

.

Since the view model implements the Delete command, the problem is to attach the Delete button in the UI for each element to the Delete command implemented by the view model. The difficulty arises from the fact that the data context for each of the elements in the ListBox refers to an element in the collection instead of the parent view model that implements the Delete command.

One approach to solving this problem is to bind a button in the data template to a command in the parent view, using the ElementName property to ensure that the binding is relative to the parent control, and not to the data template. The following XAML illustrates this method.

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

The button content in the data template is associated with the Name property of the item in the collection. However, the command for the button is linked via the data context of the root element with the Delete command. This allows the button to be associated with a command at the parent view level instead of at the element level. You can use the CommandParameter property to specify the element to which the command will be applied, or you can implement the command to work with the currently selected element (via the CollectionView ).

Command binding behaviors


In Silverlight 3 and earlier versions, Silverlight did not provide controls that support commands. The ICommand interface was available, but no controls implemented the Command property to allow them to be bound to the ICommand implementation. To overcome this limitation and maintain the MVVM pattern in Silverlight 3, the Prism library (version 2.0) provided a mechanism that allows any Silverlight control to be associated with a command object using attached behavior. This mechanism also worked in WPF, which allowed the view model implementations to be again in both Silverlight applications and WPF.

The following example shows how Prism uses attached behavior to bind a command object defined on a view model to a button click event.

 <Button Content="Submit All" prism:Click.Command="{Binding Path=SubmitAllCommand}" prism:Click.CommandParameter="{Binding Path=TickerSymbol}" /> 

Silverlight 4 added support for the Command property in all controls inherited from Hyperlink and ButtonBase , allowing them to be tied directly to the command object in the same way as in WPF. The use of the Command property for these controls is described in the " Commands " section in Chapter 5, " Implementing the MVVM Pattern ". However, the attached behavior of the command remains in the Prism library for reasons of backward compatibility and to support the development of custom behaviors, as will be described later.

The behavior approach is a common method for implementing and encapsulating interactive behavior and can be easily applied to controls in a view. Using behaviors to support commands, as shown earlier, is just one of many scenarios that can support behaviors. Microsoft Expression Blend provides many behaviors, including InvokeCommandAction and CallMethodAction , described in the section “Invoking Command Methods from the View” in Chapter 5, “ Implementing the MVVM Pattern ”, and the Expression Blend Behaviors SDK provides the ability to develop custom behaviors. With Expression Blend, you can easily create and edit behaviors, which makes it very easy to add to an application. For more information on developing custom behaviors in Expression Blend, see " Creating Custom Behaviors " on MSDN.

Although the introduction of command-supporting controls in Silverlight 4 and the Expression Blend Behaviors SDK eliminate most of the need for Prism behaviors, their compact syntax and implementation, as well as their easy expansion, can be found useful.

Application of behaviors

Command behaviors are based on an attached behavior pattern. This pattern connects events triggered by controls and command objects provided by the view model. The command behavior consists of two parts: an attached property and an object of behavior. The attached property establishes the relationship between the target control and the behavior object. The behavior object controls the target control and raises events based on events or state changes in the control or view model.

Commands triggered by a Click event in ButtonBase controls are provided by the ButtonBaseClickCommandBehavior class. The following illustration shows the relationship between ButtonBase , ButtonBaseClickCommandBehavior , and the ICommand object provided by the view model.

ButtonClick  IComman.

Your application may need to call commands not only at the Click event of ButtonBase , or it may need to customize how the behavior interacts with the target control or view model with which it is associated. In these cases, you will need to define your own associated property and / or behavior implementation.

The Prism library provides the 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
class 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
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
 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
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
 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
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
 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
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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


All Articles