从4月份Mix 10大会微软发布 Silverlight 4 至今,SL的第四个4版本的推出已经历时半年之久, 昨日在SBlakeMore.Com上看到一组关于采用Silverlight Com组件实现图像采集和数据实时分析运用相关讨论. 微软在定义Silverlight前景规划时 众所周知 SL一直走的跨平台 跨浏览器这条路线. 相比在前面的3个版本. 在第四个版本中,微软处理了8000多个客户功能请求,其中之一就是添加COM组件支持. 随着这个功能作为新特性在Sl 4版本中推出, 对微软宣扬的SL 一直保持跨平台的争论也就出现了.
原因很简单:COM对象仅仅能够运行在Windows平台的IE或Firefox浏览器中,苹果的Mac OS X或Linux并不支持COM, 而对于COM组件支持是否会破坏Silverlight 4的跨平台兼容性这个问题,微软也做出相关声明: 虽然Mac并不支持COM组件 但微软正在研究如何使得某些 COM组件能够访问Mac版的Silverlight, 紧接着Linux的发言人Miguel de Icaza表示,为了更好地配合微软的Silverlight 4功能,该公司正在试图添加各项组件支持.但对于Linux团队来讲,这也是一个富有挑战性的工作.
目前关于Silverlight 4Com对跨平台的争议在很多SL开发者中常常能看到相关争论. 这里不再提及. 如下我会演示Windows 7系统关于Silverlight 4Com组件实用几个应用场景.
<1>Silverlight 4 Com组件应用实例和场景
COM component(COM组件)是微软公司为了计算机工业的软件生产更加符合人类的行为方式开发的一种新的软件开发技术。在COM构架下,人们可以开发出各种各样的功能专一的组件,然后将它们按照需要组合起来,构成复杂的应用系统, Silverlight 4完全是在满足更多企业级用户对Com组件在Windows平台运用微软才加以支持的.
类似我们常在项目实用Silverlight 数据的导入导出基本操作需求是极为常见的. 如下我会展示利用Com组件方式 实现Silverlight在OOB模式下导出到Notepad[记事本]和World 07文档.
创建一个定义一个简单页面 XAML[源码]:
2 <Grid.RowDefinitions>
3 <RowDefinition Height="Auto"/>
4 <RowDefinition Height="*"/>
5 </Grid.RowDefinitions>
6 <StackPanel>
7 <Button Content="Export data to Notepad" Click="TextExport_Click"/>
8 <Button Content="Export data to Word" Click="WordExport_Click">
9 <Button.ContentTemplate>
10 <DataTemplate>
11 <StackPanel Orientation="Horizontal">
12 <TextBlock Text="{Binding}"/>
13 <TextBlock Text=" ("/>
14 <CheckBox IsTabStop="False"
15 Checked="CheckBox_StateChanged"
16 Unchecked="CheckBox_StateChanged"
17 Content="Print directly with default printer"/>
18 <TextBlock Text=")"/>
19 </StackPanel>
20 </DataTemplate>
21 </Button.ContentTemplate>
22 </Button>
23 </StackPanel>
24 <sdk:DataGrid Grid.Row="1" Name="dataGrid1" AutoGenerateColumns="True"/>
25 </Grid>
在页面Grid中简单模拟多条数据 定义一个实体:
1: /// <summary>
2: /// 定义一个数据载体实体Entity
3: /// Sign by chenkai Date:2010年11月8日21:46:29
4: /// </summary>
5: public class PersonEntity
6: {
7: public string Name { set; get; }
8: public string Gender { set; get; }
9: public int Age { set; get; }
10: }
在Mainpage后台页面使用Com时需要引入命名空间:
1: using System.Runtime.InteropServices.Automation;
2: using System.Collections;
3: using System.Threading;
页面加载初始化DataGrid中填充相关的数据:
1: /// <summary>
2: /// 加载数据
3: /// </summary>
4: void MainPage_Loaded(object sender, RoutedEventArgs e)
5: {
6: var list = new List<PersonEntity>();
7: var rand = new Random();
8: for (int i = 0; i < 9; i++)
9: list.Add(new PersonEntity
10: {
11: Name = "Person:" + i,
12: Age = rand.Next(20),
13: Gender = (i % 2 == 0 ? "Male" : "Female"),
14: });
15:
16: //Bind
17: dataGrid1.ItemsSource = list;
18: }
利用C# 新特性实现DataGrid数据导出到NotePad[记事本中]:
1: /// <summary>
2: /// 核心操作 导出数据到记事本.
3: /// Sign by chenki Date:2010年11月8日21:51:18
4: /// </summary>
5: private void TextExport_Click(object sender, RoutedEventArgs e)
6: {
7: // Check if using AutomationFactory is allowed.
8: if (!AutomationFactory.IsAvailable)
9: {
10: MessageBox.Show("This function need the silverlight application running at evaluated OOB mode.");
11: }
12: else
13: {
14: // Use shell to open notepad application.
15: using (dynamic shell = AutomationFactory.CreateObject("WScript.Shell"))
16: {
17: shell.Run(@"%windir%\notepad", 5);
18: Thread.Sleep(100);
20: shell.SendKeys("Name{Tab}Age{Tab}Gender{Enter}");
21: foreach (PersonEntity item in dataGrid1.ItemsSource as IEnumerable)
22: shell.SendKeys(item.Name + "{Tab}" + item.Age + "{Tab}" + item.Gender + "{Enter}");
23: }
24: }
25: }
注意所有的导出操作全部OOB模式下执行才能有权限实现,这个稍微我会详细解释:
当运行起程序后安装本地桌面:
在OOB模式下运行起来:
导出到记事本效果:
实现World就相对麻烦很多.在World导出对应OFFices套件必须是07 版本或是更高版本. 如下实现:
1: /// <summary>
2: /// 核心操作 数据导出到World中
3: /// sign by chenkai DAte:2010年11月8日22:06:15
4: /// </summary>
5: private void WordExport_Click(object sender, RoutedEventArgs e)
6: {
7: // Check if using AutomationFactory is allowed.
8: if (!AutomationFactory.IsAvailable)
9: {
10: MessageBox.Show("This function need the silverlight application running at evaluated OOB mode.");
11: }
12: else
13: {
14: // Create Word automation object.
15: dynamic word = AutomationFactory.CreateObject("Word.Application");
16: word.Visible = true;
17:
18: // Create a new word document.
19: dynamic doc = word.Documents.Add();
20:
21: // Write title
22: dynamic range1 = doc.Paragraphs[1].Range;
23: range1.Text = "Silverlight4 Word Automation Sample\n";
24: range1.Font.Size = 24;
25: range1.Font.Bold = true;
26:
27: var list = dataGrid1.ItemsSource as List<PersonEntity>;
28:
29: dynamic range2 = doc.Paragraphs[2].Range;
30: range2.Font.Size = 12;
31: range2.Font.Bold = false;
32:
33: // Create table
34: doc.Tables.Add(range2, list.Count+1, 3, null, null);
35:
36: dynamic cell = doc.Tables[1].Cell(1, 1);
37: cell.Range.Text = "Name";
38: cell.Range.Font.Bold = true;
39:
40: cell = doc.Tables[1].Cell(1, 2);
41: cell.Range.Text = "Age";
42: cell.Range.Font.Bold = true;
43:
44: cell = doc.Tables[1].Cell(1, 3);
45: cell.Range.Text = "Gender";
46: cell.Range.Font.Bold = true;
47:
48: // Fill data to table cells
49: for (int i = 0; i < list.Count; i++)
50: {
51: cell = doc.Tables[1].Cell(i + 2, 1);
52: cell.Range.Text = list[i].Name;
53:
54: cell = doc.Tables[1].Cell(i + 2, 2);
55: cell.Range.Text = list[i].Age;
56:
57: cell = doc.Tables[1].Cell(i + 2, 3);
58: cell.Range.Text = list[i].Gender;
59: }
60:
61: if (_isprint)
62: {
63: // Print the word directly without preview.
64: doc.PrintOut();
65: }
66:
67: }
68: }
Word 07版导出效果:
有些人为何要为对于数据导出 类似文件操作等为何要在OOB模式运行. 这一切都源自Silverlight的为了运行环境和部署的安全上做的沙箱设计.COM组件是客户端运行的,OOB模式获得高级权限跳出沙箱设计对本地系统文件操作权限的限制. 如果在安全沙盒模型下的,那就无法调用COM组件. 关于Silverlight的沙箱设计相比做过WP7嵌入式数据库本地访问应该是对这点深有体会. 虽然完整保证Silverlight安全但却本地系统交互上做了诸多限制对开发产生诸多不便.
<2>关于dynamic关键字
在Visual C# 2010中引入了一种新的dynamic类型,该类型是一个静态的(static)类型,但是一个dynamic类型的对象会绕过静态类型检查。在大多数情况下dynamic和object类型有些相似,但是在编译时,dynamic类型被假定为支持任何操作,也就是说dynamic类型的对象可以是一个Office对象,可以是一个COM对象或者是DOM对象,而如果在运行时发现该对象不是期望的对象则会抛出一个运行时异常.
从上面代码可以看出大量使用了Dynamic类型. 注意我们开始编码引入空间System.Runtime.InteropServices.Automation下AutomationFactory 类提供对已注册的自动化服务器的访问.
自动化是应用程序用于向脚本撰写工具和其他应用程序公开功能的一种基于 Windows 的技术。例如,可以使用自动化将 Office 功能添加到基于 Silverlight 的应用程序中 如上操作就是.
应用程序或公开功能的组件称作自动化服务器,而访问功能的应用程序称作自动化客户端。因为必须预先安装并在完全信任下运行自动化服务器,所以 Silverlight 仅将受信任的应用程序作为自动化客户端, 这也是为什么要在OOB模式下一个重要原因.
如上对NotePad导出操作:
1: using (dynamic shell = AutomationFactory.CreateObject("WScript.Shell"))
2: {
3: shell.Run(@"%windir%\notepad", 5);
4: Thread.Sleep(100);
5:
6: shell.SendKeys("Name{Tab}Age{Tab}Gender{Enter}");
7: foreach (PersonEntity item in dataGrid1.ItemsSource as IEnumerable)
8: shell.SendKeys(item.Name + "{Tab}" + item.Age + "{Tab}" + item.Gender + "{Enter}");
9: }
利用Dynamic对象封装Shell脚本来执行数据导出. 而作为OFFice套件之一的World则完全不同.采用直接动态创建一个对象方式CreateObject 或 GetObject 方法检索对自动化服务器的后期绑定引用,若要将引用用作后期绑定对象,必须将其分配给 Object 类型(在 Visual Basic 中)或dynamic 类型(在 C# 中)的变量. 同理对于outLook操作也是同样道理
Silverlight 只能使用已安装的自动化服务器;因此,当它们找不到要求的进程 ID 时,CreateObject 和 GetObject 方法将引发异常。
下表显示了几个常见的 progID 值:
-
办公自动化:Outlook.Application、Excel.Application、PowerPoint.Application
-
系统自动化:Scripting.FileSystemObject、WScript.Shell、Shell.Application
-
Windows 管理规范:WbemScripting.SWbemLocator
一般情况下,这些字符串映射到自动化服务器的特定版本。如果服务器有多个版本,则该服务器通常会添加版本编号到该进程 ID
类似操作OutLook就极为简单:
1: private dynamic outlook;
2:
3: private bool InitializeOutlook()
4: {
5: try
6: {
7: // If GetObject throws an exception, then Outlook is
8: // either not running or is not available.
9: outlook = AutomationFactory.GetObject("Outlook.Application");
10: return true;
11: }
12: catch (Exception)
13: {
14: try
15: {
16: // Start Outlook and display the Inbox, but minimize
17: // it to avoid hiding the Silverlight application.
18: outlook =
19: AutomationFactory.CreateObject("Outlook.Application");
20: outlook.Session.GetDefaultFolder(6 /* Inbox */).Display();
21: outlook.ActiveWindow.WindowState = 1; // minimized
22: return true;
23: }
24: catch (Exception)
25: {
26: // Outlook is unavailable.
27: return false;
28: }
29: }
30: }
如上关于Silverlight 4使用Com组件最为简短的分析. 使用范围主要涉及到我们常用的OFFice套件中.