CachedFileUpdater (快取檔案更新程式) 讓應用程式能為其他應用程式提供的檔案建立「即時同步」關係。當應用程式透過檔案開啟選取器選取某檔案時,提供者可將該檔案標記為需由 CachedFileUpdater 更新 (Local 模式) 或由應用程式自行更新並通知 Updater (Remote 模式)。本文完整示範提供者端與呼叫端的雙向實作。
在 Manifest 宣告「檔案開啟選取器」,支援特定副檔名。啟動時接收 FileOpenPickerActivatedEventArgs,取得 FileOpenPickerUI,呼叫 AddFile 並使用 CachedFileUpdater.SetUpdateInformation 關聯更新資訊。
<Page x:Class="CachedFileUpdaterProvider.MyOpenPicker" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:CachedFileUpdaterProvider" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <TextBlock Name="lblMsg" FontSize="14.667" /> <Button Name="btnPickCachedFileLocal" Content="提供一個由 Local 更新的 CachedFile(由 CachedFileUpdater 更新 CachedFile)" Click="btnPickCachedFileLocal_Click_1" Margin="0 10 0 0" /> <Button Name="btnPickCachedFileRemote" Content="提供一個由 Remote 更新的 CachedFile(由 app 更新 CachedFile)" Click="btnPickCachedFileRemote_Click_1" Margin="0 10 0 0" /> </StackPanel> </Grid> </Page>
using System;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
using Windows.Storage.Pickers.Provider;
using Windows.Storage.Provider;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace CachedFileUpdaterProvider
{
public sealed partial class MyOpenPicker : Page
{
private FileOpenPickerUI _fileOpenPickerUI;
public MyOpenPicker()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var args = (FileOpenPickerActivatedEventArgs)e.Parameter;
_fileOpenPickerUI = args.FileOpenPickerUI;
_fileOpenPickerUI.Title = "自訂檔案開啟選取器";
}
private async void btnPickCachedFileLocal_Click_1(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(@"webabcdCachedFileUpdaterLocal.txt", CreationCollisionOption.ReplaceExisting);
string textContent = "I am webabcd";
await FileIO.WriteTextAsync(file, textContent);
CachedFileUpdater.SetUpdateInformation(file, "cachedFileLocal", ReadActivationMode.BeforeAccess, WriteActivationMode.NotNeeded, CachedFileOptions.RequireUpdateOnAccess);
lblMsg.Text = "選擇的檔案: " + file.Name;
AddFileResult result = _fileOpenPickerUI.AddFile("myFile", file);
}
private async void btnPickCachedFileRemote_Click_1(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync(@"webabcdCachedFileUpdaterRemote.txt", CreationCollisionOption.ReplaceExisting);
string textContent = "I am webabcd";
await FileIO.WriteTextAsync(file, textContent);
CachedFileUpdater.SetUpdateInformation(file, "cachedFileRemote", ReadActivationMode.NotNeeded, WriteActivationMode.AfterWrite, CachedFileOptions.RequireUpdateOnAccess);
lblMsg.Text = "選擇的檔案: " + file.Name;
AddFileResult result = _fileOpenPickerUI.AddFile("myFile", file);
}
}
}
在 Manifest 宣告「快取檔案更新程式」。啟動時接收 CachedFileUpdaterActivatedEventArgs,取得 CachedFileUpdaterUI。處理 FileUpdateRequested (Local 模式顯示 UI 讓使用者確認更新,Remote 模式直接回報 Complete) 與 UIRequested (顯示更新 UI)。
<Page x:Class="CachedFileUpdaterProvider.MyCachedFileUpdater" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:CachedFileUpdaterProvider" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <TextBlock Name="lblMsg" FontSize="14.667" /> <Button Name="btnUpdate" Content="更新 CachedFile(由 CachedFileUpdater 更新 CachedFile)" Click="btnUpdate_Click_1" Margin="0 10 0 0" /> </StackPanel> </Grid> </Page>
using System;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
using Windows.Storage.Provider;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace CachedFileUpdaterProvider
{
public sealed partial class MyCachedFileUpdater : Page
{
private CachedFileUpdaterUI _cachedFileUpdaterUI;
private FileUpdateRequest _fileUpdateRequest;
private CoreDispatcher _dispatcher = Windows.UI.Xaml.Window.Current.Dispatcher;
public MyCachedFileUpdater()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var args = (CachedFileUpdaterActivatedEventArgs)e.Parameter;
_cachedFileUpdaterUI = args.CachedFileUpdaterUI;
_cachedFileUpdaterUI.Title = "快取檔案更新程式";
_cachedFileUpdaterUI.FileUpdateRequested += _cachedFileUpdaterUI_FileUpdateRequested;
_cachedFileUpdaterUI.UIRequested += _cachedFileUpdaterUI_UIRequested;
}
async void _cachedFileUpdaterUI_UIRequested(CachedFileUpdaterUI sender, object args)
{
await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
Windows.UI.Xaml.Window.Current.Content = this;
lblMsg.Text = "FileUpdateStatus: " + _fileUpdateRequest.Status.ToString();
});
}
void _cachedFileUpdaterUI_FileUpdateRequested(CachedFileUpdaterUI sender, FileUpdateRequestedEventArgs args)
{
_fileUpdateRequest = args.Request;
FileUpdateRequestDeferral fileUpdateRequestDeferral = _fileUpdateRequest.GetDeferral();
if (_cachedFileUpdaterUI.UpdateTarget == CachedFileTarget.Local)
{
if (_cachedFileUpdaterUI.UIStatus == UIStatus.Hidden)
{
_fileUpdateRequest.Status = FileUpdateStatus.UserInputNeeded;
fileUpdateRequestDeferral.Complete();
}
}
else if (_cachedFileUpdaterUI.UpdateTarget == CachedFileTarget.Remote)
{
_fileUpdateRequest.Status = FileUpdateStatus.Complete;
fileUpdateRequestDeferral.Complete();
}
}
private async void btnUpdate_Click_1(object sender, RoutedEventArgs e)
{
FileUpdateRequestDeferral fileUpdateRequestDeferral = _fileUpdateRequest.GetDeferral();
await FileIO.AppendTextAsync(_fileUpdateRequest.File, Environment.NewLine + "由 CachedFileUpdater 更新:" + DateTime.Now.ToString());
string fileContent = await FileIO.ReadTextAsync(_fileUpdateRequest.File);
lblMsg.Text = "檔案名: " + _fileUpdateRequest.File.Name;
lblMsg.Text += Environment.NewLine;
lblMsg.Text += "檔案內容: " + fileContent;
_fileUpdateRequest.Status = FileUpdateStatus.Complete;
fileUpdateRequestDeferral.Complete();
btnUpdate.IsEnabled = false;
}
}
}
protected override void OnFileOpenPickerActivated(FileOpenPickerActivatedEventArgs args)
{
var rootFrame = new Frame();
rootFrame.Navigate(typeof(MyOpenPicker), args);
Window.Current.Content = rootFrame;
Window.Current.Activate();
}
protected override void OnCachedFileUpdaterActivated(CachedFileUpdaterActivatedEventArgs args)
{
var rootFrame = new Frame();
rootFrame.Navigate(typeof(MyCachedFileUpdater), args);
Window.Current.Content = rootFrame;
Window.Current.Activate();
}
透過 FileOpenPicker 選取提供者提供的檔案,儲存 FutureAccessList 權杖。Local 模式下讀取檔案會自動觸發 Updater 更新;Remote 模式下呼叫 CachedFileManager.DeferUpdates -> 修改檔案 -> CompleteUpdatesAsync 完成更新並取得狀態。
<Page x:Class="XamlDemo.Picker.CachedFileUpdaterDemo" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Picker" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <TextBlock Name="lblMsg" FontSize="14.667" /> <Button Name="btnGetCachedFile" Content="開啟 Provider 提供的 CachedFile" Click="btnGetCachedFile_Click_1" Margin="0 10 0 0" /> <Button Name="btnReadCachedFile" Content="由 CachedFileUpdater 更新檔案" Click="btnReadCachedFile_Click_1" Margin="0 10 0 0" /> <Button Name="btnWriteCachedFile" Content="由 app 更新檔案" Click="btnWriteCachedFile_Click_1" Margin="0 10 0 0" /> </StackPanel> </Grid> </Page>
using System;
using Windows.Storage;
using Windows.Storage.AccessCache;
using Windows.Storage.Pickers;
using Windows.Storage.Provider;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace XamlDemo.Picker
{
public sealed partial class CachedFileUpdaterDemo : Page
{
private string _cachedFileToken;
public CachedFileUpdaterDemo()
{
this.InitializeComponent();
}
private async void btnGetCachedFile_Click_1(object sender, RoutedEventArgs e)
{
if (XamlDemo.Common.Helper.EnsureUnsnapped())
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.FileTypeFilter.Add(".txt");
StorageFile file = await openPicker.PickSingleFileAsync();
if (file != null)
{
_cachedFileToken = StorageApplicationPermissions.FutureAccessList.Add(file);
string fileContent = await FileIO.ReadTextAsync(file);
lblMsg.Text = "檔案名: " + file.Name;
lblMsg.Text += Environment.NewLine;
lblMsg.Text += "檔案內容: " + fileContent;
}
else
{
lblMsg.Text = "取消了";
}
}
}
private async void btnReadCachedFile_Click_1(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(_cachedFileToken))
{
StorageFile file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(_cachedFileToken);
string fileContent = await FileIO.ReadTextAsync(file);
lblMsg.Text = "檔案名: " + file.Name;
lblMsg.Text += Environment.NewLine;
lblMsg.Text += "檔案內容: " + fileContent;
}
}
private async void btnWriteCachedFile_Click_1(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(_cachedFileToken))
{
StorageFile file = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(_cachedFileToken);
CachedFileManager.DeferUpdates(file);
await FileIO.AppendTextAsync(file, Environment.NewLine + "由 app 更新:" + DateTime.Now.ToString());
FileUpdateStatus status = await CachedFileManager.CompleteUpdatesAsync(file);
lblMsg.Text = status.ToString();
lblMsg.Text += Environment.NewLine;
if (status == FileUpdateStatus.Complete)
{
string fileContent = await FileIO.ReadTextAsync(file);
lblMsg.Text += "檔案名: " + file.Name;
lblMsg.Text += Environment.NewLine;
lblMsg.Text += "檔案內容: " + fileContent;
}
}
}
}
}
contentId 識別同一檔案的不同版本。