有朋友给老周提出建议:老周,能不能在写博客时讲一下有深度的小故事?技术文章谁不会写。讲一下对人生有启发性的故事会更好。

哎呀,这要求真是越来越高了。好吧,尽量吧,如果有小故事的话,老周在就每次写博客时写出来;如果没有故事可讲,那只能请您原谅了,呵呵。

有人问老周,你每天都玩手机的吗?答案是肯定的,与时俱进嘛,玩是肯定的。不过,老周从不做低头族,虽然玩,但不会一整天都低着头看手机,这样做让人觉得你很没礼貌(如果一个人独处就没关系),也很没情趣。尤其是一堆人在说话时,你再不喜欢讲话也应该插上一两句,老低着头在那里,一来对身体不好,二来也显得不尊重别人。

其实,老周在家独处时,也不会总拿着手机的。我觉得现在的人很奇怪,似乎大家都知道某些事情对身心不好,但就是不知道为什么,明知道有害也要沉迷其中。这大概就是佛家所说的过度执迷了。执着本没什么不好,但执迷就有点物极必反了。

要说现代人到底懂不懂什么是爱,这真的难说,如果对自己都负不起责任的话,不懂得惜爱自己的人,估计也很难去爱别人。生活中很多东西(比如手机)都是我们的工具,我们是要做工具的主人,还是成为工具的奴隶。唉,只有自己心里明白了。究其根本,可能就是因为很多人的精神家园一片苍白的原因吧。

总之,适可而止就不会有什么后患。

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

本文将说一说如何将当前应用程序集成到系统的文件选择器中,为啥会有这个? 因为Windows App不同于传统的桌面应用,大概是为了数据安全的需要,在应用安装后,操作系统会为每一个应用程序分配独立的注册表项,以及独立的存储目录。严格上说,这些属于某个应用程序的“隐私”,是不应该让其他应用程序去访问的。

不过,有时候真的希望某个应用可以将它的本地文件提供给其他应用使用。其实有一种思路就是可以把共用的文件直接存到系统的图片、音乐、视频、文档等库中。当然,如果可以把当前应用程序集成到系统的文件选择器中的话,会让我们处理起来比较灵活,因为从界面到文件,我们开发者都可以自行控制,也可以操作哪些文件希望公开给其他应用程序,或只留给自己使用。

SDK提供了这些集成功能,这个功能在Win 8的时候就有,到了Win 10,就与传统的系统文件对话框融合到一起了。以前在Win 8/8.1的应用里面,是使用独立的全屏的文件选择器的,现在是把新的呈现引擎与传统的Shell窗口结合到一起了。

SDK提供了打开文件对话框和保存文件对话框的集成支持,而且实现原理相近。本文老周只以集成打开文件对话框为例,至于保存文件对话框的集成,有空的话,老周再补写,因为原理相近。

老规矩,先介绍一下要点:

1、要让应用程序可以集成到文件选择器中,需要重写Application类的OnFileOpenPickerActivated方法,当文件选择器激活当前应用时,会调用该方法。

2、从OnFileOpenPickerActivated方法的参数对象的FileOpenPickerUI属性可以获取到一个FileOpenPickerUI实例,后面的所有操作都是在这个FileOpenPickerUI对象上做文章了。因为我们的应用需要提供一个可视化的界面来让用户操作的,所以通常会导航到一个页面,并把FileOpenPickerUI实例作为参数传递过去。

3、要把某个文件添加到选择器的选择结果中,可以调用AddFile方法,方法的第一个参数为文件的标识,这个标识在整个选择列表中必须是唯一的,通常可以用文件名来标识;第二个参数就是要加入到选择结果列表中的文件实例。如果文件选择器是多选,则整个结果列表会返回给调用方,如果是单选,就只返回一个文件实例给调用方。在AddFile之前,请调用CanAddFile方法来检查一下某个文件到底能不能添加到结果列表中,能就返回true,不能就返回false。“命里无时莫强求”,如果不能添加,那你就省省事吧。

4、RemoveFile方法可以从结果列表中删除一个文件,注意只是从选择结果列表中删除而已,不会真的把文件从硬盘中删除。删除时指定文件的标识,这个标识就是刚才AddFile时的标识,为什么标识要唯一,原因就在这里。在删除前,可以用ContainsFile方法查看一下结果列表中有没有要删除的项。

5、重点,很容易忘记,就是要在清单文件中配置相关扩展声明,让应用程序支持文件选择器的集成。

