在silverlight开发涉及编程细节地方较多.因通常章节过小 而又无法单独拿出来逐一讲述. 另外版本更新等问题对同一操作出现差异等,本篇主要目的是把开发遇到问题解决方法和技巧加以总结放在一起. 主要是日常编程设计细节. 简介实用.并不间断实时更新.
(1)Silverlight 4.0 Out of Brower操作:
out of Brower是3.0版本的新特性. 3.0中需要单独配置silverlight Application标识信息. 比较麻烦. 而4.0直接找到项目属性-Silverlight分栏下设置:
启用Run out of Browser. 可以在下面按钮设置 具体配置:
保存. 运行项目即可看见多个提示.
(2)Silverlight利用独立存储用户登录信息:
Silverlight 为客户端,没有了WebForm中服务器端我们常用的会话Session.登录时存储动态用户信息,Cookie可以本地存储,减少开销服务端资源.Silverlight的独立存储恰恰很想WebFrom中Cookie. 因为Application安全考虑. silverlight不允许直接操作本地的文件系统. 但是独立存储机制却提供一个虚拟目录方式访问本地文件系统. 当然silverlight也提供了操作这个虚拟空间的对象和方法. 每个Silverlight Application应用程序都硬盘分配自己独立存储空间. 默认大小为1M.也可根据项目需要修改大小.Isolated Storaged根据应用程序作用域不同分为: 应用程序和站点两种. 使用时分别用IsolatedStorageFile.GetUserStoreForApplication() 和IsolatedStorageFile.GetUserStoreForSite().
对于用户信息建议使用IsolatedStorageSetting对象独立存储配置以键值对方式存储. 使用如下:
用户登录时存储需要引用空间:
- //测试发现普通的Class libiry无法正常引用到SL项目必须用Sl Class libiry
- using System.IO.IsolatedStorage;
- using System.IO;
存储登录信息:
- //利用独立存储保存用户登录数据 使用独立存储配置保存key value值
- IsolatedStorageSettings userset =IsolatedStorageSettings.ApplicationSettings;
- //先清空
- userset.Clear();
- userset.Add("username",getCurLoginuser.LoginName);//以键值方式key value
- userset.Add("password",getCurLoginuser.LoginPass);
- userset.Add("uid", getCurLoginuser.Uid);
- userset.Save();//保存进入
在需要地方获取:
- //获得当前登录用户信息
- IsolatedStorageSettings getcuruser = IsolatedStorageSettings.ApplicationSettings;
- this.userInfo.Text = "欢迎:" + getcuruser["username"].ToString();//通过Setting方法 简单获取
- this.date.Text = DateTime.Now.ToLongDateString();
如上一种简单方式实现.其实如上方式.虽然能存储用户登录信息. 但有有一个缺点:用户在退出的时候必须主动删除才行,否则会一直保存存储空间中,用户下次访问时 无法访问到页面.
换一种方式我们把用户信息尝试放到Application.Current.Resource中,但又出现一个问题保存后刷新页面程序会重新加载App.原来保存用户信息也就丢失了.这种方式也不可取 类似在Cookie中可以设置Cookie的存储周期以及过期时间. 同理 在IsolatedStorage独立存储中叶可以设置过期时间.
(3)Unhandled Error in Silverlight Application Code:4004异常处理:
4004这个异常Code编号 在SL2.0甚是常见.版本得到完善后.今天在处理大批量数据时 SL4中又碰到这个4004Code.
异常出现场景:本来一个统计信息表中仅有几十条数据,下午批量增加10W当量数据. 在刚运行程序立马报出4004Code异常 具体信息如下:
Line: 56
Error: Unhandled Error in Silverlight Application
Code: 4004
Category: ManagedRuntimeError
Message: System.InvalidOperationException: Only one WebContextBase can be created per application.
at System.ServiceModel.DomainServices.Client.ApplicationServices.WebContextBase..ctor(Boolean setAsCurrent)
at System.ServiceModel.DomainServices.Client.ApplicationServices.WebContextBase..ctor()
at SlEventManager.WebContext..ctor()
at SlEventManager.App..ctor()
除了这写信息 没有任何提示,在SilverLover.com讨论一番.其实碰到这个错误一般在Domain Service DeBug时出现.如果想看到具体程序的详细信息:可以这样方式获取:
A:在silverlight中Server端重写override DomainService 的OnError方法,这样可以在SilverlightClient中TryCatch中获得比较详细的server 错误的信息.快速定位异常.
B:可以用fiddler去监视request/response这样能获得比较详细的错误信息.在浏览器中装一个Web Request跟踪插件. 方式获取 直接看到.
C:方式三就需要Silverlight 与JavaScript进行交互:其实类中OnError属性可以指定一个JavaScript函数来返回Silverlight Application的异常信息. 默认函数名:onSilverlightError,你可以通过Silverlight调用JS函数来显示详细异常信息:
定义JS函数:
- <script type="text/JavaScript">
- function onSilverlightError(sender,args)
- {
- var appSource="";
- if(sender!=null && sender!=0)
- {
- appSource=sender.getHost().Source;
- }
- var errortype=args.ErrorType;
- var errorCode=args.ErrorCode;
- if(errortype=="ImageError" || errortype=="MediaError")
- {
- return;
- }
- var errmsg="Unhandled Error in Silverlight Application "+appSource+"\n";
- errmsg+="Code:"+errorCode+" \n";
- errmsg+="Category:"+errortype+"\n";
- errmsg+="Message:"+args.ErrorMessage+ " \n";
- if(errortype=="ParserError")
- {
- errmsg+="File:"+args.XamlFile+" \n";
- errmsg+="Line:"+args.lineNumber+ " \n";
- errmsg+="Position:"+args.charPosition+" \n";
- }
- else if(errortype=="RuntimeError")
- {
- errmsg+="Line:"+args.lineNumber+ " \n";
- errmsg+="Position:"+args.charPosition+" \n";
- }
- throw new Error(errmsg);
- }
- </script>
上面代码是从Ruby编译器RedRails中手动写的. 格式上应该能清晰看到一个异常信息组成整个过程.Silverlight中调用Javascript方法如下:
JS在客户端比较的强大.而silverlight提供了两种对JavaScript访问接口:Invoke和InvokeSelf().首先在页面加载事件中把JS写到页面.如下:因为对JS操作徐引用空间:
- using System.Windows.Browser;
加载方法:
- void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
- {
- //创建Javascript脚本 简单演示
- string jstxt = @"function onSilverlightError(showmsg){alert(showmsg);}";
- HtmlElement getelement = HtmlPage.Document.CreateElement("Script");
- getelement.SetAttribute("type","text/javascript");
- getelement.SetProperty("text","jstext");
- //添加页面中
- HtmlPage.Document.Body.AppendChild(getelement);
- }
调用方法:
- //调用方法:
- HtmlPage.Window.Invoke("onSilverlightError", "这是Invoke方式调用");
- ScriptObject callclientjs = HtmlPage.Window.GetProperty("onSilverlightError") as ScriptObject;
- callclientjs.InvokeSelf("这是invokeself方法");
如上为两种调用方式:invoke是通过System.Windows类方法调用 而invokeself则是通过一个ScriptObject对象调用.以此在Silverlight中如出现异常可以同JS弹出对话框方式获取具体详细信息.
(4)Silverlight初始化参数设置和获取.
silverlight承载在页面中包含在一个Div的Object元素中. 关于silverlight插件基本属性. 请参考这里. 不在赘述.
其中有一个initParams属性允许自定义向Silverlight插件初始化参数. 参数在应用程序初始化时传递给Appliaction_StartUp启动事件. 那么在Silverlight中如何设置获取初始化参数.
A:手动设置
- <div id="silverlightControlHost">
- <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="75%" height="75%">
- <param name="source" value="ClientBin/SLClientDemo.xap"/>
- <param name="onError" value="onSilverlightError" />
- <param name="background" value="white" />
- <param name="minRuntimeVersion" value="4.0.50401.0" />
- <param name="autoUpgrade" value="true" />
- <!--手动设置传递参数-->
- <param name="initParams" value="Author=chenkai,Date=2010,BlogerTeach=i'm Silverlighter" />
- <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50401.0" style="text-decoration:none">
- <img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Get Microsoft Silverlight" style="border-style:none"/>
- </a>
- </object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px">
- </iframe>
- </div>
注意在initParams属性中设置参数语法:参数采用单一字符串输入格式,其中用逗号 (,) 分隔各个参数,并用等号 (=) 来分隔键与值。例如:"key1=value1,key2=value2,key3=value3"。需要处理分隔符,以便此 API 可以将其作为字典提供.
B:获取参数
silverlight提供两种获取页面传递过来的参数方式:Application_Startup事件中StartupEventArgs.initParams 和在System.Windows.Browser空间下HtmlDocument.QueryString. 在Application_Startup事件中:
- //两种方式获取参数
- MainPage getpage = new MainPage();
- this.RootVisual =getpage;
- Grid getgrid = getpage.LayoutRoots;
- int count=0;
- double toplenght=20;
- foreach (string getkey in e.InitParams.Keys)
- {
- count++;
- if(count>0)
- {
- toplenght+=20;
- }
- getgrid.Children.Add(new TextBlock()
- {
- Text =string.Format("参数{0}值:{1}", getkey, e.InitParams[getkey]),
- Padding=new Thickness(15,toplenght,toplenght,toplenght),
- FlowDirection=FlowDirection.LeftToRight
- });
- //获取成功
- }
- foreach (string getstrkey in HtmlPage.Document.QueryString.Keys)
- {
- count++;
- if (count > 0)
- {
- toplenght += 30;
- }
- getgrid.Children.Add(new TextBlock {
- Text=string.Format("QueryString参数{0}值{1}",getstrkey,HtmlPage.Document.QueryString[getstrkey]),
- Padding=new Thickness(25,toplenght,toplenght,toplenght),
- FlowDirection=FlowDirection.LeftToRight
- });
- }
因e.initParams返回值为System.Collections.Generic.IDictionary<String, String>类型. 所以可以直接转键值对类型来获得:
- //直接转换成字典表
- IDictionary<string, string> getdic = e.InitParams;
- if (getdic.Count > 0)
- {
- MessageBox.Show(getdic["Author"].ToString()+"新数据:"+getdic["Date"].ToString());
- }
两者区别:
initParmams:只要卸载Application_StartUp事件中.当silverlight运行程序后可以通过. StartUpEventArgs或App.Current.Host.InitParams获得, 后者可以在任何Xaml中随时随地获取参数值.
HtmlDocumentPage:可以在Silverlight任何位置调用 直接获取. 但前提是参数值必须存在 更多详细细节请参见MSDN
Silverlight插件作为Aspx页面我们也可以动态修改要传入silverlight初始化参数的值: 修改
- <!--设置参数传递-->
- <param name="initParams" value="Author=chenkai,Date=2010" runat="server" id="initParams" />
通过Button事件测试:
- <!--添加一个Button事件测试-->
- <asp:Button ID="Button1" runat="server" Text="ChangeParValue" onclick="Button1_Click" />
事件内容:
- <script runat="server">
- protected void Button1_Click(object sender, EventArgs e)
- {
- //修改参数信息
- this.initParams.Attributes.Add("value", "BookName=chenkaiunion");
- }
- </script>
当button按钮触发事件页面重新加载App,新设置参数值即在Silverlight程序中生效.测试通过.
(5)Silverlight Theme主题使用:
Silverlight 4相对Silverlight 3 Theme 主题应用上得到增强.Silverlight 4中实现一个Theme步骤.
准备工作: 安装相应版本的SDK. 例如SL4 安装成功后对应路径在:C:\Program Files\Microsoft SDKs\Silverlight\v4.0\Toolkit\Apr10\Themes 下.
实现TwilightBlue 主题:SL 4完整演变成控件:
- <my:TwilightBlueTheme>
- <StackPanel x:Name="LayoutRoot" Background="White" Orientation="Vertical" >
- <ComboBox Height="25" Margin="68,76,0,0" Name="changestyle" Width="230" />
- <Button Content="Change Silverlight Theme" Margin="68,26,0,0" Height="32" Name="button1" Width="231" />
- </StackPanel>
- </my:TwilightBlueTheme>
如果实现多个主题之间切换:
(A)在本地SDK 安装Theme目录下添加相应主题DLL文件引用
(B)再对应的XAML文件添加到项目中
后台通过System.Windows.Controls.Theming空间下静态类ImplicitStyleManager对主题进行动态更新:
- //资源字典文件的路径
- Uri uri = new Uri("Themes/BubbleCreme.xaml", UriKind.Relative);
- ImplicitStyleManager.SetResourceDictionaryUri(LayoutRoot, uri);
- //设置更新模式
- ImplicitStyleManager.SetApplyMode(LayoutRoot, ImplicitStylesApplyMode.Auto);
- //应用样式到StockPanel控件
- ImplicitStyleManager.Apply(LayoutRoot);
在说明一下Silverlight 3中与Silverlight 4应用不同之处:
<1> SL 3中不能根据资源中的样式的TargetType将样式自动应用到所有目标类型的元素上, 但SL 4中 则加以完全支持.
<2> SL 3通过将主题应用到容器控件上或将容器控件放在主题控件的内容中的方式,可以将主题自动应用到控件的所有子元素,但无法将主题直接应用到所有页面或用户控件中,例如常用ChildWindows. 而在SL 4中Theme已经支持到ChildWindows应用中. 大大简化自定义子窗体Theme难度.
<3> SL中对空间样式定义效果同主体效果 放在一块时会产生叠加效果. So Theme 没有完全限制住开发者自主生产的能力. 很方便.
运行后效果: