從PRISM開始學WPF(九)互動Interaction(彈窗)-更新至Prism7.1
0x07互動
[7.1updated]無變化
這是這個系列的最後一篇了,主要介紹了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";
});
}
最後一篇了!我會持續修正之前文章裡理解偏頗的地方。謝謝大家!