Table of contents
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.
Save commands implemented by an instance of the view model of each element, as shown in the following illustration.
CompositeCommand class.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.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.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.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 previouscommandProxyobject provides an instance access to the commands of the compound objectSubmitandCancel, which are defined statically. For more information, see theStockTraderRICommands.csfile.
DelegateCommand and CompositeCommand classes were designed to work with the Prism regions.EditRegion region, and the UI developer wanted to use the TabControl element to place the views in this region.

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.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.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.monitorCommandActivity parameter monitorCommandActivity set to true , the CompositeCommand class shows the following behavior:CanExecute . Returns true only when all active commands can be executed. Child commands that are inactive are not processed.Execute . Executes all active commands. Child commands that are not active are not processed.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.ListBox . The data template, used to show each item, defines the Delete button, which allows the user to delete individual items from the collection.
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.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> 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 ).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. <Button Content="Submit All" prism:Click.Command="{Binding Path=SubmitAllCommand}" prism:Click.CommandParameter="{Binding Path=TickerSymbol}" /> 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.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.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.
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.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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxCommandBehaviorBase , , 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.aspxSource: https://habr.com/ru/post/176869/
All Articles