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 previouscommandProxy
object provides an instance access to the commands of the compound objectSubmit
andCancel
, which are defined statically. For more information, see theStockTraderRICommands.cs
file.
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.aspx
CommandBehaviorBase , , ICommand
. CanExecuteChanged
, Silverlight, WPF.
, , CommandBehaviorBase , . , . , . ButtonBaseClickCommandBehavior
.
public class ButtonBaseClickCommandBehavior : CommandBehaviorBase<ButtonBase> { public ButtonBaseClickCommandBehavior(ButtonBase clickableObject) : base(clickableObject) { clickableObject.Click += OnClick; } private void OnClick(object sender, System.Windows.RoutedEventArgs e) { ExecuteCommand(); } }
CommandBehaviorBase , . , , . , , , , , CanExecute
, .
, . XAML. . . Prism , , , . , . , , , Click
, Command
. Click.Command
, .
. .
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached( "Command", typeof(ICommand), typeof(Click), new PropertyMetadata(OnSetCommandCallback)); private static readonly DependencyProperty ClickCommandBehaviorProperty = DependencyProperty.RegisterAttached( "ClickCommandBehavior", typeof(ButtonBaseClickCommandBehavior), typeof(Click), null);
Command
ButtonBaseClickCommandBehavior
, OnSetCommandCallback
, .
private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { ButtonBase buttonBase = dependencyObject as ButtonBase; if (buttonBase != null) { ButtonBaseClickCommandBehavior behavior = GetOrCreateBehavior(buttonBase); behavior.Command = e.NewValue as ICommand; } } private static void OnSetCommandParameterCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { ButtonBase buttonBase = dependencyObject as ButtonBase; if (buttonBase != null) { ButtonBaseClickCommandBehavior behavior = GetOrCreateBehavior(buttonBase); behavior.CommandParameter = e.NewValue; } } private static ButtonBaseClickCommandBehavior GetOrCreateBehavior(ButtonBase buttonBase ) { ButtonBaseClickCommandBehavior behavior = buttonBase.GetValue(ClickCommandBehaviorProperty) as ButtonBaseClickCommandBehavior; if ( behavior == null ) { behavior = new ButtonBaseClickCommandBehavior(buttonBase); buttonBase.SetValue(ClickCommandBehaviorProperty, behavior); } return behavior; }
, Attached Properties Overview MSDN.
. , Silverlight, -, , , . , .
, ( ) , , . UI , UI.
-
- , IAsyncResult
. , , , GetQuestionnaire
, : BeginGetQuestionnaire
EndGetQuestionnaire
. , BeginGetQuestionnaire
. , , EndGetQuestionnaire
.
.NET Framework 4.5 await
async
*Async
. "Asynchronous Programming with Async and Await (C# and Visual Basic)" .
, EndGetQuestionnaire
, , () BeginGetQuestionnaire
. , , EndGetQuestionnaire
, .
IAsyncResult asyncResult = this.service.BeginGetQuestionnaire( GetQuestionnaireCompleted, null // , ); private void GetQuestionnaireCompleted(IAsyncResult result) { try { questionnaire = this.service.EndGetQuestionnaire(ar); } catch (Exception ex) { // - . } }
, End*
( , EndGetQuestionnaire
), , . , , UI. , , .
UI, -, UI, UI, Dispatcher
SynchronizationContext
. WPF Silverlight, Dispatcher
.
Questionnaire
, QuestionnaireView
. Silverlight CheckAccess
, , UI. , BeginInvoke
, UI.
var dispatcher = System.Windows.Deployment.Current.Dispatcher; if (dispatcher.CheckAccess()) { QuestionnaireView.DataContext = questionnaire; } else { dispatcher.BeginInvoke(() => { Questionnaire.DataContext = questionnaire; }); }
MVVM RI , , IAsyncResult
, . , . , .
this.questionnaireRepository.GetQuestionnaireAsync( result => { this.Questionnaire = result.Result; });
result
, , , , . , .
this.questionnaireRepository.GetQuestionnaireAsync( result => { if (result.Error == null) { this.Questionnaire = result.Result; ... } else { // Handle error. } })
, . , , . , , , .
, MVVM, , . , -MVVM , MessageBox
code-behind UI, . MVVM , .
MVVM, , . , , , , .
MVVM. , , , , . , , , . .
. , . , . .
, , , . , . , . , WPF Silverlight, .

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

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

, MessageBox
, , , .
var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK); if (result == MessageBoxResult.Yes) { CancelRequest(); }
, - , , Silverlight. . . .
interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK, result => { if (result == MessageBoxResult.Yes) { CancelRequest(); } });
, . , WPF MessageBox
, ; Silverlight , .
– . , . , . , , , .

, , – , , – . , , , UI .
MVVM, , , . , , , .
Prism IInteractionRequest
InteractionRequest . IInteractionRequest
, . , . InteractionRequest IInteractionRequest
Raise
, , , , .
InteractionRequest . Raise
( T
) , , . , . , . , , .
public interface IInteractionRequest { event EventHandler<InteractionRequestedEventArgs> Raised; } public class InteractionRequest<T> : IInteractionRequest { public event EventHandler<InteractionRequestedEventArgs> Raised; public void Raise(T context, Action<T> callback) { var handler = this.Raised; if (handler != null) { handler( this, new InteractionRequestedEventArgs(context, () => callback(context))); } } }
Prism , . Notification
. , . – Title
Content
, . , , , , .
Confirmation
Notification
– Confirmed
, , , . Confirmation
, MessageBox
, / . , Notification
, , .
InteractionRequest , InteractionRequest , . , Raise
, , , .
public IInteractionRequest ConfirmCancelInteractionRequest { get { return this.confirmCancelInteractionRequest; } } this.confirmCancelInteractionRequest.Raise( new Confirmation("Are you sure you wish to cancel?"), confirmation => { if (confirmation.Confirmed) { this.NavigateToQuestionnaireList(); } }); }
MVVM Reference Implementation (MVVM RI) , IInteractionRequest
InteractionRequest , (. QuestionnaireViewModel.cs
).
, . , . UI .
. Microsoft Expression Blend Behaviors Framework . , , .
EventTrigger
, Expression Blend, , , , . , Prism EventTrigger
, InteractionRequestTrigger
, Raised
IInteractionRequest
. XAML .
, , InteractionRequestTrigger
. Silverlight Prism PopupChildWindowAction
, . , . ContentTemplate
PopupChildWindowAction
, , UI, Content
. Title
.
, , PopupChildWindowAction
, . Notification
NotificationChildWindow
, Confirmation
– ConfirmationChildWindow
. NotificationChildWindow
, , ConfirmationChildWindow
OK Cancel , . , , ChildWindow
PopupChildWindowAction
.
, InteractionRequestTrigger
PopupChildWindowAction
, RI MVVM .
<i:Interaction.Triggers> <prism:InteractionRequestTrigger SourceObject="{Binding ConfirmCancelInteractionRequest}"> <prism:PopupChildWindowAction ContentTemplate="{StaticResource ConfirmWindowTemplate}"/> </prism:InteractionRequestTrigger> </i:Interaction.Triggers> <UserControl.Resources> <DataTemplate x:Key="ConfirmWindowTemplate"> <Grid MinWidth="250" MinHeight="100"> <TextBlock TextWrapping="Wrap" Grid.Row="0" Text="{Binding}"/> </Grid> </DataTemplate> </UserControl.Resources>
ContentTemplate
, UI Content
. Content
, , TextBlock
Content
.
, , , . , , , , . , RI MVVM , Confirmed
Confirmation
true
, OK.
, . Prism InteractionRequestTrigger
PopupChildWindowAction
.
MVVM, , , , . ( , , , ). , , , .
, . Managed Extensibility Framework (MEF) Unity Application Block (Unity) , , .
, , , , ( ) . , , . , , .
, MEF
MEF, , Import
, , , Export
. , .
, QuestionnaireView
RI MVVM , , Import
. , MEF . set
, .
[Import] public QuestionnaireViewModel ViewModel { set { this.DataContext = value; } }
, .
[Export] public class QuestionnaireViewModel : NotificationObject { ... }
, .
public QuestionnaireView() { InitializeComponent(); } [ImportingConstructor] public QuestionnaireView(QuestionnaireViewModel viewModel) : this() { this.DataContext = viewModel; }
MEF, Unity. , , . , Visual Studio Expression Blend, , , . , , , , InitializeComponent
.
, Unity
Unity, , MEF. , . , . , .
, , , . , , .
public QuestionnaireView() { InitializeComponent(); } public QuestionnaireView(QuestionnaireViewModel viewModel) : this() { this.DataContext = viewModel; }
, , Visual Studio Expression Blend.
, , . Unity set
, .
public QuestionnaireView() { InitializeComponent(); } [Dependency] public QuestionnaireViewModel ViewModel { set { this.DataContext = value; } }
Unity.
container.RegisterType<QuestionnaireViewModel>();
, .
var view = container.Resolve<QuestionnaireView>();
,
, , . , MEF Unity, .
. , UI, .
, RI MVVM , , . , . ShowView
UI.
private void NavigateToQuestionnaireList() { // "questionnaire list". this.uiService.ShowView(ViewNames.QuestionnaireTemplatesList); }
UI UI . UI. ShowView
UIService
(, ), , .
public void ShowView(string viewName) { var view = this.ViewFactory.GetView(viewName); this.MainWindow.CurrentView = view; }
Prism . , , , . , , " View-Based Navigation " 8, " Navigation ".
MVVM
MVVM . - mocking . , , .
INotifyPropertyChanged
INotifyPropertyChanged
, . , ; , , , , .
, , PropertyChanged
, . , ChangeTracker
, MVVM, , . . , .
var changeTracker = new PropertyChangeTracker(viewModel); viewModel.CurrentState = "newState"; CollectionAssert.Contains(changeTracker.ChangedProperties, "CurrentState");
, , INotifyPropertyChanged
, , , .
, , set
, . , , , , .
var changeTracker = new PropertyChangeTracker(viewModel); var question = viewModel.Questions.First() as OpenQuestionViewModel; question.Question.Response = "some text"; CollectionAssert.Contains(changeTracker.ChangedProperties, "UnansweredQuestions");
INotifyPropertyChanged
, PropertyChanged
, , , , . , .
INotifyDataErrorInfo
, , , , IDataErrorInfo
, ( Silverlight) INotifyDataErrorInfo
. INotifyDataErrorInfo
, , , , , .
INotifyDataErrorInfo
: , , , , ErrorsChanged
, GetErrors
, .
, , . , GetErrors
, , , . , , , , . , .
// . var notifyErrorInfo = (INotifyDataErrorInfo)question; question.Response = -15; Assert.IsTrue(notifyErrorInfo.GetErrors("Response").Cast<ValidationResult>().Any()); // . var notifyErrorInfo = (INotifyDataErrorInfo)question; question.Response = 15; Assert.IsFalse(notifyErrorInfo.GetErrors("Response").Cast<ValidationResult>().Any());
, , .
INotifyDataErrorInfo
GetErrors
, INotifyDataErrorInfo
, ErrorsChanged
GetErrors
. , HasErrors
, .
INotifyDataErrorInfo
. , , , - . , INotifyDataErrorInfo
(, ).
, , :
HasErrors
. , . ErrorsChanged
, , GetErrors
. ( , ) , . GetErrors
ErrorsChanged
.
INotifyPropertyChanged
, , NotifyDataErrorInfoTestHelper
MVVM, INotifyDataErrorInfo
, . , , , . .
var helper = new NotifyDataErrorInfoTestHelper<NumericQuestion, int?>( question, q => q.Response); helper.ValidatePropertyChange( 6, NotifyDataErrorInfoBehavior.Nothing); helper.ValidatePropertyChange( 20, NotifyDataErrorInfoBehavior.FiresErrorsChanged | NotifyDataErrorInfoBehavior.HasErrors | NotifyDataErrorInfoBehavior.HasErrorsForProperty); helper.ValidatePropertyChange( null, NotifyDataErrorInfoBehavior.FiresErrorsChanged | NotifyDataErrorInfoBehavior.HasErrors | NotifyDataErrorInfoBehavior.HasErrorsForProperty); helper.ValidatePropertyChange( 2, NotifyDataErrorInfoBehavior.FiresErrorsChanged);
MVVM, , . , , .
, , , . , , , , , IAsyncResult
, - , , UI.
, , , . , . , UI, , , , , " UI."
, , . , , mock-, , , , , .
UI, , . , , , . , . , , .
questionnaireRepositoryMock .Setup( r => r.SubmitQuestionnaireAsync( It.IsAny<Questionnaire>(), It.IsAny<Action<IOperationResult>>())) .Callback<Questionnaire, Action<IOperationResult>>( (q, a) => callback = a); uiServicemock .Setup(svc => svc.ShowView(ViewNames.QuestionnaireTemplatesList)) .Callback<string>(viewName => requestedViewName = viewName); submitResultMock .Setup(sr => sr.Error) .Returns<Exception>(null); CompleteQuestionnaire(viewModel); viewModel.Submit(); // UI . callback(submitResultMock.Object); // – . Assert.AreEqual(ViewNames.QuestionnaireTemplatesList, requestedViewName);
, .
, . "Trees in WPF" MSDN: http://msdn.microsoft.com/en-us/library/ms753391.aspx
, . "Attached Properties Overview" MSDN: http://msdn.microsoft.com/en-us/library/cc265152(VS.95).aspx
MEF, ."Managed Extensibility Framework Overview" MSDN: http://msdn.microsoft.com/en-us/library/dd460648.aspx .
Unity, ."Unity Application Block" MSDN: http://www.msdn.com/unity .
DelegateCommand
, .Chapter 5, " Implementing the MVVM Pattern ."
Microsoft Expression Blend, ."Working with built-in behaviors" MSDN: http://msdn.microsoft.com/en-us/library/ff724013(v=Expression.40).aspx .
Microsoft Expression Blend, ."Creating Custom Behaviors" MSDN: http://msdn.microsoft.com/en-us/library/ff724708(v=Expression.40).aspx .
Microsoft Expression Blend, ."Creating Custom Triggers and Actions" MSDN: http://msdn.microsoft.com/en-us/library/ff724707(v=Expression.40).aspx .
WPF Silverlight, ."Threading Model" "The Dispatcher Class" MSDN: http://msdn.microsoft.com/en-us/library/ms741870.aspx
http://msdn.microsoft.com/en-us/library/ms615907(v=VS.95).aspx .
unit- Silverlight, ."Unit Testing with Silverlight 2": http://www.jeff.wilcox.name/2008/03/silverlight2-unit-testing/ .
, . " View-Based Navigation " in Chapter 8, " Navigation ."
, , ."Event-based Asynchronous Pattern Overview" MSDN: http://msdn.microsoft.com/en-us/library/wewwczdw.aspx
IAsyncResult
, ."Asynchronous Programming Overview" MSDN: http://msdn.microsoft.com/en-us/library/ms228963.aspx
Source: https://habr.com/ru/post/176869/
All Articles