WP7有约(八):在ListPicker控件的选择页面上播放铃声
Written by Allen Lee
上节课我们在ListPicker控件的选择页面上实现了播放图标的效果,随后sjcxyf同学又发现了新的问题:
我在项目里面添加了一个MP3文件,然后我在页面加了一个MediaElement控件,我在Image_Tap事件里面添加了播放音乐的代码,但是表现出来的结果是当选择页面弹出来之后点击播放图标不能播放音乐,我试图尝试在选择模板里面加入MediaElement控件来实现,但是最后结果还是一样。
在Silverlight for Windows Phone里,MediaElement有一个很特别的限制,你必须把它添加到可视化树,否则它不会播放。假设我们在铃声设置页面上添加一个播放按钮,如图1所示,接着在这个按钮的Click事件处理程序里创建MediaElement对象,然后通过它播放预先添加的铃声(这个铃声文件的Build Action属性的值是Content),如代码1所示。
图 1 铃声设置页面
代码 1 通过MediaElement对象播放铃声
private void Button_Click(object sender, RoutedEventArgs e)
{
var media = new MediaElement();
media.Volume = 1;
media.Source = new Uri(“Windows Logon Sound.wav”, UriKind.Relative);
}
运行这个应用程序,然后单击播放按钮,你会发现什么声音也没有。但如果我们在创建MediaElement对象之后把它添加到LayoutRoot里,如代码2所示,那么单击播放按钮就能听到声音了,因为此时MediaElement对象已在可视化树上了。
代码 2 把MediaElement对象添加到LayoutRoot
private void Button_Click(object sender, RoutedEventArgs e)
{
var media = new MediaElement();
LayoutRoot.Children.Add(media);
media.Volume = 1;
media.Source = new Uri("Windows Logon Sound.wav", UriKind.Relative);
}
这正是为什么我们经常看到别人在XAML里创建MediaElement对象,然后在代码隐藏里调用它的Play方法播放音频。不过,如果你试图把代码2照搬到Image控件的Tap事件处理程序里,你会发现还是听不到声音,为什么呢?
当我们单击ListPicker控件时,它会打开一个新的选择页面,此时,由于铃声设置页面不再可见,不用渲染,Silverlight for Windows Phone会把它从可视化树上摘除,因此,我们在Image控件的Tap事件处理程序里把MediaElement对象添加到铃声设置页面的LayoutRoot里就失去意义了。
那么,如果我们在选择页面的项目模板里创建MediaElement对象又会怎样呢?毫无疑问,这会导致同一个页面存在多个MediaElement对象,而Silverlight for Windows Phone并不支持这种用法,因此这条路是行不通的。
从上面的分析不难看出,MediaElement的最大问题在于它对可视化树的依赖,那么,有没有不依赖于可视化树的解决方案?有的,我们可以借用XNA的SoundEffect:
1) 添加Microsoft.Xna.Framework库的引用,如图2所示。
图 2添加Microsoft.Xna.Framework库的引用
2) 添加XNA命名空间的引用,如代码3所示。
代码 3 XNA命名空间
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
3) 把Image控件的Tap事件处理程序改为代码4所示的那样。
代码 4 通过XNA的SoundEffect播放铃声
private void Image_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
using (var stream = TitleContainer.OpenStream("Windows Logon Sound.wav"))
{
var audio = SoundEffect.FromStream(stream);
FrameworkDispatcher.Update();
audio.Play();
}
e.Handled = true;
}
代码4所做的事情很简单,它以流的形式打开铃声文件,然后用这个流对象创建SoundEffect对象,并更新XNA的组件状态,最后播放铃声。
一般情况下,当我们提到Windows Phone应用程序的存储区域时,很多人会第一时间想到独立存储区,不过,Windows Phone应用程序还有另外一个存储区域,这个区域就是应用程序的安装文件夹,和独立存储区不同的是,安装文件夹里面的内容是只读的。当我们在Visual Studio的属性窗口里把内容文件的Build Action属性的值设为Content时,比如前面那个铃声文件,安装程序会把它们部署到安装文件夹里。事实上,Windows Phone SDK 7.1的LINQ to SQL就通过"appdata"和"isostore"在连接字符串里区分这两种区域。
XNA提供的TitleContainer.OpenStream方法可以用来读取安装文件夹里的内容,如果你的应用是从网上下载铃声的,那么这些铃声将会保存在独立存储区里,这意味着代码4里打开文件流的代码应该换成对应的独立存储访问代码。