【Win 10 應用開發】Toast通知激活應用——前台&后台


老周最近熱衷於講故事,接下來還是講故事時間。

有人問我:你上大學的時候,有加入過學生會嗎?讀大學有沒有必要加入學生會?

哎喲,這怎么回答呢,從短期來說,加入學生會有點用,至少可以娛樂一下,運氣好的話,說不定能遇到紅顏知己,但這概率相當低。從長遠發展看嘛,是沒什么用。老周當年讀了四年本科,在學生會混了四年,什么名堂也沒混出來。

一方面老周向來不求虛名,所以也沒去參選所謂的什么部長、主席之類的,這些“官銜”聽起來很高大上,實際上很庸俗蠢。既然沒興趣,因而老周更不需要去搞那些見不得人的勾當,什么勾當?你懂的。其次,老周沒感覺到這些職務,對將來的職業生涯發展有什么幫助,基本上屬於混日子罷了。

老周當初一上大學,本不想進什么學生會天地會之類的,之所以進了,純屬意外,簡直太意外了,事情是這樣的。話說那天學生會招新,盡管老周的母校比較老舊,但校園里還是被各處擺攤招新的學兄學姐們弄得像大觀園似的,熱鬧非常。

誰不知道,大觀園里美女多,於是我宿舍的室友開始動心了,有兩個人鼓起勇氣,決定明天去面試。本來跟我沒什么關系,誰想第二天,又有一位室友感興趣了,他們索性商量全宿舍一起出動。老周本不想去,就被他們硬拉着去。

一伙漫無目的地隨處看,我以為他們是去看美女的,原來他們還真的去面試了,走着走着,我們進入了一個教室,里面坐着幾位師兄和師姐。進去前要在門口登記,寫上個人信息,然后排隊面試(教室里都沒幾個人,排什么隊),我說我不是來面試的,是陪室友來玩玩的。聽我一說完,一位師兄叫我出去走廊外面等。

正當我往外走時,另一位師兄又把我叫回來,說既然來了就面面試吧,沒事的。這時候我才知道,他們是要招書法高手的。老周雖不敢當什么高手,但從小就握過幾年毛筆,不防耍耍。就這樣,老周就莫名其妙地進了學生會。

想來有趣,本來想進的,沒進;我本不想進的,反而進了。真是世事無常,禍福相依啊。

=========================================================

故事講完了,下面說正經話。

話說上一回,老周給大伙伴們介紹了win 10中Toast通知的新型模板,既包含有舊版本的兼容格式,也有用於通用平台的“自適應”通知。前一文章中,老周主要介紹了XML文檔的基本格式,並演示了兩個例子。

今天,咱們探討一下如何在Toast通知中使用交互命令,並通過交互命令激活應用程序。所謂交互命令,就是在Toast通知的界面上添加一些可以讓用戶操作的元素,來與應用程序進行互動。這些交互元素主要有:

1、按鈕。用action元素來指定,用戶點擊按鈕后會激活應用程序,並把用戶所點擊的按鈕的參數作為激活參數傳遞給應用程序。

2、輸入框。就是一個文本框,用戶可以在里面輸入文本。

3、選擇列表。類似於下拉列表框,用戶可以在里面選擇一個項。

這些交互元素都統一放到actions元素下面。

舉個例子,比如這樣:

<toast>
  <visual>
    <binding template="ToastGeneric">
      <text>唱山歌</text>
      <text>一起去唱山歌吧.</text>
      <image placement="appLogoOverride" src="c.png" />
    </binding>
  </visual>
  <actions>
    <action content="報名" arguments="join" imageUri="jn.png" />
    <action content="不去" arguments="cancel" />
  </actions>
</toast>

大家可以想象一下這個Toast通知是什么樣子的,現在不截圖給你看,待會兒咱們做示例時再看。

首先,visual元素在前一篇爛文中講過,是描述toast通知的可視化部分,第一個text被視為標題,所以顯示出來字體較大,第二個text作為正文,顯示一行文本。image元素指定一個圖像,因為設置了placement="appLogoOverride",表明這個圖像會替換應用的默認圖標,顯示在通知的左上角。

 