好,抽象的话讲完了,下面就给大伙儿来点不抽象的东东,以缓和一下性情。

我定义了这么个页面,用ListView来显示待用户选择的文件。

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView Name="lvFiles" SelectionMode="Extended" SelectionChanged="OnSelectionChanged" IsMultiSelectCheckBoxEnabled="True">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:FileItem">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Image Margin="2" Width="85" Height="85" Source="{x:Bind Icon}" x:Phase="1"/>
<TextBlock Grid.Row="1" Margin="3" HorizontalAlignment="Center" Text="{x:Bind Name}" x:Phase="0"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListView>
</Grid>

这里我用到了新的绑定扩展标记x:Bind。它与Binding的不同在于,Binding是在运行阶段完成绑定;Bind是在编译时完成绑定。所以这两个家伙的区别就在于开始绑定的一刹那,也就是说,性能的提升在于开始绑定的一瞬间,如果界面上的数据不需要运态改变,后续的运行就不会因为频繁取值而占用CPU时间。

这里要弄清楚的是,Bind只是省去了动态绑定消耗的性能,并不表示它能压缩内存。如果数据量非常大,那没办法了,因为数据在内存中它肯定需要空间来存放的,谁叫你把那么数据放到内存中呢。再说了,大批量数据的加载是考验硬件性能的,像很多国产平板,尤其是那些100块钱以下的山寨板,配置不会高到哪里去,因此,别动不动就上一大堆数据,这很不厚道。如果数据条数很大,可以实现分段加载(预提取)功能,这个功能在SDK有提供,有时间老周给大家演示演示。

上面页面中是使用了绑定,注意在DataTemplate中使用x:Bind时,一定要加上x:DataType,以指定要绑定的数据源的类型,因为Bind默认的相对点是UserControl或者Page,而Binding的相对点是DataContext。因此,在DataTemplate中使用的话,如果不指定DataType,那么Bind们就找不到源对象,因为它是编译时绑定的,所以是强类型的,不能使用动态类型(dynamic),要用动态类型,请用Binding。

x:Phase表示分阶段提取数据,默认为0,即第一阶段,为1表示第二阶段,依此类推。不指定时表明默认值0。在本例中,Image中的图标可能加载得较慢,为了让数据可以马上显示,让TextBlock的文本在第一阶段加载,然后在第二阶段来加载图标。

ListView绑定的是我自定义的类,我用它来封装文件信息。

    public class FileItem : INotifyPropertyChanged
{
StorageFile m_file = null;
BitmapImage m_icon = null; public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propn = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propn));
} public FileItem(StorageFile file)
{
m_file = file;
GetIconAsync();
} /// <summary>
/// 文件名
/// </summary>
public string Name => m_file?.Name;
/// <summary>
/// 关联的文件
/// </summary>
public StorageFile File => m_file;
/// <summary>
/// 图标
/// </summary>
public BitmapImage Icon
{
get { return m_icon; }
private set
{
if (value != m_icon)
{
m_icon = value;
OnPropertyChanged();
}
}
} private async void GetIconAsync()
{
IRandomAccessStream stream = await m_file.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.SingleItem);
Icon = new BitmapImage();
Icon.DecodePixelWidth = ;
await Icon.SetSourceAsync(stream);
stream.Dispose();
}
}

GetIconAsync方法是取得文件的图标。

重写页面的OnNavigatedTo方法,从参数中取得App传递过来的FileOpenPickerUI对象。这里我是在本地目录中生成20个文件文件,来作为演示文件。

        protected override async void OnNavigatedTo(NavigationEventArgs e)
{
// 获取参数
pickerUI = e.Parameter as FileOpenPickerUI;
// 获取本地文件列表
var files = await ApplicationData.Current.LocalFolder.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.DefaultQuery);
if (files.Count == )
{
await CreateFilesAsync();
// 重新获取
files = await ApplicationData.Current.LocalFolder.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.DefaultQuery);
} List<FileItem> items = new List<FileItem>();
foreach (var f in files)
{
items.Add(new FileItem(f));
}
lvFiles.ItemsSource = items;
}

CreateFilesAsync方法是我定义的,用来生成演示的20个文本文件。

        private async Task CreateFilesAsync()
{
int n = ; //文件个数
StorageFolder localfolder = ApplicationData.Current.LocalFolder;
// 创建文件
for (int x = ; x < n; x++)
{
StorageFile file = await localfolder.CreateFileAsync($"{x + 1}.txt", CreationCollisionOption.ReplaceExisting);
Guid g = Guid.NewGuid();
// 写入内容
await FileIO.WriteTextAsync(file, g.ToString());
}
}

