DelegateCommand
object and the InvokeCommandBehavior
behavior.
IInteractionRequest
interface and the InteractionRequest<T>
class. The IInteractionRequest
interface defines an interaction start event. The behavior in the view is associated with this interface and subscribes to this event. The InteractionRequest<T>
class implements the IInteractionRequest
interface and defines two Raise
methods for initiating interaction and setting the request context, and also, if desired, a callback delegate.
InteractionRequest<T>
class coordinates the interaction of the view model with the view during the interaction request. The Raise
method allows the view model to initiate an interaction and define a context object (of type T
) and a callback delegate that is called when the interaction ends. The context object allows the view model to transfer data and state to the view, for use during user interaction. If a callback delegate is defined, then the context object will be passed back to the view model during its call. This allows you to send back any changes that occurred during the interaction.
public interface IInteractionRequest { event EventHandler<InteractionRequestedEventArgs> Raised; } public class InteractionRequest<T> : IInteractionRequest { public event EventHandler<InteractionRequestedEventArgs> Raised; public void Raise(T context, Action<T> callback) { var handler = this.Raised; if (handler != null) { handler( this, new InteractionRequestedEventArgs( context, () => callback(context))); } } }
Notification
class is the base class for all context objects. It is used when the interaction request must inform the user about any event that occurred in the application. It provides two properties - Title
and Content
. Usually, this message is one-way, that is, it is assumed that the user will not change the context values ​​during the interaction.
Confirmation
class is inherited from the Notification
class and adds a third property — Confirmed
— used to determine whether the user has confirmed the operation or canceled it. The Confirmation
class is used to perform the MessageBox
style interaction, in which you need to get a yes / no response from the user. You can define your own context class, inherited from the Notification
class, to store the data and states necessary for the interaction.
InteractionRequest<T>
class, the view model must create an instance of this class and set a read-only property to allow the view to bind to this property.
public IInteractionRequest ConfirmCancelInteractionRequest { get { return this.confirmCancelInteractionRequest; } } this.confirmCancelInteractionRequest.Raise( new Confirmation("Are you sure you wish to cancel?"), confirmation => { if (confirmation.Confirmed) { this.NavigateToQuestionnaireList(); } }); }
EventTrigger
provided by Expression Blend can be used to track the events of the start of an interaction, by associating it with an interaction request object defined in the presentation model. However, the Prism library contains its own EventTrigger
, called the InteractionRequestTrigger
, which automatically connects to the appropriate Raised
IInteractionRequest
.
InteractionRequestTrigger
starts the actions specified in it. For Silverlight, the Prism library provides the PopupChildWindowAction
class, which displays a pop-up window to the user. After the child window is displayed, its DataContext
set to the context parameter specified in the request object. Using the ContentTemplate
property, you can define a data template that is used to display the passed context. The title of the popup window is associated with the Title
property of the context object.
InteractionRequestTrigger
and PopupChildWindowAction
, to display a pop-up window for receiving confirmation from the user.
<i:Interaction.Triggers> <prism:InteractionRequestTrigger SourceObject="{Binding ConfirmCancelInteractionRequest}"> <prism:PopupChildWindowAction ContentTemplate="{StaticResource ConfirmWindowTemplate}"/> </prism:InteractionRequestTrigger> </i:Interaction.Triggers> <UserControl.Resources> <DataTemplate x:Key="ConfirmWindowTemplate"> <Grid MinWidth="250" MinHeight="100"> <TextBlock TextWrapping="Wrap" Grid.Row="0" Text="{Binding}"/> </Grid> </DataTemplate> </UserControl.Resources>
Content
property of the context object. After the pop-up window is closed, the context object is passed back to the view model via the callback method, saving all user-modified data. In this example, the Confirmed
property is set to true
if the user clicks the OK
button.
InteractionRequestTrigger
and PopupChildWindowAction
classes can be used as a base for writing your own triggers and actions.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:localInter="clr-namespace:PrismNotifications.Notifications" xmlns:inter="clr-namespace:Microsoft.Practices.Prism.Interactivity.InteractionRequest;assembly=Microsoft.Practices.Prism.Interactivity" xmlns:local="clr-namespace:PrismNotifications" x:Class="PrismNotifications.MainWindow" Title="MainWindow" Height="350" Width="525"> <Window.DataContext> <local:MainWindowsViewModel /> </Window.DataContext> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <i:Interaction.Triggers> <inter:InteractionRequestTrigger SourceObject="{Binding ShowNotificationInteractionRequest}"> <localInter:ShowChildWindowsAction> <DataTemplate DataType="{x:Type inter:Notification}"> <Grid Width="200" Height="150"> <TextBlock Text="{Binding Content}" /> </Grid> </DataTemplate> </localInter:ShowChildWindowsAction> </inter:InteractionRequestTrigger> </i:Interaction.Triggers> <StackPanel HorizontalAlignment="Right" Margin="10" Grid.Row="1"> <Button Command="{Binding ShowNotificationCommand}"> Show notificaiton windows </Button> </StackPanel> </Grid> </Window>
using Microsoft.Practices.Prism.Commands; using Microsoft.Practices.Prism.Interactivity.InteractionRequest; using Microsoft.Practices.Prism.ViewModel; namespace PrismNotifications { /// <summary> /// . /// </summary> public class MainWindowsViewModel : NotificationObject { public MainWindowsViewModel() { ShowNotificationInteractionRequest = new InteractionRequest<Notification>(); ShowNotificationCommand = new DelegateCommand( () => ShowNotificationInteractionRequest.Raise( new Notification { Title = "", Content = "." })); } /// <summary> /// . /// </summary> public InteractionRequest<Notification> ShowNotificationInteractionRequest { get; private set; } /// <summary> /// , <see cref="ShowNotificationInteractionRequest"/>. /// </summary> public DelegateCommand ShowNotificationCommand { get; private set; } } }
Raise
method is called, to which an instance of the Notification
class is passed, with the specified Title
and Content
properties. The Grid
element contains the InteractionRequestTrigger
trigger, which is associated with the ShowNotificationInteractionRequest
property, which is an interaction request. Inside the trigger is placed the ShowChildWindowsAction
action, in which the data template is specified.
using System.Windows; using System.Windows.Interactivity; using System.Windows.Markup; using Microsoft.Practices.Prism.Interactivity.InteractionRequest; namespace PrismNotifications.Notifications { /// <summary> /// . /// </summary> [ContentProperty("ContentDataTemplate")] public class ShowChildWindowsAction : TriggerAction<UIElement> { /// <summary> /// , . /// </summary> public DataTemplate ContentDataTemplate { get; set; } protected override void Invoke(object parameter) { var args = (InteractionRequestedEventArgs) parameter; } } }
TriggerAction<T>
class, where T
is the type of the object to which the trigger joins. Using the ContentPropertyAttribute
attribute, ContentPropertyAttribute
indicate that the ContentDataTemplate
property will be a content property. When an interaction request occurs, the Invoke
method will be called, in which a parameter of the InteractionRequestedEventArgs
type containing the context and the callback delegate will be passed. Let's make it so that when this method is called, a child window is displayed with the header defined in the args.Context.Title
property and the content specified in the args.Context
property. Also, you need to remember to call the callback method (if specified) when closing the window.
protected override void Invoke(object parameter) { var args = (InteractionRequestedEventArgs) parameter; // , , . Window parentWindows = Window.GetWindow(AssociatedObject); // , . var childWindows = new Window { Owner = parentWindows, WindowStyle = WindowStyle.ToolWindow, SizeToContent = SizeToContent.WidthAndHeight, WindowStartupLocation = WindowStartupLocation.CenterOwner, Title = args.Context.Title, Content = args.Context, ContentTemplate = ContentDataTemplate, }; // . childWindows.Closed += (sender, eventArgs) => { if (args.Callback != null) { args.Callback(); } }; // . childWindows.ShowDialog(); }
Panel
is the root element and, if so, add a popup to the collection of its children. If not, we will create a new Grid
and replace the root element with it, adding the existing one to its collection of elements. When you open the popup, we will block the contents of the window, and when you close it, unblock and call the delegate of the callback. When you move a window, the default popup does not move with it, so you must manually force it to update its location. When creating a popup, you can set its property PopupAnimation = PopupAnimation.Fade
and AllowsTransparency = true
, for its smooth appearance and disappearance.
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Interactivity; using System.Windows.Markup; using Microsoft.Practices.Prism.Interactivity.InteractionRequest; namespace PrismNotifications.Notifications { /// <summary> /// . /// </summary> [ContentProperty("ContentDataTemplate")] public class ShowPopupAction : TriggerAction<UIElement> { private Action _callback; private Popup _popup; private ContentControl _popupContent; private Panel _root; /// <summary> /// , . /// </summary> public DataTemplate ContentDataTemplate { get; set; } protected override void OnAttached() { // . Window window = Window.GetWindow(AssociatedObject); if (window == null) { throw new NullReferenceException("Windows is null."); } // , Grid, // - . _root = window.Content as Panel; if (_root == null) { _root = new Grid(); _root.Children.Add((UIElement) window.Content); window.Content = _root; } // . _popupContent = new ContentControl { ContentTemplate = ContentDataTemplate, }; // , . _popup = new Popup { StaysOpen = true, PopupAnimation = PopupAnimation.Fade, AllowsTransparency = true, Placement = PlacementMode.Center, Child = _popupContent, }; _popup.Closed += PopupOnClosed; window.LocationChanged += (sender, a) => UpdatePopupLocation(); _root.Children.Add(_popup); } private void UpdatePopupLocation() { // , // . // . if (!_popup.IsOpen) { return; } const double delta = 0.1; _popup.HorizontalOffset += delta; _popup.HorizontalOffset -= delta; } private void PopupOnClosed(object sender, EventArgs eventArgs) { // . if (_callback != null) { _callback(); } _root.IsEnabled = true; } protected override void Invoke(object parameter) { var args = (InteractionRequestedEventArgs) parameter; _callback = args.Callback; _popupContent.Content = args.Context; // . _root.IsEnabled = false; _popup.IsOpen = true; } } }
<i:Interaction.Triggers> <inter:InteractionRequestTrigger SourceObject="{Binding ShowNotificationInteractionRequest}"> <localInter:ShowPopupAction ContentDataTemplate="{StaticResource popupTemplate}" /> </inter:InteractionRequestTrigger> </i:Interaction.Triggers>
App.xaml
.
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:localInter="clr-namespace:Microsoft.Practices.Prism.Interactivity.InteractionRequest;assembly=Microsoft.Practices.Prism.Interactivity" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" x:Class="PrismNotifications.App" StartupUri="MainWindow.xaml"> <Application.Resources> <DataTemplate DataType="{x:Type localInter:Notification}" x:Key="popupTemplate"> <Border Width="200" Height="150" Background="{StaticResource {x:Static SystemColors.WindowBrushKey}}" BorderBrush="{StaticResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" CornerRadius="2" Padding="5"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <TextBlock Text="{Binding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" /> <Button Content="Close" HorizontalAlignment="Right" Grid.Row="2"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <ei:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Popup}}" PropertyName="IsOpen" Value="False" /> </i:EventTrigger> </i:Interaction.Triggers> </Button> <TextBlock HorizontalAlignment="Center" Text="{Binding Title}" /> </Grid> </Border> </DataTemplate> </Application.Resources> </Application>
Popup
element tree and change its IsOpen
property to false
. This can be done using triggers and actions from the Expression Framework. As a result, we get a pop-up window of the following form:
Source: https://habr.com/ru/post/152003/