2020年的UWP(5)——UWP和Desktop Extension的雙向交互
上一篇我們提到了怎麼在Desktop Extension中等待並處理UWP端發出的request。本篇將討論UWP和Desktop Extension雙向交互的場景,即存在從兩端各自發出request,交由對方接受處理。
依然是回顧下之前總結的四個場景分類:
- 執行後立即退出
- 等待request,處理完後退出
- 一或多個request/response周期
- 和UWP程式相同生命周期
這種存在多個request/response周期的場景,具有以下特徵:
- UWP和Desktop Extension兩端雙向通訊
- 通過request傳遞參數
- Desktop Extension端存在用戶交互
- Desktop Extension端滿足條件時退出
該場景示意圖如下:
上圖僅顯示了最簡化的雙向交互流程,在Sample工程中,以互相發文字消息的UWP和WPF窗體舉例。兩個窗體始終保持在前台,也不存在AppServiceConnection被銷毀的問題。
而實際的業務場景中,可能存在複雜的變化。即特徵中提到的「Desktop Extension端滿足條件時退出「這一點。
上圖展示了Sample工程運行時的介面。如何使用Desktop Extension,及建立AppServiceConnection之前的隨筆已解釋過,此處不再提及。僅對本篇的不同之處進行分析。
TwoWayExchange.FrontUWP工程是一個簡單的UWP工程,在MainPage.cs的構造函數中我們通過AppServiceHandler這個幫助類來註冊Connected事件。
public MainPage() { this.InitializeComponent(); if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0)) { AppServiceHandler.Instance.Connected += Instance_Connected; FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync(); } }
該事件會在App.xaml.cs中的OnBackgroundActived方法中被觸發。這也是我們的程式碼,實際和AppServiceConnection建立關聯的起點。
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) { base.OnBackgroundActivated(args); if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails details) { if (details.CallerPackageFamilyName == Package.Current.Id.FamilyName) { var deferral = args.TaskInstance.GetDeferral(); args.TaskInstance.Canceled += (sender, e) => { deferral?.Complete(); }; AppServiceHandler.Instance.OnBackgroundActivated(details); } } }
在觸發Connected事件後,我們在獲得的AppServiceConnection對象上註冊RequestReceived事件,同時保存AppServiceConnection對象以供SendMessage時使用。
private void Instance_Connected(object sender, AppServiceConnectionConnectedEventArgs e) { AppServiceHandler.Instance.Connected -= Instance_Connected; Connection = e.Connection; Connection.RequestReceived += Connection_RequestReceived; } private async void Connection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { var message = args.Request.Message; if (message.TryGetValue("Desktop", out object content)) { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { this.textBoxReceive.Text += $"{content}\n"; }); } } private async void Button_Click(object sender, RoutedEventArgs e) { var valueSet = new ValueSet(); valueSet.Add("UWP", this.textBoxSend.Text); var response = await Connection.SendMessageAsync(valueSet); }
而在Desktop Extension的WPF工程中,幾乎是對稱的程式碼結構,不同之處無非AppServiceConnection源頭是在Desktop端通過OpenAsync方法建立。
public async Task InitializeAsync() { Connection = new AppServiceConnection(); Connection.PackageFamilyName = Package.Current.Id.FamilyName; Connection.AppServiceName = "TwoWayExchangeAppService"; AppServiceConnectionStatus status = await Connection.OpenAsync(); if (status == AppServiceConnectionStatus.Success) { Connection.RequestReceived += Connection_RequestReceived; } } private void Connection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { if (args.Request.Message.TryGetValue("UWP", out object content)) { Dispatcher.Invoke(() => { this.textBoxReceive.Text += $"{content}\n"; }); } } private async void Button_Click(object sender, RoutedEventArgs e) { var message = new ValueSet(); message.Add("Desktop", this.textBoxSend.Text); await Connection.SendMessageAsync(message); }
至於用於打包的Packaging工程,創建及注意事項,和前一篇完全一致,不再贅述。
本篇的重點在於雙向交互,所以在Desktop端注意Windows.mind和System.Runtime.WindowsRuntime兩個文件的引用添加,否則是無法利用AppServiceConnection來通訊的。
雖然本篇給出的示例極為簡單,似沒有實用價值。而在實際開發中,多有遇到UWP端暫時無法實現的UI及功能,諸如不規則的,透明的,或需要錨定的窗體,Windows桌面右下角的Systray等。均可以通過文中提及的方式來實現交互。
Github:
//github.com/manupstairs/UWPSamples/tree/master/UWPSamples/DataExchangeUWP/TwoWayExchange