怎么样调用 Silverlight 客户端 APIs

介绍

Microsoft Silverlight 插件 支持 一个扩展的编程模型,这包括 托管 和非托管 代码. 非托管 APIs 在 Silverlight 1.0 (formerly WPF/E) Beta release是有效的,同样也存在于Silverlight 1.1 Alpha (May 2007) release. 而 托管 APIs 是在 Silverlight 1.1 Alpha (May 2007) release才支持的. 你可以使用CLR调用 托管 APIs ,这个特性也是在新版本的VS也就是 Visual Studio Code Name "Orcas."中将要集成的, 目前, C# 和 Visual Basic 是可以在1.1中使用的.你也可以使用 dynamic language runtime (DLR). 更多关于 DLR, 请查看 怎么样使用动态语言来开发 Silverlight .

详细的介绍 Siverlight 托管 API 目前不能提供. 然而, 你可以使用一些反射工具来查看 托管 程序集而来了解有些什么 APIs 是可以调用的. 有关此项详细消息, 请查看 怎么样使用Visual Studio Object Browser浏览Silverlight 程序集.

在本篇我们将介绍一些你在开发基于Silverlight的 应用程序的一些常用的使用托管代码的APIs . 在大多数情况下, 对于object model而言,托管 API 一般与对语法和出现的位置都比较严格,这和 非托管 API是一样的. 因此, 你甚至可以在非托管 代码的基础上来进行 托管代码的开发. 然而, 因为object model或命名方式的不一致,这种等价还是需要商榷的.

Silverlight 托管 类库的 层次

很多的托管类依靠Silverlight control 和其UI 特性进行编程,它们都是遵循这些层次关系的:

依赖对象

  • UIElement

    • FrameworkElement

如果你曾经有过Windows Presentation Foundation (WPF)开发基础, 那么你就会对这里的层次关系感到熟悉. 然而, 却不像WPF中表面一样, 当在开发 Silverlight程序时,你将自己总结和体会对每个类所充当的在层次关系中的角色. 在Silverlight 中你可以这样认为: FrameworkElement 类是基础 "element" 类,它们包含了很多你在XAML中定义或UI中定义的元素的有用的 APIs.

但是这不代表所有你在 XAML中例示的元素都是 FrameworkElement. 这里有许多元素,它们起了给其它一些UI元素的某些属性赋值的作用, 但是这些属性可能会被精心的包装过. 我们举一个确切的例子, AnimationStoryboard的派生类, 它们都不是 FrameworkElement 对象. 事实上, animations Storyboard 经常用来控制一些UI 元素.

浏览 Object Tree 并且 找到已经命名的Objects

一个独立的XAML页的object tree一般都是以一个 XAML root element开始的. 当前存在的 object tree 假设 XAML 页 已经是 Silverlight control的源并且已经解析成功了 . (当然也可以直接创建一个作为resources的 XAML 文件, 或者直接将其载入到object tree中,在这种状况下object tree加入到XAML的 root 里来.)

对于 托管代码, 当你为XAML root定义了一个 x:Class , 这样你就可以轻松的得到XAML root的引用: 你可以直接在后台敲入 "this" 或 "Me". 然而, 某一个object的object tree中的属性并不是都可以引用到的,比如它的构造函数. 如果构造函数被调用,在这种情况下, object tree的大部分还没有构造出来; 你必须等到XAML被解析了.