接下來,重點關注actions元素,actions元素下面專門用來放置交互命令,有兩個元素可用:

<action>:表示定義一個命令按鈕。例如:

<action content = "確定"  arguments = "yes"  activationType ="foreground或background" imageUrl="b.png"  />

content表示要在按鈕上顯示的文本,上面例子在按鈕上顯示“確定”。imageUrl表示顯示在按鈕上的圖標,如果不用圖標,就可以不設置imageUrl屬性。arguments用來指定一個參數,這個參數是你自己定的,比如我這里叫yes,當用戶點擊按鈕后,arguments的值會傳遞給應用程序,這樣應用程序才會知道你到底點擊了哪個按鈕。

activationType指示以何種方式激活應用程序,如果值為foreground,則表明toast通知將在前台激活應用程序,這時候用戶可以看見應用程序;如果值為background,表明toast通知通過后台方式激活應用,此時用戶看不到應用程序,后台激活必須指定一個后台任務,當激活時就會執行后台任務。如果值為protocol,表示將通過協議來激活應用程序。

 

如果希望用戶可以在Toast通知上輸入內容,可以這樣定義XML:

<input id="name" type="text" />

Toast通知上會顯示一個文本框,id屬性是必須指定的,而且必須是actions中的唯一值,不能重復,這個id值在激活應用程序時會傳遞給應用。type屬性指定input元素的類型,text表示文本框,讓用戶輸入文本。如果是selection表示顯示一個列表選擇框,用戶只能從中選擇一個項。

如果type為selection,那么input元素下會包含N個selection元素,每個selection元素表示一個選項,如

<input id="age" type="selection">
    <selection content="五歲" id="5" />
    <selection content="七歲" id="7" />
</input>

上面例子,定義了兩個選項,每個selection的id值必須唯一,它表示該項的值,這個id也會傳遞給被激活的程序。content表示項中顯示的文本,用來給你看的。即當toast彈出時,在下拉列表中,你看到的是五歲、七歲兩個選項。

下面我們來實戰一下,先看Toast通知如何從前台激活應用。

首先構造XML文檔。

            string visual = "<visual>" +
                                "<binding template=\"ToastGeneric\">" +
                                    "<text>宇宙第一應用</text>" +
                                    "<text>應用正在收集您的信息。</text>" +
                                "</binding>" +
                             "</visual>";
            string actions = "<actions>" +
                                "<input id=\"name\" type=\"text\" placeHolderContent=\"請輸入姓名\" />" +
                                "<input id=\"city\" type=\"text\" placeHolderContent=\"請輸入城市\" />" +
                                "<action content=\"確定\" arguments=\"ok\" />" +
                                "<action content=\"取消\" arguments=\"cancel\" activationType=\"foreground\" />" +
                             "</actions>";
            string toastXml = $"<toast>{visual}{actions}</toast>";

 有一點大伙要注意,當input和action元素同時使用時,input元素必須放在action元素的前面。placeHolderContent屬性主要設置占位文本,即當文本框中沒有輸入任何內容時顯示的文本。

上面的Toast通知定義了兩個可供輸入的文本框,以及兩個命令按鈕。

 

下面代碼顯示通知。

            // 加載XML文檔
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(toastXml);
            // 顯示通知
            ToastNotification notification = new ToastNotification(doc);
            ToastNotifier notifier = ToastNotificationManager.CreateToastNotifier();
            notifier.Show(notification);


