1. 程式人生 > WINDOWS開發 >從PRISM開始學WPF(九)互動Interaction(彈窗)-更新至Prism7.1

從PRISM開始學WPF(九)互動Interaction(彈窗)-更新至Prism7.1

原文:從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能獲取confirmationconfirmed屬性,後來才發現,自學這個東西,急於求成是不行的。
看下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,也就是,我們可以用他來裝下任何東西。

FinishInteractioninvoke來關閉互動介面,這個很簡單,具體他怎麼關閉的暫時不看了。接下來,我們大概有一個思路了:
首先,先定義好我們需要資料載體類實現INotification,再設計一個usercontrole,他的vm實現IInteractionRequestAware介面,然後在NotificationRequest.Raise的時候使用usercontrole,美滋滋!!!??
我們來通過客製化彈窗,實現從一個字串列表中選擇一個字串,返回給父窗體:

先設計Notification,他並沒有直接實現INotification,而是實現了IConfirmation,IConfirmationINotification的基礎上,添加了一個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";
                });
        }

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