对于根结点前的一些object对象,你有三种方法来使用它们, 其中有两点是有密切关系的.

  • 你可以顺着object tree的root一直往下, 使用关相的 内容模型所提供的属性来得到所需要的对象引用, 比如 Panel.Children, 或使用泛型集合索引. 这种方法可能并不是你最好的选择, 因为这需要你对XAML文件的结构非常的熟悉. 这通常只是用来进入内容模型,奇怪的是, 目前确没有单个的 API 是提供这项功能的. 一般的,你可能只是使用这种方法来进入内容模型或集合的的某一个层次而已.

  • 你可以给XAML element敲入一个 x:Name属性 . 然后你就可以使用DependencyObject.FindName这个API来得到该对象的引用了.(DependencyObject 是一个很基础的类, 所以你几乎可以在所有的Element中找到这个API.) 如果你使用过 非托管 APIs 来开发过 Microsoft Silverlight 1.0 (formerly WPF/E), 那这个方法你可能就会比较熟悉了. FindName 返回一个类型的 Object, 但FindName 有一个不太方便的就是你必须指定它返回的类型,这样你才能正确有使用它. 这意味你必须了解当前方法所返回的正确的类型.

    CS
    //using 'this' against the root where this x:Class applies
    //in this case we know that we are trying to find a TextBlock and can cast
    TextBlock tb = this.FindName("myTextBlock") as TextBlock;
    
    
    VB
    'using 'this' against the root where this x:Class applies
    'in this case we know that we are trying to find a TextBlock and can cast
    Dim tb As TextBlock = CType(Me.FindName("myTextBlock"), TextBlock)
    
    
  • 只要你使用Visual Studio Code Name "Orcas" 和其提供 Silverlight 模板,并使用它的 build过程, 这你就可以有一个更好选择了. 给你的Element一个x:Name 属性. 这样, 你可以不用一定要使用 FindName了, 考虑到 x:Name 其实是和后台的相对应的类是引用关系. 打个比方, 如果你创建了一个element <TextBlock x:Name="myTextBlock"/>, 并且你编程的对象是 x:Class,这样,你只需要要简单的敲入 myTextBlock 就可以得到引用了, 然后你敲入一个点, 相对应的属性,方法或者事件处理事件就能出来了. 通过IntelliSense你可以浏览对象所提供的所有可供调用的内容. 每次生成后台代码的过程可以确保这些自动引用的正确添加. 这个生成的代码文件其实是x:Class的一个partial类,它们将在使用的时候一起编译 . 你可以查看Silverlight project的编译机制: x:Class的partial class定义, XAML root中的 Loaded handler 其实会调用后台文件的 InitializeComponent, 和在obj 目录中生成的文件 (它重定义了 InitializeComponent 以此来在每次加载时,能正确的提供这种引用关系). 你可以发现当你加入一个已经命名的element到 XAML文件中来, 你可能需要预编译,这样才能得到相应的有着最新对象名的 IntelliSense .

  • 生成的后台代码和其引用都是能简单获得的,但你应该注意,你并不是时刻都能有效的使用它的. 比如, 当你想进行 Silverlight control 合并, 你会发现后台文件和XAML文件的关系似乎是反着的, XAML文件总要比后台文件慢一点. 因此, 你可能会觉得在创建自定义控件时使用 FindName 要更频繁一些.

从object tree 向 root element方向遍历, 你可以用 API FrameworkElement.Parent. 当到达 root时, FrameworkElement.Parent将返回 null.

获取或设置一些附加属性

附加属性是XAML 语言定义的,以此来说明怎么样给 elements 的各种特殊属性赋值, 甚至这个属性并不存在于当前element的成员列表中. 举附加属性的一个例子,在 Silverlight 客户端有一个 API 由三个 Canvas 属性组成, 他们可以定位Canvas的子elements的呈现外观. 他们是 Canvas.Left, Canvas.Top, 和 Canvas.ZIndex. 当你想在XAML文件中进行这些属性的设置的时候, 你必须以 Canvas owner的类型来限定并以Owner.Property 形式来调用, 注意,你不要直接在 Canvas中来设置该属性. 相反, 你应该在 Canvas的直接子对象中来设置, 父对象 Canvas 会读入这些属性信息,来控制子对象的呈现效果.

因为附加属性的特殊性,在托管代码中,你不能使用 Object.Property 形式来给其赋值, 因为这总有两个对象在使用 (一个是 XAML 中的对象本身, 还有一个是在属性被设置的时候的对象的实例).

在 托管 代码中,你可以通过调用DependencyObject.GetValue来得到 附加属性值 .第一个参数是指属性的从属者, 它是后台类中的一个成员.你可以通过 DependencyObject.SetValue来设置属性值. 第一个参数同样是指属性的从属者, 第二个是要设置的值. 在每个示例中,都是使用的实例方法, 它们的调用都依靠实例所提供的可以设置的附加属性.

CS
//myTextBlock is a TextBlock from XAML, which is child of a Canvas
double currentLeft = (double) myTextBlock.GetValue(Canvas.LeftProperty);
if (currentLeft > 400.0) { myTextBlock.SetValue(Canvas.LeftProperty, 400); }

VB
'myTextBlock is a TextBlock from XAML, which is child of a Canvas
Dim currentLeft As Double = CType(myTextBlock.GetValue(Canvas.LeftProperty), Double)
If (currentLeft > 400.0) Then
    myTextBlock.SetValue(Canvas.LeftProperty, 400)
End If

GetValueSetValue 其实有很广的用处.对于 Silverlight client,只要对象提供了相应的属性,你就可以通过这两个方法来取得或者设置它们的值.例如, 你可以调用 myTextBlock.SetValue(TextBlock.TextProperty "hello"). 但是在这些示例中,并没有 "regular" (非附加) 属性, 然而 Instance.Property 形式对于设置或获取属性取来说还是要更直观一些.

高和宽

HeightWidth 存在于 FrameworkElement. 你可以设置 HeightWidthCanvas, TextBlock 和各种 Shape 基础类上.

有一小部分的elements (比如 TextBlock) 还存在一个 ActualWidthActualHeight 属性. 它们是在实际情况下被计算出来的,只读的属性. ActualWidthActualHeight将受到多种因素的影响而改变,这将帮助你得到实际上显示出来的尺寸. 比如, 当你的 TextBlock里包含一些文字, 文字的大小受 FontSize, FontFamily, FontSpacing, 等的影响, 当然,TextBlock 的实际展示大小也会受这些因素的影响了.

注意

HeightWidth 对于自定义控件,目前还需要有一个工作区来配合使用, 这取决于你如何使用基础的属性. 想查看详细说明, 点击 怎么样创建一个自定义的 Silverlight Controls.

TextBlock and Text APIs

The TextBlock class 包括 a number of APIs. There is a very limited text object model (exposed through the Inlines collection), and there are several expected text-related properties for text in UI:

  • FontFamily: Set as a string that names the font family. Silverlight initially supports only a few fonts (see the 非托管 documentation for TextBlock for details). You can download fonts other than the initial fonts by calling TextBlock.SetFontSource.

  • FontSize: Set in pixels.

  • FontStretch: Uses the FontStretches enumeration. Check the enumeration values in the object browser, or see the Silverlight 1.0 beta documentation for details.

  • FontStyle: A value of the FontStyles enumeration, Normal, or Italic.

  • FontWeight: Uses the FontWeights enumeration. Check the enumeration values in the object browser, or see the 非托管 Silverlight documentation for more details.

  • Foreground: Typically, a solid color declared by XAML attribute, but it can also be set to any class derived from Brush (for example, an ImageBrush or GradientBrush) for special effects.

  • TextDecorations: A value of the TextDecorations enumeration, None, or Underline.

  • TextWrapping: A value of the TextWrapping enumeration, NoWrap, or Wrap.

  • Text

CS
TextBlock tb = new TextBlock();
tb.FontFamily = "Arial";  //one of the initial fonts
tb.FontSize = 20;
tb.FontStretch = FontStretches.SemiExpanded;
tb.FontStyle = FontStyles.Italic;
tb.FontWeight = FontWeights.DemiBold;
SolidColorBrush golden = new SolidColorBrush();
golden.Color = Color.FromRgb(211, 147, 12);
tb.Foreground = golden;
tb.TextDecorations = TextDecorations.Underline;
tb.TextWrapping = TextWrapping.Wrap;
tb.Text = "I am nondefault!";
Run il = new Run();
il.FontSize = 40;
il.Text = "Hello!";
tb.Inlines.Insert(0,il);

VB
Dim tb As TextBlock = New TextBlock()
tb.FontFamily = "Arial"  'one of the initial fonts
tb.FontSize = 20
tb.FontStretch = FontStretches.SemiExpanded
tb.FontStyle = FontStyles.Italic
tb.FontWeight = FontWeights.DemiBold
Dim golden As SolidColorBrush = New SolidColorBrush()
golden.Color = Color.FromRgb(211, 147, 12)
tb.Foreground = golden
tb.TextDecorations = TextDecorations.Underline
tb.TextWrapping = TextWrapping.Wrap
tb.Text = "I am nondefault!"
Dim il As Run = New Run()
il.FontSize = 40
il.Text = "Hello!"
tb.Inlines.Insert(0, il)

Colors and Brushes

When set in XAML as an attribute value, you can specify solid colors with approximately 256 named color values based on UNIX X11, including intriguing colors such as "MistyRose" and "PapayaWhip". Or, you can specify RGB or ARGB values by preceding the string value with a hash sign (#). In the Silverlight 托管 API, the number of predefined colors available is smaller; it is limited to 16 colors. Therefore, the primary mechanism for defining a solid color is to call the static Color.FromArgb, Color.FromScRgb, or Color.FromRgb method. Also, the various classes derived from Brush (such as SolidColorBrush) have only the default constructor. They do not have any convenience constructors. You must then set the relevant properties individually after construction. For classes such as RadialGradientBrush you must also construct each GradientStop and add to the GradientCollection.

CS
Rectangle r = new Rectangle();
r.Width = 400;
r.Height = 300;
SolidColorBrush golden = new SolidColorBrush();
golden.Color = Color.FromRgb(211,147,12);
r.Fill = golden;

VB
Dim r As Rectangle = New Rectangle()
r.Width = 400
r.Height = 300
Dim golden As SolidColorBrush = New SolidColorBrush()
golden.Color = Color.FromRgb(211, 147, 12)
r.Fill = golden

A large amount of routine code can be required, especially to produce brushes and to handle the potential for reusing a brush. The following development strategy will be helpful when a brush gets significantly complex:

  1. Define the brush as a XAML file, or even as a string in valid XAML form (including the xmlns at the root).

  2. Include that string or file as an embedded resource for your application.

  3. Access the resource as a stream, and use the stream as a string source for the XamlReader.Load API (more about this API in an upcoming section).

  4. Cast the result to the appropriate Brush class, and use it to set your property.

One advantage of this approach is that you can use design tools such as Microsoft Expression Blend to produce the XAML, which will yield satisfying results more quickly than iterating on ARGB values in code or pasting strings from other design tools. The Silverlight 托管 APIs do not yet have a resources system that is quite as developed as the ResourceDictionary-based resources system for WPF, but embedded resources can serve the purpose.

Mouse and Mouse Position

Browser client APIs can be used to get mouse positions in the overall browser, but that is usually not adequate for 基于Silverlight的 applications. Instead of using a service that constantly monitors the mouse input device (which is the case for WPF) the mouse position is reported only in response to mouse events. 然而, one of these events is MouseMove, which fires frequently. Even if the mouse never moves, you can get an initial mouse position when the MouseEnter event for the main Canvas fires as the Canvas is loaded (so long as the mouse started over the client area). You obtain the mouse pointer position by calling the method MouseEventArgs.GetPosition from the event data instance, instead of using directly-defined X and Y properties. This method takes a parameter of type UIElement; the element you provide to the method will be used to calculate a position offset. If you pass null, the coordinate system is relative to the Silverlight control content area. Otherwise, the typical element to pass to the GetPosition input parameter if you do not pass null is the event sender (which you will need to cast to UIElement). You can pass any element, including elements not clicked upon by strict hit-testing definition, which might mean that the X and Y values you get from GetPosition are negative.

CS
void el_MouseClick(object sender, MouseEventArgs e) {
    //sender is a 200px ellipse, the closer you click to its relative center, the more it disappears
    Ellipse el = sender as Ellipse;
    double x = e.GetPosition(el).X;
    el.Opacity = Math.Abs(x - 100)/100;
}

VB
Sub el_MouseClick(ByVal sender As Object, ByVal e As MouseEventArgs) Handles HotMouse.MouseLeftButtonUp
    'sender is a 200px ellipse, the closer you click to its relative center, the more it disappears
    Dim el As Ellipse = CType(sender, Ellipse)
    Dim x As Double = e.GetPosition(el).X
    el.Opacity = Math.Abs(x - 100) / 100
End Sub

The two most common scenarios for getting the mouse position are to determine where the mouse was when a button was clicked, and to determine where the mouse is when crossing a boundary. Both of these scenarios are involved in a drag-and-drop scenario as well.

XamlReader

In JavaScript, there are no constructors, so you cannot create a new instance of an element you want to add to a tree. You can use DHTML-like strategies such as having parallel trees that are already constructed and using a hide-show approach, you can use animations (but only to change properties, not add to the tree), or you need to call Control.content.createFromXAML to effectively use the parser as a constructor engine. For this reason, you may have found yourself calling createFromXAML fairly frequently in Silverlight 1.0 if you were creating a particularly dynamic UI.

In 托管 code, you have access to constructors. Therefore, in many cases, calling XamlReader.Load (which is the 托管 createFromXaml equivalent) is unnecessary, particularly because you are already working in a code model and calling a constructor is not difficult. The constructor approach, combined with setting properties on the newly constructed instances and adding them to the existing object tree, works well if the run-time design changes are simple.

On the other hand, if the design changes were complex, and you had a designer-coder responsibility split where the designer produced two different UIs for each state, it might make sense to load from XAML to refresh the UI. 然而, in this case, you really should not load XAML from a string directly, as the XamlReader.Load signature might indicate. Instead, it is better code style to obtain the string from a stored resource or a separate file, perhaps using System.IO.StreamReader, so that the XAML handoff from designer to coder will consist of a file rather than strings.

CS
Assembly assembly = this.GetType().Assembly;
//BigBrush.xaml is a LinearGradientBrush with half a dozen stops
//and perhaps it gets used frequently, from different files
Stream s = assembly.GetManifestResourceStream("CallClientAPIs.BigBrush.xaml");
StreamReader sr = new StreamReader(s);
Brush b = (Brush)XamlReader.Load(sr.ReadToEnd());
sr.Close();

VB
Dim assembly As Assembly = Me.GetType().Assembly
'BigBrush.xaml is a LinearGradientBrush with half a dozen stops
'and perhaps it gets used frequently, from different files
Dim s As Stream = assembly.GetManifestResourceStream("CallClientAPIsVB.BigBrush.xaml")
Dim sr As StreamReader = New StreamReader(s)
Dim b As Brush = CType(XamlReader.Load(sr.ReadToEnd()), Brush)
sr.Close()

For both the JavaScript createFromXAML and XamlReader.Load methods, you should also consider that APIs that take markup as strings can actively fight the UI-designing capabilities of whatever design tool is used to define the UI markup, if you use those APIs indiscriminately.

Downloader

In 托管 code, you can create the Downloader object directly using its constructor (in 非托管 code, you had to call Control.createObject to get it from the Silverlight control).

After you construct Downloader, you typically attach handlers to one or more of the DownloadProgress, DownloadFailed, or Completed events. After that, you call Open, passing the Uniform Resource Identifier (URI) of the content to download. Finally, you call Send to initiate the request.

note

If you are handling download progress, make sure to set the async parameter to true when you call Downloader.Open.

Getting Events and Information from the Hosting Browser

You can access the HTML DOM for the document contained in the browser, and get quite a bit of information that way (see How to: Access the HTML DOM from 托管 Code). 然而, there is one particular set of information that is difficult to obtain that way: the size of the hosting browser window (minus toolbars, edges and chrome), and when the user changes that size. A quick way to get that information is to handle the events of the BrowserHost class, which exists in the System.Windows.Interop namespace. At any time, you can check the values of the BrowserHost.ActualWidth and BrowserHost.ActualHeight properties. You can also write handlers for the Resize or FullScreenChange event so that you know specifically when the ActualWidth or ActualHeight changes, and which user action (resize or toggle fullscreen) caused the change.

Rectangle and Rect

Silverlight has two similarly named types: the Rectangle class, which is in the namespace System.Windows.Shapes, and the Rect structure, which is in System.Windows. The Rectangle class is a UI element that you can place on a page to fill an area with a particular color or brush. The Rect structure is not a UI element; it is a structure that is used as a value for other types of objects that are generally rectangular, and it may or may not have an immediate visual representation. For example, Rect describes a RectangleGeometry or the bounds of an ink Stroke. Rect 包括 several utility methods so that you can compare Rect instances, or adjust values of an existing Rect.

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值