正因為應用程序是被前台激活的,所以肯定要為Application類作激活處理,在App類中,重寫基類的OnActivated方法。

        protected override void OnActivated(IActivatedEventArgs args)
        {
            // 判斷激活類型
            // 確認是由Toast通知激活應用
            if (args.Kind == ActivationKind.ToastNotification)
            {
                // 轉換參數類型
                ToastNotificationActivatedEventArgs toastargs = (ToastNotificationActivatedEventArgs)args;
                // 獲取頁面引用
                Frame root = Window.Current.Content as Frame;
                if (root == null)
                {
                    root = new Frame();
                    Window.Current.Content = root;
                }
                if (root.Content == null)
                {
                    root.Navigate(typeof(MainPage));
                }
                MainPage page = (MainPage)root.Content;
                // 判斷用戶點擊哪個按鈕
                string activeargs = toastargs.Argument;
                if (activeargs == "ok") //確定按鈕
                {
                    // 獲取用戶輸入的內容
                    string name = toastargs.UserInput["name"] as string;
                    string city = toastargs.UserInput["city"] as string;
                    page.ShowTextFromForeactivation($"用戶點擊了確定按鈕,輸入的內容為:\n姓名:{name}\n城市:{city}。");
                }
                else //取消按鈕
                {
                    page.ShowTextFromForeactivation("用戶點擊了取消按鈕。");
                }
            }
            Window.Current.Activate();
        }

 先要通過方法參數的Kind屬性來判斷一下應用程序是不是被Toast通知所激活的,如果值是ToastNotification,證明應用程序是被Toast通知激活的。

然后要把方法參數的類型轉化為ToastNotificationActivatedEventArgs類型,ToastNotificationActivatedEventArgs類專用於Toast通知的激活。

Argument屬性中取得的值就是被點擊的action的arguments屬性中的值,上面我定義的toast有兩個action,arguments的值分別為ok和cancel,如果用戶點擊了確定按鈕,那么從ToastNotificationActivatedEventArgs對象的Argument屬性得到的值為ok,否則就是cancel。

UserInput屬性獲取的就是input元素的值,屬性類型為ValueSet,實際上是一個字典類型。Key為Toast通知中input元素的id,Value的值是input元素中輸入的內容。通過UserInput屬性,程序就知道用戶在文本框中輸入了什么內容。

運行應用程序,當通知彈出時,輸入相關內容,如下圖。

輸入一些文本后,然后點擊確定按鈕,然后應用程序被激活,會看到主頁面上顯示從Toast通知傳遞到應用程序的輸入數據。如下圖所示。

---------------------------------------------------------------------------------------------------

上面演示的是Toast通知從前台激活應用程序,下面來看看后台激活。Toast通知從后台激活應用程序,用戶不會看到應用程序界面,但應用程序會在后台任務中處理從Toast通知傳遞到應用程序的數據。

要實現從后台處理Toast通知,首先要實現一個后台任務。記住后台任務要在一個獨立的Runtime組件項目中。后台的實現代碼如下:

    public sealed class ToastBgTask : IBackgroundTask
    {
        public async void Run(IBackgroundTaskInstance taskInstance)
        {
            var deferral = taskInstance.GetDeferral();

            ToastNotificationActionTriggerDetail details = taskInstance.TriggerDetails as ToastNotificationActionTriggerDetail;
            if (details != null)
            {
                // 先確認用戶點擊了yes按鈕
                string cmdargs = details.Argument;
                if (cmdargs == "yes")
                {
                    // 獲取選擇的項
                    object value = details.UserInput["ut"];
                    // 保存數據
                    StorageFolder local = ApplicationData.Current.LocalFolder;
                    JsonObject jsobj = new JsonObject();
                    jsobj.SetNamedValue("updatetime", JsonValue.CreateNumberValue(Convert.ToDouble(value)));
                    StorageFile newFile = await local.CreateFileAsync("data.json", CreationCollisionOption.ReplaceExisting);
                    await FileIO.WriteTextAsync(newFile, jsobj.Stringify(), Windows.Storage.Streams.UnicodeEncoding.Utf8);
                }
            }

            deferral.Complete();
        }
    }

當Toast通知激活后台任務后,可以從 taskInstance.TriggerDetails屬性獲取到一個ToastNotificationActionTriggerDetail對象實例。和前台激活一樣,通過Argument屬性可以獲取Toast通知中被用戶點擊的action元素的參數。從UserInput屬性可以獲取到input元素的id值。

后台任務完成后,記得要在主項目中引用,這個老周已經重復了幾千遍了。

然后打開清單文件,在Package/Applications/Application節點下添加后台任務聲明。

    <Extensions>
      <Extension Category="windows.backgroundTasks" EntryPoint="BackgroundTasks.ToastBgTask">
        <BackgroundTasks>
          <Task Type="general"/>
        </BackgroundTasks>
      </Extension>
    <
/
Extensions>

 

由於在后台任務中,是把用戶在Toast通知上選擇的項保存到本地文件中,所以在主應用中,可以讀出文件的內容,並顯示出來。

        protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
            try
            {
                // 讀入后台保存的內容
                StorageFolder local = ApplicationData.Current.LocalFolder;
                StorageFile dataFile = await local.GetFileAsync("data.json");
                if (dataFile == null) return;

                string jsstr = await FileIO.ReadTextAsync(dataFile, Windows.Storage.Streams.UnicodeEncoding.Utf8);

                JsonObject jsobj = null;
                if (JsonObject.TryParse(jsstr, out jsobj))
                {
                    double d = jsobj.GetNamedNumber("updatetime");
                    tbResultFromBackActivate.Text = $"你選擇了每隔{d:N0}天更新一次。";
                }
                await dataFile.DeleteAsync();
            }
            catch(Exception ex)
            {
                Debug.WriteLine($"文件讀取異常:{ex.Message},異常類型:{ex.GetType().Name}。");
            }

        }

 

最后,我們實現顯示Toast通知的代碼。

            string visual = "<visual>" +
                                "<binding template=\"ToastGeneric\">" +
                                    "<text>無敵應用</text>" +
                                    "<text>請選擇更新間隔天數。</text>" +
                                "</binding>" +
                            "</visual>";
            string actions = "<actions>" +
                                "<input id=\"ut\" type=\"selection\" defaultInput=\"5\">"+
                                    "<selection id=\"3\" content=\"3天\" />" +
                                    "<selection id=\"5\" content=\"5天\" />" +
                                    "<selection id=\"10\" content=\"10天\" />" +
                                "</input>" +
                                "<action content=\"是\" activationType=\"background\" arguments=\"yes\" />" +
                                "<action content=\"否\" activationType=\"background\" arguments=\"no\" />" +
                            "</actions>";
            string toastXml = $"<toast>{visual}{actions}</toast>";

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(toastXml);
            ToastNotification notification = new ToastNotification(doc);
            ToastNotifier notifier = ToastNotificationManager.CreateToastNotifier();
            notifier.Show(notification);

 

input元素的type為selection,表示Toast通知上的輸入行為為列表選擇,並使用三個selection元素定義了三個選項。注意,在input元素中,defaultInput屬性指定輸入控件的默認值。如果type為text,該屬性設置默認的文本;在本例中,type為selection,所以默認值就是希望默認被選中的項的id值

 

似乎一切就緒了,實際上我們還有很關鍵一步沒有做——注冊后台任務,清單文件中僅僅是聲明,而要希望讓Toast通知的操作激活后台任務,還需要對后台任務進行注冊。

        private async void RegToastBackgroundTask()
        {
            // 判斷一下是否允許訪問后台任務
            var res = await BackgroundExecutionManager.RequestAccessAsync();
            if (res == BackgroundAccessStatus.Denied || res == BackgroundAccessStatus.Unspecified)
            {
                return;
            }

            Type taskType = typeof(BackgroundTasks.ToastBgTask);
            var task = BackgroundTaskRegistration.AllTasks.Values.FirstOrDefault(t => t.Name == taskType.Name);
            if (task == null)
            {
                // 注冊后台任務
                BackgroundTaskBuilder bd = new BackgroundTaskBuilder();
                bd.Name = taskType.Name;
                bd.TaskEntryPoint = taskType.FullName;
                // 聲明觸發器
                ToastNotificationActionTrigger trigger = new ToastNotificationActionTrigger();
                bd.SetTrigger(trigger);
                task = bd.Register();
            }
        }

后台的觸發器便用 ToastNotificationActionTrigger類型。

 

現在我們來看一下,運行程序,等Toast通知出現后,把程序關了。然后在Toast通知上選擇一個項,然后提交。

 

然后再次啟動應用程序,可以看到結果了。

 

如何,這Win10的Toast通知是不是很牛X呢。由於小妹妹正在拿着我的920玩,所以就不用手機運行了,手機模擬器沒有安裝,大伙兒有興趣的可以耍耍。

示例源碼:http://files.cnblogs.com/files/tcjiaan/toastActivationApp.zip

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM