天天看點

從PRISM開始學WPF(九)互動Interaction?

原文: 從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";
                });
        }           

最後一篇了!我會持續修正之前文章裡了解偏頗的地方。謝謝大家!