Universal App - 處理 File activation
在<Universal App - 處理 URI activation>介紹註冊 App 預計處理的 URI scheme,接下來該篇將介紹
註冊 App 處理特定的 File type,讓 URI scheme 與 File type 均可在同一個 App 支援。
App 可以註冊處理特定的 「File type」,不管是 Store App 或 Phone App:
[注意]
a. Desktop apps 與 Windows Runtime apps 可註冊處理相同的 file type;
=>當用戶選擇了 Windows Runtime apps 為預設執行 app 時,之後每次遇到該 file type 時則會啟動該 app。
b. 建議一個 app 註冊一個 file type,不建議處理所有的 file type (系統 file type 仍是使用系統 App);
=> Windows Phone 8.1 某些 URIs 與 File types 是被系統註冊的,所以加在自己的 App 裡也會被系統忽略;
=>如果 file type 只是 App 內用到的,則不需要特別去註冊為預設的處理者;
c. 註冊了 file type 後,App 需要實作觸發後的功能以符合用戶的期待;
=>例如:.jpg,用戶會預期開啟了 App 會看到該圖示…等;
=>詳細可參考<Guidelines and checklist for file types and URIs>的說明;
往下說明如何透過 package.appxmanifest 註冊要處理的 File type 與 App.cs 要註冊那些對應的事件來回應。
A. 開啟專案的 package.appxmanifest 檔案,註冊 File Type Association:
詳細設定說明如下表:
Type | Field | Description | |||
Property | Display name | 設定當系統收到 File type 後跳出讓用戶選擇的對話框 (Control Panel) 裡 App 顯示的名稱; | |||
Logo | 設定當系統收到 File type 後跳出讓用戶選擇的對話框 (Control Panel) 裡 App 顯示的圖示; | ||||
Info tip | <Programmatic Identifiers>定義該欄位適用於 group of file types。設定之後該 Tip 文字會出現在用戶手指或滑鼠移動至指定的 file types 上時。 | ||||
Name | 為 group of file types 所設定的顯示名稱。該名稱會搭配 Logo、Info tip 與 Edit flags 一致呈現於指定的這些 file types。 名稱建為一個可以跨應用程序更新保持相同的組名。 該值需要是全小寫字(all lower case letters)。 | ||||
Edit flags | Open is safe Always unsafe | Edit flags 指定 group of file types 的編輯標記,可參考<Programmatic Identifiers>的說明。 Edit flags 用於控制當開啟的 file 是不受信任時,它該如何處理。 Open is safe:true:表示打開的文件類型可以為任何下載的文件被安全地調用。 Always unsafe:true:指示自動調用打開動詞的文件類型被禁用。 (原譯) Specify the edit flags for a group of file types. The edit flags control how a file is accessed when it is acquired from an untrusted source. The OpenIsSafe flag indicates that the Open verb for the file type can be safely invoked for any downloaded files. AlwaysUnsafe flag indicates that the option to automatically invoke the Open verb for the file type is disabled. The user can override this attribute through the File Type dialog box. Use of this flag means OpenIsSafe is not respected. It prevents the Never ask me check box from being enabled on the security dialog when opening untrusted files of this type. 這裡的設定要特別注意,因為如果指定的 file types 是比較特殊的,例如:可能會被系統視為安全問題的,如:*.js、*.script、*.exe …,則需要特別來設定該欄位值。 詳細內容可參考<Writing Secure Code>。 | |||
Supported file types | Content type | 定義要支援的 MIME content type。例如: image/jpeg 或其他特定的類型。 以下的類型是不可以設定的: application/force-download, application/octet-stream, application/unknown,application/x-msdownload. | |||
File type | 定義要註冊的 file type。該值需要是全小寫字(all lower case letters)。 格式:.{type}。 以下分別列出在不同系統不可以註冊的 file type: 〉Windows Store apps:
〉Windows Phone apps: 系統內鍵 App 所註冊的 file type:
系統專用的 file type:
| ||||
App settings | Executable | <Application>, The default launch executable for the app. This file must be present in the package. | |||
Entry point | <Application>, The activatable class ID, such as ""Office.Winword.Class". If you specify this attribute, you must also specify theExecutable attribute. If you specify this attribute you must not specify theStartPage attribute. (Not required) | ||||
Start page | <Application>, The default launch HTML page for the app. This file must be present in the package. If you specify this attribute, you cannot specify either theEntryPoint attribute or theExecutable attribute. (Not required) | ||||
Desired view (Windows only) | 設定當 File type 被執行時,該 App 預計開啟多大的畫面空間。 |
如下圖:
設定後的 XML 範例如下:(我將 Name 與 File type 設定為一樣,當然實作時 Name 只是顯示用, File type 才是重點;)
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="AssociationUnApp.Windows.App">
<m2:VisualElements />
<m2:SplashScreen Image="Assets\SplashScreen.png" />
</m2:VisualElements>
<Extensions>
<Extension Category="windows.fileTypeAssociation">
<FileTypeAssociation Name=".pou">
<SupportedFileTypes>
<FileType>.pou</FileType>
</SupportedFileTypes>
</FileTypeAssociation>
</Extension>
</Extensions>
</Application>
</Applications>
B. 建立專用的 icons (proper icons):
由於 App 被用戶選擇為預設處理特定 File type 時,該 App 將會被顯示於各種地方,例如:Default programs control panel、
Windows Explorer ItemsView、context menus、and the Ribbon、File picker 與 Search results on the Start screen。
<How to handle URI activation (XAML) >建議準備適用的圖示與大小來對應不同環境的呈現,如下:
Type | Size |
Windows Store app | 製作 16/32/48/256 pixel versions 這些 size 的 small logo 與 icon。 |
Windows Phone app | 製作 63/129/236 pixel versions 這些 size 的 small logo 與 icon。 |
需要注意命名:
〉for small logo:{file name}.targetsize-{size pixel}.png;
〉for icon:{file name}.tartgetsize-{size pixel}.png;
〉file name 的定義可參考<Association launching sample>;
參考範例如下圖:
C. 註冊處理 OnFileActivated 事件負責處理 file activation:
protected override void OnFileActivated(FileActivatedEventArgs args)
{
// TODO: Handle file activation
// The number of files received is args.Files.Size
// The first file is args.Files[0].Name
}
處理這個事件上,要注意 Windows Phone 的版本:
a. 透過 File Contract 開啟 App 時,用戶如果按下 Back button,應要讓畫面回到 start screen,而不是該 App 以前的內容;
b. 建議建立一個新的 XAML Frame 來服務每一個不同的 activation event 開啟對應的 Page;
c. 利用新的 XAML Frame 來負責那 navigation backstack 將不會存在先前的內容永遠只有一層;
d. 假設不額外建立一個 XAML Frame 來負責 activation event 的話,要記得當被觸發 activation event 時,
在導向新的一個 Page 之前清除存在的 navigation backstack;
當 App 被啟動是因為註冊處理的 file type 被開啟時,則會得到一個這樣的資料物件。它被觸發於 Activated 事件時所傳出的參數,
所以註冊處理的 event handler 將會收到 (對應的是 ActivationKind == File) ;
[注意]
a. Windows store app 其 Activated 的事件類型為:OnFileActivated,如果未 override OnFileActivated 的話,預設是 OnLaunched 事件;
b. 所有 override Application 的相關 activation scenario 都應該呼叫 Windows.Activate 在實作的方法裡;
c. 當觸發 activation 事件後,可以從參數裡得到 StorageFile,請直接使用它,不要企圖去連結該 StorageFile 的 Path 因為它無權限存取;
重要的屬性:
Property | Access type | Description |
CurrentlyShownApplicationViewId | Read-only | Gets the identifier for the currently shown app view. |
Files | Read-only | Gets the files for which the app was activated. |
Kind | Read-only | Gets the activation type. |
NeighboringFilesQuery | Read-only | Gets the neighboring files of the files for which the app was activated. |
PreviousExecutionState | Read-only | Gets the execution state of the app before it was activated. |
SplashScreen | Read-only | Gets the splash screen object that provides information about the transition from the splash screen to the activated app. |
Verb | Read-only | Gets the action associated with the activated file. |
了解如何註冊處理 File type 之後,接下來舉個簡單的範例來說明如何實作:
[範例]
a. 建立一個處理 *.pou 的 File activation,*.pou 裡面是一個 JSON 格式的內容,目的啟動 App 並將內容驗證與讀取出來顯示於畫面;
FileTypeTest.pou 如下的內容:
{
"contact": {
"UserData": [
{
"name": "Pou",
"blog": "http://www.dotblogs.com.tw/pou",
"phone": "+886933120390"
},
{
"name": "Pou1",
"blog": "http://www.dotblogs.com.tw/pou1",
"phone": "+886933120391"
},
{
"name": "Pou2",
"blog": "http://www.dotblogs.com.tw/pou2",
"phone": "+886933120392"
},
{
"name": "Pou3",
"blog": "http://www.dotblogs.com.tw/3",
"phone": "+886933120393"
}
]
}
}
b. 由於 OnActivated 與 OnFileActiavted 這二個事件的觸發情境接近,所以均需要注意 App 的 Lifecycle;
參考<Universal App - 處理 URI activation>將原本 OnLaunched 中建立 rootFrame 與 還原 Suspended 前的邏輯均獨立出來。
接著在 OnFileActivated 事件裡加入獨立出來的「CreateRootFrame()」與「RestoreStatus()」,為了避免 App 啟動時沒有經過 OnLaunched,
造成畫面無法呈現與資料沒有還原的問題。其程式內容如下:
protected override async void OnFileActivated(FileActivatedEventArgs args)
{
//base.OnFileActivated(args);
// 取得觸發的 File,因為範例只有點擊一個檔案,所以直接以 File[0],
// 如果支援用戶選擇多個檔案,記得要處理;
StorageFile kbl = args.Files[0] as StorageFile;
String content = await FileIO.ReadTextAsync(kbl);
Frame rootFrame = CreateRootFrame();
RestoreStatus(args.PreviousExecutionState);
// File type activation 建立開立一個新的 Page
if (rootFrame.Content == null)
{
// 設定啟動畫面為新的 Page
rootFrame.Navigate(typeof(ContactPage), content);
}
else
{
// 判斷是否在處理檔案的畫面:
Page page = rootFrame.Content as Page;
if (page.GetType().Name == "ContactPage")
{
ContactPage act = page as ContactPage;
act.SplitAndBindData(content);
}
else
{
#if WINDOWS_PHONE_APP
// 清掉所有的 Page
rootFrame.BackStack.Clear();
#endif
// 設定啟動畫面為新的 Page
rootFrame.Navigate(typeof(ContactPage), content);
}
}
// Ensure the current window is active
Window.Current.Activate();
}
在 OnFileActivated 事件裡可以從 FileActivatedEventArgs 取得 Files,需注意 App 是否要支援同時開啟多個檔案。
這個範例我將 OnFileActviated 要處理的事情獨立至新的 Page,所以增加一些處理:
1. Windows Phone app 參考 msdn 上的建議去掉所有 backstack 中的內容,直接進入新的 Page;
2. Windows Store app 則沒有清掉 backstack 直接進入;
3. 如果目前用戶操作的畫面剛好新的 Page 時,則直接轉型進行資料的處理。
c. 新 Page:ContactPage.xaml.cs 處理的邏輯:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.Parameter != null)
{
SplitAndBindData(e.Parameter.ToString());
}
}
public void SplitAndBindData(String source)
{
Boolean isOk = false;
try
{
DataContractJsonSerializer tJsonSerial = new DataContractJsonSerializer(typeof(PouFile));
MemoryStream tMS = new MemoryStream(Encoding.UTF8.GetBytes(source));
PouFile fileType = tJsonSerial.ReadObject(tMS) as PouFile;
if (fileType != null)
{
userDatas = fileType.contact.UserData.ToList();
isOk = true;
}
}
catch (Exception)
{
}
if (isOk)
{
lsContacts.ItemsSource = null;
lsContacts.ItemsSource = userDatas;
}
else
{
MessageDialog msg = new MessageDialog("file content is invalidated!");
msg.ShowAsync();
}
}
以上的範例非常容易,僅是針對 File type 被觸發時做了一些內容的處理。如果您的 App 在針對 File type 有需要做帳號管理的話,
不妨記得判斷用戶登入的資料是否存在,如果存在可讓用戶直接處理 file,如果沒有,則可以在獨立的 page 加入登入的邏輯 page,
做一個來回 Page 的切換不需要大量調整架構。
[範例程式]
更多範例的說明可以參考<Association launching sample>。
======
處理 File activation 時對於 Windows Phone 要特別注意,將會需要增加 OnActivated 的事件處理,
因為用戶可能透過 FilePicker 的方式開啟 App,所以要特別處理這一段的部分。
希望對大家有所幫助,謝謝。
References:
〉Auto-launching with file and URI associations (XAML)
〉How to handle file activation (C#/VB/C++) (重要)
〉How to launch the default app for a file (C#/VB/C++)
〉Guidelines for file types and URIs
〉Store Apps 自訂 URI 與 File Association (重要)
〉App contracts and extensions (Windows Runtime apps) (重要)
〉Default Programs (說明用戶在某些特殊條件(URIs,File extensions)預設開啟的 App)
〉File type and URI associations model & Windows 8 和 Windows Server 2012 相容性手冊