随便弄个GUID,写到文本文件中。

因为文件选择操作我交给ListView控件来干活,所以要处理它的SelectionChanged事件,在选择项发生变化后及时管理文件选择结果列表。

        private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// 移除列表
if (e.RemovedItems.Count > )
{
if (pickerUI.SelectionMode == FileSelectionMode.Multiple)
{
for (int i = ; i < e.RemovedItems.Count; i++)
{
FileItem item = e.RemovedItems[i] as FileItem;
// 移除前先判断是否存在目标项
if (pickerUI.ContainsFile(item.Name))
{
pickerUI.RemoveFile(item.Name);
}
}
}
else
{
FileItem item = e.RemovedItems[] as FileItem;
if (pickerUI.ContainsFile(item.Name))
{
pickerUI.RemoveFile(item.Name);
}
}
} // 添加列表
if (e.AddedItems.Count > )
{
// 如果是多选
if (pickerUI.SelectionMode == FileSelectionMode.Multiple)
{
for (int i = ; i < e.AddedItems.Count; i++)
{
FileItem item = e.AddedItems[i] as FileItem;
// 将项添加到被选文件列表
if (pickerUI.CanAddFile(item.File))
{
pickerUI.AddFile(item.Name, item.File);
}
}
}
else //如果是单选
{
FileItem item = e.AddedItems[] as FileItem;
if (pickerUI.CanAddFile(item.File))
{
pickerUI.AddFile(item.Name, item.File);
}
}
} }

接下来,就轮到App类上面做手脚了。重写OnFileOpenPickerActivated方法,取得UI引用,然后导航到我们上面定义的页面。

        protected override void OnFileOpenPickerActivated(FileOpenPickerActivatedEventArgs args)
{
FileOpenPickerUI UI = args.FileOpenPickerUI;
Frame f = Window.Current.Content as Frame;
if (f == null)
{
f = new Frame();
Window.Current.Content = f;
} f.Navigate(typeof(FileListPage), UI); Window.Current.Activate();
}

别忘了,清单文件。打开清单文件,切换到“声明”选项卡。

添加一个“文件打开选取器”,然后在右边配置所支持的文件类型,你可以直接勾选支持任意类型的文件,就像我这样。当然,你可以单独配置所支持的文件类型。文件类型输入时不要带星号,直接.jpg、.txt、.doc这样就行了,不要漏了前面的“.”。

为了让应用程序可以测试,可以在主页面上调用FileOpenPicker来选取一个文件,然后显示文件的内容。

            FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".txt"); StorageFile file = await picker.PickSingleFileAsync();
if (file == null)
{
return;
} string msg = null;
msg += $"文件名:{file.Name}\n";
// 读出内容
string str = await FileIO.ReadTextAsync(file);
msg += $"文件内容:{str}"; tb.Text = msg;

这里面有个奇怪的现象,就是如果用VS来调试运行时,你在文件选择器上无法用鼠标操作,不知道什么原因,可能是VS无法注入系统消息钩子。所以,在测试时,只能通过开始菜单来启动应用程序才能正常操作。

运行后,点击主页上的按钮,打开文件选择器,然后在左边的导航栏中找到你的程序,点击后会激活。

选择文件后,确定,回到应用程序,就能看到选取的文件的内容了。

好,今天的牛皮就吹到这里。

示例代码下载

