WP7开发解惑(转载)
很多初学Windows Phone 7开发的朋友经常因为资源文件的BuildAction属性设置不当而导致图片无法显示、多媒体文件无法访问之类的问题。在Windows Phone 7中,资源文件的BuildAction属性通常有Content/Resource/None三个可选值,那么究竟设置为哪一个才合适呢?下面我们就这一问题进行简单的探索。
疑惑1:BuildAction属性设置为Content/Resource/None的资源有何区别?
这个问题我们通过一个简单的测试来解答。为了容易观察,我们选择两个较大的视频文件(每个10M左右)作为资源文件进行测试。
如图所示,我们添加两个视频文件video1.wmv、video2.wmv到项目中的Medias文件夹。
并分别设置其BuildAction属性为Content、Resource。
按F6键编译项目。然后到项目的bin/Debug文件夹中找到编译生成的xap文件,将其后缀改为zip(对Silverlight有所了解的朋友应该清楚,一个Xap文件实际上就是一个zip压缩包)。
用压缩软件打开该zip文件,在其中的Medias文件夹中将看到vedio1.wmv文件以独立文件的形式存在于压缩包中(右图)。
那么vieo2.wmv文件哪里去了呢?注意观察左图中DemoCode1.dll文件的大小---10M多!!!你猜的没错,vieo2.wmv就是被嵌入到了这个dll文件中。
那么设置为None的情况又是如何呢?实际上试过之后你会发现,设置为None的资源文件既不会直接打包在xap文件中,也不会嵌入xap中的dll内,编译过程会完全忽略该资源文件。那么以下的探讨中也将忽略BuildAction设置为None的情况。
那么通过以上测试我们可以总结如下:
- BuildAction属性设置为Conten的文件将被作为独立文件直接打包在xap文件中
- BuildAction属性设置为Resource的文件将被嵌入到xap包中的dll文件内
- BuildAction属性设置为None的文件,将不会存以任何形式在于xap包中
疑惑2:BuildAction选择Content还是Resource?
根据目前的使用经验,简单总结如下。
多数情况下,两种形式都可以使用,但是以下情况使用Content更为便捷:
- 一般情况下,使用Content时,资源文件的URI更为简洁,而Resource相对繁琐一些。
- e.g.如video1.wmv文件通常可通过URI“/Medias/video1.wmv”访问,
而video2.wmv则需要“[项目名];component/Medias/video1.wmv"访问(XAML中除外)
- e.g.如video1.wmv文件通常可通过URI“/Medias/video1.wmv”访问,
- 由于使用Content时,文件是直接打包在项目中,因此对于需要批量打包生成xap文件的场景,只能用Content。
- 对于多媒体文件,务必使用Content形式以取得更佳的性能。
- MSDN文档原文:
“Media processing on Windows Phone 7 is optimized to use files and network streams, but not in-memory streams. This means that any media files included in the application, such as sound effects, should have their Build Action set to Content and notResource.”
“Windows Phone 上的多媒体处理针对文件和网络流做了优化,而内存流没有。这就意味着包含在应用程序中任何媒体文件,如声音效果等,应该设置其BuildAction属性为Content而不是Resource。” - 更详细解释参考WPMind的WP7 Dev 101 【7】 选择BuildAction
- MSDN文档原文:
对于以下场景,使用Resource可能更合适:
- 需要避免异步加载的资源文件应当使用Resource
- e.g.一个典型的场景是Panorama控件的背景图片,如果设置为Content在首次显示页面时会有闪烁。MSDN文档原文:
“If a Panorama control is using an image for the background, its Build Action should be set to Resource; otherwise, it will not appear immediately when the application is first displayed. Setting the Build Action to Content would cause it to be loaded asynchronously.”
“如果Panorama 控件使用图片作为背景,其BuildAction属性应当色号之为Resource;否则,当应用程序手册显示时图片将不会立即呈现。将BuildAction设置为Content属性会导致其异步加载。” - 很多用作背景的图片都和上例相仿
- e.g.一个典型的场景是Panorama控件的背景图片,如果设置为Content在首次显示页面时会有闪烁。MSDN文档原文:
- 使用Resource可以比Content更好的保护自己Xap包中的资源文件(如自己辛苦设计的图片等)。
- 这很容易理解,因为Content时资源文件直接以文件形式存在于Xap包中,别人拿到Xap解压后也就拿到了你的资源文件。而Resource时嵌入在dll文件中,获取相对麻烦些(但也不是完全安全)。
- 需要从C#代码中动态访问的资源文件(如XML文件等),需要设置为Resource。欲了解详情请参考下一课介绍的场景。
扩展知识
实际上,不仅是资源文件可以设置BuildAction属性,VS项目中的所有文件都有BuildAction属性,如xaml文件、cs文件等,只是一般情况下我们不需要改动这些文件的BuildAction属性而已。点此了解更多BuildAction相关介绍。
下载:点此下载DemoCode
很多朋友在论坛发贴抱怨Windows Phone 7中无法将安装文件夹中的文件拷贝到独立存储中。我很理解产生这种需求的来源:很多时候我们希望将自己预先定义好的一些配置文件添加到项目中,然后在用户安装程序后,通过代码将配置文件拷贝到独立存储中;或者在使用一些第三方的基于独立存储的数据库时,也希望能够将预先定义的数据库文件从安装文件夹拷贝到独立存储中。那么这种需求究竟能否实现呢?答案是:完全可以!
疑惑1:如何从安装文件夹拷贝文件到独立存储?
大部分朋友在尝试以常规的文件系统操作的方式来访问安装文件夹中的文件中,都会遇到类似如下的错误:
- Attempt to access the method failed: System.IO.File.Open(System.String, System.IO.FileMode)
- Attempt to access the method failed: System.IO.StreamReader..ctor(System.String, System.Text.Encoding)
也就是读取安装文件夹中文件失败。其实这在Silverlight中是一个正常现象,是Silverligth的安全机制所决定的。在WP7中我们同样无法打破这种安全机制,那么其实我们所讨论的问题的核心,就是如何正常访问安装文件夹中文件?
受安全机制所限,通过文件系统的方式显然是不可能了。但是幸运的是,我们还是有其他变通的方法的,那就是:将要访问的文件设置为资源,然后通过Application.GetResourceStream()方法获得资源文件流。
如要在代码中访问图中所示的“/Data/MyData.txt”文件,可通过如下方法实现。
首先,将文件的BuildAction属性设置为Resource,这样将保证该文件将来会被以资源形式编译到dll中。(关于Resouce和Content的区别及使用,请参考《BuildAction之Content与Resource》一文)。
然后,在代码中就可以通过如下方式取得该文件的文件流。
Stream stream = App.GetResourceStream( new Uri("/DemoCode2;component/Data/MyData.txt", UriKind.Relative)).Stream;
既然文件流获取到了,那么对其进行复制操作就是顺理成章的了。如下代码即可将文件复制到独立存储中。
using (FileStream fileStream = IsolatedStorageFile.GetUserStoreForApplication().OpenFile("MyData.txt", FileMode.Create)) { byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, bytes.Length); fileStream.Write(bytes, 0, bytes.Length); }
当然,通过GetResourceStream方法也可以解决一些其他类似的问题,如读取安装文件夹中文件。以下代码演示了在获得文件流后读取文件内容并显示。
StreamReader reader = new StreamReader(stream); string str = reader.ReadToEnd();
借助GetResourceStream方法,即可通过一种变通的方式解决类似的访问安装文件夹中文件(实际编译后已经不再是文件而是嵌入到了dll中)的问题。
疑惑2:是否BuildAction设置为Content的文件就都无法通过C#代码访问了呢?
目前看来,大多类型文件设置为Content时是无法通过C#代码访问的。目前看来,仅考虑Silverlight库时,大多类型文件设置为Content时是无法通过C#代码访问的。但个别类型文件例外:
(1)图片文件可以通过URI访问
Uri uri = new Uri("/Data/Jellyfish.jpg", UriKind.Relative); BitmapImage bmp = new BitmapImage(uri); image1.Source = bmp;
(2)XML文件可以借助XElement.Load()方法访问
XElement el = XElement.Load("/Data/AllUsers.xml"); textBlock1.Text = el.ToString();
(3)多媒体文件可以通过MediaPlayerElement访问
疑惑3:是否可能修改安装文件夹中文件?
目前看来,无论文件的BuildAction设置为Resource还是Content、无论是何种类型文件,以上两种访问方式都仅限于读取,无法向安装文件夹写入数据。
下载:点此下载DemoCode
PS:目前看来,大多类型文件设置为Content时是无法通过C#代码访问的。目前看来,仅考虑Silverlight库时,大多类型文件设置为Content时是无法通过C#代码访问的
由于目前还没有深入研究XNA,所以上文讨论的都是仅考虑Silverlight,不考虑XNA框架的情况。经过马宁的提醒,发现原来如果借助XNA库的话,Content类型的文件也是可以访问到的,在此做一补充修正,同时感谢马宁的指正^-^。
当安装文件夹中文件BuildAction设置为为Content时,可以借助XNA库中TitleContainer类的OpenStream()方法获得文件流(先要引用Microsoft.Xna.Framework库)。之后可以按照同样的方法进行文件的操作,如读取、复制等。
Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream("Data/MyData2.txt");
也就是说,目前可以通过两种方式访问到安装文件夹中的文件:
(1)当文件为Resource类型时,可通过Application.GetResourceStream方法取得文件流。
(2)当文件为Content类型时,可通过XNA库中的TitleContainer.OpenStream方法获得文件流。
注:SDKhome移动平台开发的交流平台 http://www.sdkhome.com