1. 程式人生 > >從PRISM開始學WPF(九)交互Interaction?

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

The sele 返回 viewmodel control mode design href cut

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

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

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