【Win10 应用开发】集成文件打开选择器的更多相关文章

  1. 背水一战 Windows 10 (94) - 选取器: 自定义文件打开选取器

    [源码下载] 背水一战 Windows 10 (94) - 选取器: 自定义文件打开选取器 作者:webabcd 介绍背水一战 Windows 10 之 选取器 自定义文件打开选取器 示例1.演示如何 ...

  2. [转载]]Java开发如何在线打开Word文件

    此方案使用了PageOffice产品实现在线打开Word文档: 1. 首先从PageOffice官网下载产品开发包,http://www.zhuozhengsoft.com/dowm/ ,下载Page ...

  3. [原创]Java开发如何在线打开Word文件

    此方案使用了PageOffice产品实现在线打开Word文档: 1. 首先从PageOffice官网下载产品开发包,http://www.zhuozhengsoft.com/dowm/ ,下载Page ...

  4. 使用win10自带虚拟光驱打开ISO镜像文件

    使用win10自带虚拟光驱打开ISO镜像文件非常的简单. 工具/原料   电脑 win10系统 方法/步骤   第一种方法,双击ISO文件.打开“我的电脑”,打开所要打开的ISO文件所在的目录,双击要 ...

  5. eclipse开发工具内打开某js文件总是用记事本方式打开的问题

    问题现象: 开发时不知道按到了什么快捷键,导致在某js文件内点击某调用方法时莫名其妙的用记事本方式打开了该js文件,试了几次都是这样.索性将该js文件关掉重新打开,结果双击该文件还是弹出了一个记事本, ...

  6. Win10 UWP开发系列:使用VS2015 Update2+ionic开发第一个Cordova App

    安装VS2015 Update2的过程是非常曲折的.还好经过不懈的努力,终于折腾成功了. 如果开发Cordova项目的话,推荐大家用一下ionic这个框架,效果还不错.对于Cordova.PhoneG ...

  7. Win10/UWP开发—使用Cortana语音与App后台Service交互

    上篇文章中我们介绍了使用Cortana调用前台App,不熟悉的移步到:Win10/UWP开发—使用Cortana语音指令与App的前台交互,这篇我们讲讲如何使用Cortana调用App的后台任务,相比 ...

  8. 【WP 8.1开发】文件选取器的使用方法

    在以往的WP7x/8.0开发中,我们使用选择器可以浏览并打开图片.音频.视频等一些特殊文件,在8.0 SDK中的运行时API(从Win 8 app中移植)尽管提供了Windows.Storage.Pi ...

  9. Win 10 开发中Adaptive磁贴模板的XML文档结构,Win10 应用开发中自适应Toast通知的XML文档结构

    分享两篇Win 10应用开发的XML文档结构:Win 10 开发中Adaptive磁贴模板的XML文档结构,Win10 应用开发中自适应Toast通知的XML文档结构. Win 10 开发中Adapt ...

随机推荐

  1. ASP.NET Core 1.1.0 Release Notes

    ASP.NET Core 1.1.0 Release Notes We are pleased to announce the release of ASP.NET Core 1.1.0! Antif ...

  2. javascript的api设计原则

    前言 本篇博文来自一次公司内部的前端分享,从多个方面讨论了在设计接口时遵循的原则,总共包含了七个大块.系卤煮自己总结的一些经验和教训.本篇博文同时也参考了其他一些文章,相关地址会在后面贴出来.很难做到 ...

  3. Gradle配置APK自动签名完整流程

    转载请注明出处:http://www.cnblogs.com/LT5505/p/6256683.html 一.生成签名 1.命令行生成签名,输入命令keytool -genkey -v -keysto ...

  4. CENTOS 6.5 平台离线编译安装 Mysql5.6.22

    一.下载源码包 http://cdn.mysql.com/archives/mysql-5.6/mysql-5.6.22.tar.gz 二.准备工作 卸载之前本机自带的MYSQL 安装 cmake,编 ...

  5. .NET Core的日志[1]:采用统一的模式记录日志

    记录各种级别的日志是所有应用不可或缺的功能.关于日志记录的实现,我们有太多第三方框架可供选择,比如Log4Net.NLog.Loggr和Serilog 等,当然我们还可以选择微软原生的诊断框架(相关A ...

  6. NET Core-学习笔记(三)

    这里将要和大家分享的是学习总结第三篇:首先感慨一下这周跟随netcore官网学习是遇到的一些问题: a.官网的英文版教程使用的部分nuget包和我当时安装的最新包版本不一致,所以没法按照教材上给出的列 ...

  7. jQuery.Ajax IE8 无效(CORS)

    今天在开发的时候,遇到一个问题,$.get()在 IE8 浏览器不起作用,但 Chrome,Firefox 却是可以的,网上资料很多,最后发现是 IE8 默认不支持 CORS 请求,需要手动开启下: ...

  8. Phantomjs+Nodejs+Mysql数据抓取(2.抓取图片)

    概要 这篇博客是在上一篇博客Phantomjs+Nodejs+Mysql数据抓取(1.抓取数据) http://blog.csdn.net/jokerkon/article/details/50868 ...

  9. java中的内部类总结

    内部类不是很好理解,但说白了其实也就是一个类中还包含着另外一个类 如同一个人是由大脑.肢体.器官等身体结果组成,而内部类相当于其中的某个器官之一,例如心脏:它也有自己的属性和行为(血液.跳动) 显然, ...

  10. 【swift】BlockOperation和GCD实用代码块

    //BlockOperation // // ViewController.swift import UIKit class ViewController: UIViewController { @I ...