原文: 從PRISM開始學WPF(九)互動Interaction?
0x07互動
這是這個系列的最後一篇了,主要介紹了Prism中為我們提供幾種彈窗互動的方式。
Notification通知式
Prism通過InteractionRequest 來實作彈窗互動,它是一個泛型接口,不同的類型對應不同類型的彈窗方式。
在使用
InteractionRequest
的時候需要在,xaml中需要注冊一個Trigger:
<i:Interaction.Triggers>
<prism:InteractionRequestTrigger SourceObject="{Binding NotificationRequest}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True" />
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
Interaction
這裡用到了
Interaction
,他是
i
命名空間裡的東西,那麼
i
是什麼呢?
interactivity
這個是微軟内置的類庫,他提供了一組使用者互動的類,比如我們這裡用到的
EventTrigger
可以用來執行事件觸發的操作。
在使用的時候,先引入
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
或者
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
,然後在xaml中使用他:
<i:Interaction.Triggers>
<i:EventTrigger>
</i:EventTrigger>
</i:Interaction.Triggers>
而 prism:PopupWindowAction 的 IsModal=True意味着彈框不被關閉的時候,父窗體無法使用。我剛搜尋了一下,這個詞的翻譯竟然“模态”。
模态對話框(Modal Dialogue Box,又叫做模式對話框),是指在使用者想要對對話框以外的應用程式進行操作時,必須首先對該對話框進行響應。 如單擊【确定】或【取消】按鈕等将該對話框關閉。
好,接着,我們在code-behind中聲明,使用
INotification
類型:
public InteractionRequest<INotification> NotificationRequest { get; set; }
在command的回調函數中就可以使用NotificationRequest:
NotificationRequest.Raise(new Notification { Content = "Notification Message", Title = "Notification" }, r => Title = "Notified");
最後通過
ConfirmationRequest.Raise()
方法來實作調用彈窗,這裡将
Title
修改為“Notified”。
Confirmation 确認式
跟Notification的使用方法一樣,先注冊Trigger:
<prism:InteractionRequestTrigger SourceObject="{Binding ConfirmationRequest}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True" />
</prism:InteractionRequestTrigger>
然後在使用InteractionRequest的時候使用IConfirmation類型:
public InteractionRequest<IConfirmation> ConfirmationRequest { get; set; }
callback:
ConfirmationRequest.Raise(new Confirmation {
Title = "Confirmation",
Content = "Confirmation Message" },
r => Title = r.Confirmed ? "Confirmed" : "Not Confirmed");
原本一直好奇為什麼
r
能擷取
confirmation
的
confirmed
屬性,後來才發現,自學這個東西,急于求成是不行的。
看下prism的 ConfirmationRequest.Raise()方法:
/// <summary>
/// Fires the Raised event.
/// </summary>
/// <param name="context">The context for the interaction request.</param>
/// <param name="callback">The callback to execute when the interaction is completed.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
public void Raise(T context, Action<T> callback)
{
var handler = this.Raised;
if (handler != null)
{
handler(this, new InteractionRequestedEventArgs(context, () => { if(callback != null) callback(context); } ));
}
}
CustomPopupRequest 客制化
上面的通知和提示窗體,都是内置的,很多時候,我們需要自制一些彈窗來滿足更複雜的使用場景,比如我們通過彈窗來傳遞一些資訊,貼心的Prism同樣為我們準備了一個接口
IInteractionRequestAware
:
//
// Summary:
// The Prism.Interactivity.InteractionRequest.INotification passed when the interaction
// request was raised.
INotification Notification { get; set; }
//
// Summary:
// An System.Action that can be invoked to finish the interaction.
Action FinishInteraction { get; set; }
蛤蛤,
Notification
正是我們需要的東西,再看看他是什麼鬼
//
// Summary:
// Gets or sets the title to use for the notification.
string Title { get; set; }
//
// Summary:
// Gets or sets the content of the notification.
object Content { get; set; }
原來這個被用來傳遞的東西,也有标準,需要一個名字和一個内容,内容是
object
,也就是,我們可以用他來裝下任何東西。
FinishInteraction
用
invoke
來關閉互動界面,這個很簡單,具體他怎麼關閉的暫時不看了。接下來,我們大概有一個思路了:
首先,先定義好我們需要資料載體類實作INotification,再設計一個usercontrole,他的vm實作
IInteractionRequestAware
接口,然後在
NotificationRequest.Raise
的時候使用usercontrole,美滋滋!!!
我們來通過客制化彈窗,實作從一個字元串清單中選擇一個字元串,傳回給父窗體:
先設計Notification,他并沒有直接實作
INotification
,而是實作了
IConfirmation
,
IConfirmation
在
INotification
的基礎上,添加了一個
Confirmed
屬性,來擷取彈窗傳回狀态,是布爾型的(布爾型隻有兩種狀态,很多時候,我需要有戰鬥機一般多的按鈕的時候,他就不夠用了,到時候得重新設計一個枚舉類型的),這裡,我們就直接實作
IConfirmation
(為什麼先是搞了一個接口呢?當然是為了依賴注入啊!依賴注入很難講,以前我也看了很多大佬的資料,但是沒有懂,後來去問大佬,大佬說,你看懂了嗎?我說似懂非懂,他說,那就去看代碼吧,慢慢的就懂了。):
using Prism.Interactivity.InteractionRequest;
namespace UsingPopupWindowAction.Notifications
{
public interface ICustomNotification : IConfirmation
{
string SelectedItem { get; set; }
}
}
接着是我們的實作類(一個list(源),一個string(目标))繼承
Confirmation
實作我們的接口
ICustomNotification
,繼承
Confirmation
是因為他繼承自
Notification
,而
Notification
是實作了
INotification
的,這樣,我們就在我們的類裡不用去實作
INotification
了,其實也可以不用繼承·
Confirmation
·,完全可以自己實作
ICustomNotification
他所有的接口(話說若幹年前我怎麼記得接口不可以被繼承隻能被實作呢?記錯了?):
using Prism.Interactivity.InteractionRequest;
using System.Collections.Generic;
namespace UsingPopupWindowAction.Notifications
{
public class CustomNotification : Confirmation, ICustomNotification
{
public IList<string> Items { get; private set; }
public string SelectedItem { get; set; }
public CustomNotification()
{
this.Items = new List<string>();
this.SelectedItem = null;
CreateItems();
}
private void CreateItems()
{
//add some items
}
}
}
如果不繼承
Confirmation
,則需要添加部分實作:
public bool Confirmed { get ; set ; }
public string Title { get ; set ; }
public object Content { get ; set ; }
接下來設計我們的彈窗(一個清單(顯示源),兩個按鈕,一個取消一個送出(擷取目标)):
<TextBlock Margin="10" TextWrapping="Wrap" FontWeight="Bold">Please select an item:</TextBlock>
<ListBox SelectionMode="Single" Margin="10,0" Height="100" ItemsSource="{Binding Notification.Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}"></ListBox>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button AutomationProperties.AutomationId="ItemsSelectButton" Grid.Column="0" Margin="10" Command="{Binding SelectItemCommand}">Select Item</Button>
<Button AutomationProperties.AutomationId="ItemsCancelButton" Grid.Column="1" Margin="10" Command="{Binding CancelCommand}">Cancel</Button>
</Grid>
彈窗的ViewModel,實作
IInteractionRequestAware
接口,這裡設定了一個
_notification
來接收我們用來傳遞的那個類,這很像MVC裡的Model,他隻是個資料的載體,在每一個指令最後都需要關閉窗體,并且之前對confirmed和我們的SelectedItem進行指派:
依舊省去了大部分代碼,隻看與我們有關的部分
public class ItemSelectionViewModel : BindableBase, IInteractionRequestAware
{
public string SelectedItem { get; set; }
private void CancelInteraction()
{
_notification.SelectedItem = null;
_notification.Confirmed = false;
FinishInteraction?.Invoke();
}
private void AcceptSelectedItem()
{
_notification.SelectedItem = SelectedItem;
_notification.Confirmed = true;
FinishInteraction?.Invoke();
}
public Action FinishInteraction { get; set; }
private ICustomNotification _notification;
public INotification Notification
{
get { return _notification; }
set { SetProperty(ref _notification, (ICustomNotification)value); }
}
}
}
最後就是在Shell裡調用這個客制化彈窗啦,跟之前的就一毛一樣了,将
INotification
IConfirmation
替換成我們的
ICustomNotification
,然後在new的時候使用
CustomNotification
,代碼看上去應該是這個樣子的:
xaml:
<prism:InteractionRequestTrigger SourceObject="{Binding CustomNotificationRequest}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowContent>
<views:ItemSelectionView />
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
viewmodel:
public InteractionRequest<ICustomNotification> CustomNotificationRequest { get; set; }
public DelegateCommand CustomNotificationCommand { get; set; }
public MainWindowViewModel()
{
CustomNotificationRequest = new InteractionRequest<ICustomNotification>();
CustomNotificationCommand = new DelegateCommand(RaiseCustomInteraction);
}
private void RaiseCustomInteraction()
{
CustomNotificationRequest.Raise(new CustomNotification { Title = "Custom Notification" }, r =>
{
if (r.Confirmed && r.SelectedItem != null)
Title = $"User selected: { r.SelectedItem}";
else
Title = "User cancelled or didn't select an item";
});
}
最後一篇了!我會持續修正之前文章裡了解偏頗的地方。謝謝大家!