Silverlight之out of Browser模式
摘要:在Silverlight中所有应用都可以以一种叫做OOB(Out of Browser)模式的方式脱离浏览器运行,在OOB模式下应用程序将获得更多的信任,甚至可以和windows api通信,今天就来看一下如何构建OOB应用。
主要内容:
- 什么是OOB
- 构建OOB应用
- 自定义OOB窗口
- OOB应用升级
一、什么是OOB
Silverlight out of browser从字面理解就是脱离浏览器的应用,是可以安装到本地的运行在浏览器外的应用,是一个具有独立窗口的web应用。在OOB模式下silverlight看起来更像是C/S应用,但是它却具有web应用的特性。这样一来就可以让用户像在C/S系统中一样体验绚丽、丰富的Web特性。相比浏览其中运行silverlight来讲,oob除了运行方式不同之外,还会获得更多的权限信任、更多的本地化内容,这样以来很多浏览器中很难做到甚至无法做到的事情在oob中也同样可以实现。目前很多silverlight应用都支持oob模式,例如pptv剧场版就是一个很好的案例:
二、构建OOB应用
在Visual Studio中构建OOB应用很简单,只需要简单几步设置即可完成。在此之前先看一下没有对项目做OOB设置之前的情况。
简单建一个silverlight应用,运行之后的状态如下:
点击右键发现当前只有一个菜单:
OK,现在对项目进行OOB设置:
在项目中右键Properties,进入Silverlight选项,点击Enable running application out of browser,此时下面的"Out of Browser Setting"按钮将变成可用状态,点击此按钮弹出下面的设置窗口:
然后做如下设置,这里设置了窗口大小、所需图标以及Trust权限(选中该项后安装时需要用户确认):
图标:
设置完成后可以直接启动此项目(SilverlightApplicationOOB)进行调试而不必在对于的web项目(SilverlightApplicationOOB.Web)中运行,效果如下:
这里为了对比不妨在浏览器中查看效果:
可以看到在右键菜单中多了一个选项,点击此选项可以直接将程序安装到计算机上,安装时由于之前设置了Trust所以需要用户确认以及设置开始和桌面快捷方式:
安装完成后就可以直接看OOB下的效果:
为了让用户有更好的体验,使用右键进行安装未免不够专业并且用户体验较差,一般情况下会选择使用程序进行安装控制,代码很简单:
在程序中通过Application.Current.Install()方法进行安装,当然在这之前最好进行状态检测看程序是不是已经在浏览器外运行。另外在安装时也可以使用Application.Current.InstallState跟踪安装状态。有了上面的代码接下来就可在界面放一个按钮,然后在不同的状态下动态给用户不同的提示。
XAML代码:
CS代码:
安装完后如何卸载呢,事实上可以按照一般C/S程序的方式卸载,或者在Web中运行此程序右键进行删除。
三、自定义OOB窗口
在上面或许您已经看到了,OOB设置窗口中有以下窗口风格设置:
这个选项在很多时候是很有用的,上面窗口使用的是默认风格(Default),窗口操作(最大化、最小化、关闭、移动、窗口大小设置)均调用windows api。当然,相应的界面也显得呆板一些,如果你的应用想要拥有炫酷的界面,那么Default或许无法满足你的需求。但是silverlight已经为您预留了接口,后面No Border和Borderless Round Corners就给你无限的遐想空间。下面就来看一下如何自定义一个OOB窗口。
要自定义窗口其实就是要实现最小化、最大化、关闭、窗口移动、窗口大小设置功能,除了窗口大小设置功能之外其他功能均在标题栏实现,而窗口大小的resize操作一般是放到窗口右下角进行的,因此为了便于复用,接下来会创建两个用户控件:TitleBar和ResizeButton。
创建一个silverlight class library项目Cmj.MyWeb.MySilverlight,添加一个UserControl,命名为TitleBar。TitleBar就是标题栏部分,实现最小化、最大化、关闭、窗口移动操作。标题栏主要包括ico、标题文本、最小化按钮、最大化按钮和关闭按钮几部分,这里不妨使用Grid布局将其分为一行五列,分别对应ico、标题和三个按钮。为了使控件在使用时自适应宽度,可以将标题部分所在的列设置为"*",并且注意不要给UserControl设置Width和Height属性,只需要设置DesignWidth和DesignHeight即可。在第一列中放一个Image控件用于显示应用程序图标,在第二列中放一个TextBlock显示标题内容,其余三列放三个Button按钮用于实现窗口最小化、最大化和关闭。然后准备四个图标作为按钮的背景(最大化按钮需要最大化状态和正常状态两个图标):
有了这些之后接下来就需要实现各部分功能了。最小化、最大化只需要设置MainWindow的WindowState即可;关闭时调用Close方法;而拖动操作可以交给Grid来做,在Grid的MouseLeftButtonDown中调用MainWindow的DragMove方法。需要注意的是最大化按钮的图标有两种状态,需要根据不同的状态动态改变图标。
到此为止TitleBar的功能基本上已经实现,但作为一个通用控件最好把一些内容交给使用者来操作以便增加控件灵活性,这里定义Icon、Title、Background属性用户设置标题栏图标、标题内容和标题栏背景。下面是TitleBar前台XAML和后台CS代码:
<UserControl x:Class="Cmj.MyWeb.MySilverlight.MyUserControl.TitleBar" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="24" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="Gray" MouseLeftButtonDown="LayoutRoot_MouseLeftButtonDown"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="22" /> </Grid.RowDefinitions> <Image Grid.Row="0" Grid.Column="0" Name="imgIcon" Width="16" Height="16" Source="/Cmj.MyWeb.MySilverlight;component/Images/cmjLogo.png" /> <TextBlock Grid.Row="0" Grid.Column="1" Margin="5,4" Text="CMJ Title Bar Control" Name="txtblkTitle" HorizontalAlignment="Left" /> <Button Grid.Row="0" Grid.Column="2" Height="18" HorizontalAlignment="Right" Name="btnMiniMize" VerticalAlignment="Top" Width="18" Margin="2,2,2,2" Click="btnMiniMize_Click"> <Image Name="imgMinimize" Source="/Cmj.MyWeb.MySilverlight;component/Images/minizime.png" /> </Button> <Button Grid.Row="0" Grid.Column="3" Height="18" HorizontalAlignment="Left" Name="btnMaxiMize" VerticalAlignment="Top" Width="18" Margin="2,2,2,2" Click="btnMaxiMize_Click"> <Image Name="imgMaximize" Source="/Cmj.MyWeb.MySilverlight;component/Images/maximize.png" /> </Button> <Button Grid.Row="0" Grid.Column="4" Height="18" HorizontalAlignment="Right" Name="btnClose" VerticalAlignment="Top" Width="18" Margin="2,2,5,2" Click="btnClose_Click" > <Image Name="imgClose" Source="/Cmj.MyWeb.MySilverlight;component/Images/close.png" /> </Button> </Grid> </UserControl>using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace Cmj.MyWeb.MySilverlight.MyUserControl { public partial class TitleBar : UserControl { public TitleBar() { InitializeComponent(); } private BitmapImage biNormal = new BitmapImage(new Uri("../Images/maximizeMax.png", UriKind.Relative)); private BitmapImage biMax = new BitmapImage(new Uri("../Images/maximize.png", UriKind.Relative)); public ImageSource Icon { get { return this.imgIcon.Source; } set { this.imgIcon.Source = value; } } public string Title { get { return this.txtblkTitle.Text; } set { this.txtblkTitle.Text = value; } } public Color BackgroundColor { get { return ((SolidColorBrush)this.LayoutRoot.Background).Color; } set { ((SolidColorBrush)this.LayoutRoot.Background).Color = value; } } public new Brush Background { get { return this.LayoutRoot.Background; } set { this.LayoutRoot.Background=value; } } public new double Height { get { return this.LayoutRoot.Height; } private set { this.LayoutRoot.Height = value; } } private void btnMiniMize_Click(object sender, RoutedEventArgs e) { Application.Current.MainWindow.WindowState = WindowState.Minimized; } private void btnMaxiMize_Click(object sender, RoutedEventArgs e) { if (Application.Current.MainWindow.WindowState == WindowState.Maximized) { this.imgMaximize.Source = biMax; Application.Current.MainWindow.WindowState = WindowState.Normal; } else { this.imgMaximize.Source = biNormal; Application.Current.MainWindow.WindowState = WindowState.Maximized; } this.Width = Application.Current.MainWindow.Width; } private void btnClose_Click(object sender, RoutedEventArgs e) { Application.Current.MainWindow.Close(); } private void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Application.Current.MainWindow.DragMove(); } } }
TitleBar在上面已经完成接下来就来实现窗口的resize操作,在项目中再添加一个UserControl命名为ResizeButton,在ResizeButton中放置Image控件来显示拖拽图标,接下来设置UserControl的MouseLeftButtonDown事件调用MainWindow的DragResize方法实现拖拽功能,在MouseEnter和MouseLeave事件中更改鼠标指针为SizeNESW和Arrow,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; namespace Cmj.MyWeb.MySilverlight.MyUserControl { public partial class ResizeButton : UserControl { public ResizeButton() { InitializeComponent(); } private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Application.Current.MainWindow.DragResize(WindowResizeEdge.BottomRight); } private void UserControl_MouseEnter(object sender, MouseEventArgs e) { this.Cursor = Cursors.SizeNESW; } private void UserControl_MouseLeave(object sender, MouseEventArgs e) { this.Cursor = Cursors.Arrow; } } }
两个控件都已经开发完毕,剩下的工作就是如何使用了。在SilverlightApplicationOOB中引用Cmj.MyWeb.MySilverlight的dll。接着在Main.Xaml中添加命名空间:xmlns:cmj="clr-namespace:Cmj.MyWeb.MySilverlight.MyUserControl;assembly=Cmj.MyWeb.MySilverlight"
(控件前缀任意命名,这里命名为cmj),然后在Main.Xaml中定义TitleBar和ResizeButton并设置相关属性:
<UserControl x:Class="SilverlightApplicationOOB.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:cmj="clr-namespace:Cmj.MyWeb.MySilverlight.MyUserControl;assembly=Cmj.MyWeb.MySilverlight" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" Loaded="UserControl_Loaded"> <Grid x:Name="LayoutRoot" Background="#FFCDDBE8"> <Grid.RowDefinitions> <RowDefinition Height="23"></RowDefinition> <RowDefinition Height="*"></RowDefinition> <RowDefinition Height="16"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"></ColumnDefinition> </Grid.ColumnDefinitions> <cmj:TitleBar Grid.Row="0" Title="CMJ Control" Icon="/Cmj.MyWeb.MySilverlight;component/Images/cmjLogo.png" Background="#FFCDDBE8" /> <Grid Grid.Row="1" Name="grdContent" Background="#FFADB9CD" Margin="3,0,3,0"> <TextBlock HorizontalAlignment="Center" Name="txtblkUpdateDescription" Text="Application Update!" VerticalAlignment="Center" Foreground="#FFEF0FDD" FontSize="20"/> </Grid> <cmj:ResizeButton Grid.Row="2" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,3,0"/> </Grid> </UserControl>下面是安装和运行效果:
四、OOB应用升级
上面的OOB应用看起来跟在Web中运行没有两样,又可以以类似于B/S的方式运行,但是大家都知道B/S有一个很多的弊端就是升级问题,那么OOB应用如何升级呢?总不能每次运行程序先删除再安装一次吧?事实上OOB模式下升级工作很简单,只要在程序需要升级时修改AssemblyVersion版本(在项目的AssemblyInfo.cs文件中),然后在程序启动时调用Application.Current.CheckAndDownloadUpdateAsync()方法就能完成更新工作,这里将更新操作写到前面OOBInstall类中。为了便于观看效果,在loaded事件中添加更新操作,接着在浏览器中运行并将之前安装的程序删除重新安装。然后对Main.Xaml做稍许改变(在其中添加一个TextBlock),修改版本信息为,1.0.0.1:
OOBInstall添加更新方法后:
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Net.NetworkInformation; namespace Cmj.MyWeb.MySilverlight.SiverlightOOB { public class OOBInstall { public bool IsInstalled() { if (Application.Current.InstallState == InstallState.Installed) { return true; } else { return false; } } /// <summary> /// 程序安装 /// </summary> public void Install() { if (!Application.Current.IsRunningOutOfBrowser) { if (Application.Current.InstallState != InstallState.Installed) { Application.Current.Install(); } else { MessageBox.Show("程序已安装!","系统提示",MessageBoxButton.OK); } } //调用InstallStateChanged事件将安装状态传递给调用者 Application.Current.InstallStateChanged += new EventHandler(Current_InstallStateChanged); } void Current_InstallStateChanged(object sender, EventArgs e) { //此处调用自定义事件 OnInstalling(sender, new InstallStateArgs()); } //自定义一个事件来修改安装时状态 public delegate void InstallingHandler(object sender,InstallStateArgs e); public event InstallingHandler Installing; public void OnInstalling(object sender, InstallStateArgs e) { if (Installing != null) { Installing(sender,e); } } /// <summary> /// 程序更新(如果需要更细你可以通过修改程序Assembly Version) /// </summary> public void Update() { //网络改变时事件 NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(NetworkChange_NetworkAddressChanged); //判断程序是否在oob下运行并且网络可用 if (Application.Current.IsRunningOutOfBrowser && NetworkInterface.GetIsNetworkAvailable()) { Application.Current.CheckAndDownloadUpdateCompleted += new CheckAndDownloadUpdateCompletedEventHandler(Current_CheckAndDownloadUpdateCompleted); Application.Current.CheckAndDownloadUpdateAsync(); } } void NetworkChange_NetworkAddressChanged(object sender, EventArgs e) { Console.WriteLine("网络发生改变,当前网络状态:{0}", NetworkInterface.GetIsNetworkAvailable().ToString()); } void Current_CheckAndDownloadUpdateCompleted(object sender, CheckAndDownloadUpdateCompletedEventArgs e) { if (e.UpdateAvailable) { MessageBox.Show("程序已更新到最新版本,请重新启动!", "系统提示", MessageBoxButton.OK); } else { if (e.Error != null) { MessageBox.Show("程序更新失败,错误信息如下:"+Environment.NewLine+e.Error.Message, "系统提示", MessageBoxButton.OK); } } } } public class InstallStateArgs:EventArgs { public InstallState State { get { return Application.Current.InstallState; } } } }在MainPage后台代码中调用:
重新编译程序,再启动程序,程序会自动更新,然后提示更细成功:
接着重启应用,会发现程序更新后的效果:
本作品采用知识共享署名 2.5 *许可协议进行许可,欢迎转载,演绎或用于商业目的。但转载请注明来自崔江涛(KenshinCui),并包含相关链接。 |