zoukankan      html  css  js  c++  java
  • 艾伟_转载:WPF/Silverlight陷阱:XAML自定义控件的嵌套内容无法通过名称访问 狼人:

    为了说明这个问题,假定我们需要实现一个具有特殊功能的按钮控件。编写Xaml文件如下:

    <Button x:Class="TestWpf.XamlButton"
        xmlns
    ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x
    ="http://schemas.microsoft.com/winfx/2006/xaml">
    Button>

    对 Code Behind类,唯一的改动是把向导生成的基类从UserControl改成Button:

    public partial class XamlButton : Button
    {
        
    public XamlButton()
        {
            InitializeComponent();
        }
    }

     然后在主窗体中放上这个新创建的控件:

    <Window x:Class="TestWpf.Window1"
        xmlns
    ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x
    ="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local
    ="clr-namespace:TestWpf"
        Title
    ="Window1" Height="300" Width="300">
        
    <StackPanel>
            
    <local:XamlButton x:Name="xamlBtn" Click="xamlBtn_Click">
                
    <TextBlock x:Name="xamlText" Text="Xaml Button" />
            
    local:XamlButton>
        
    StackPanel>
    Window>

    看起来很平常的代码,但是很遗憾,编译无法通过。Visual Studio会告诉我们这样的信息:

    无法在元素“TextBlock”上设置 Name 属性值“xamlText”。“TextBlock”位于元素“XamlButton”的范围之内,该元素已经具有在其他范围中定义时注册的名称。

    或许是翻译的问题,这段错误提示可以说是文不对题,因为我们可以肯定的说:这个程序里面再没有别的地方用到xamlBtn或者xamlText这样的名称。

    如果我们换个方式,不再用XAML声明控件,而是用C#代码定义:

    public class CsButton : Button
    {
    }

    然后再试试用同样的方式把这个控件加到主界面上:

    <local:CsButton x:Name="csBtn" Click="csBtn_Click">
        
    <TextBlock x:Name="csText" Text="Cs Button" />
    local:CsButton>

     完全没有问题!csText通过代码也是可以访问的,Click处理方法可以证明这一点:

    private void csBtn_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show(csText.Text);
    }

    如果用Silverlight来实验同样的代码,结果会稍有不同。在Silverlight XAML中添加x:Name并不会报错 ,但是运行时就会出现问题——xamlText总是等于null,并且FindName("xamlText")同样返回null,因此文本内容用自动生成的代码是无法访问的。但是以Button作为根对象来查找文本框,却能够找到:

    xamlText = (TextBlock)xamlBtn.Content;
    HtmlPage.Window.Alert(xamlText.Text);

    此实验可以说明:用XAML来声明自定义控件是存在严重问题的,控件内容中的对象无论是通过自动生成的成员变量还是用根容器的FindName都无法访问。要绕开这个限制,有以下几种可能的途径:

    1. 使用C#手工构造自定义控件,不用XAML声明;

    2. 使用自定义控件的FindName找到内容对象,然后手工绑定到成员变量;

    3. 使用RegisterName手工管理命名空间。此方法我没有实验,并且它仅对WPF有效,Silverlight是没有这个方法的。

    上述方法2是我们最初曾经使用的方法,但是目前已经放弃了,因为手工绑定需要程序员自己编写大量无聊的代码,并且非常容易出错。方法1是目前采用的方法,为此我们删除了许多原先已经写好的XAML,全部改用C#代码手工创建,其实这个工作并不算困难,因为大多数时候XAML到C#的映射还是比较直观的,但由于Silverlight的自身设计的限制,存在一个明显的限制:

    不像WPF,Silverlight里面没有简单的办法可以从代码创建一个Template。在WPF中,可以指定Template.VisualTree,但是Silverlight没有提供这个属性,所以要从代码里是生成Template是很困难的。网上曾有人提供过一个思路,即用字符串拼出模板的XAML字符串,再用XamlReader.Load读出模板对象。这个方法虽然可行,但比较丑陋,拼字符串总是下下策,维护也很困难。我们现在使用的是一个折中的办法,Template还是用XAML来保存,但是需要编写一些自定义代码,以便把C#控件和XAML中的模板关联起来。不幸的是,这个办法导致本来是同一个控件的内容不得不在两个地方分别维护,还要时时注意两边的代码保持同步,因此也不能说是一个完满的解决办法。

    后记: 我现在主要的工作,是基于Silverlight开发一个应用程序平台,在此过程中已经感觉到Silverlight的一些不足,包括实现上不够完整(比如说缺少Decorator,没有OnRender),部分API在版本之间的大幅度变动(针对Silverlight 3 Beta的一些例子现在都已经失效了),也有设计上的复杂性导致的一些微妙的问题,本文所提到的就是这些问题的其中之一,给框架层面的实现带来了不少麻烦。此外值得一提的是,我们现在编译的xap包大小已经长到了800k以上,可以说和Adobe Flex编译出来的文件大小不相上下。对于文件大小“贡献”最多的是System.Windows.Controls、System.Windows.Controls.Data、System.Windows.Controls.Toolkit和System.Xml.Serialization这四个程序集,其中除了最后一个或许可以考虑以后不再用XML序列化,前面3个是不可能不使用的。所以Flex文件编译以后6、7百K的体积真的算不上大,Silverlight同样是这个水平,那些总是叫唤文件太大的同学也应该了解,RIA程序的尺寸基本上也就这样了,除了用RSL之类技术切割一下以外,已经没有多大优化的余地了。如果这个大小您也不能接受的话,那还是用回Ajax吧。

  • 相关阅读:
    Java实现 LeetCode 27 移除元素
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 26 删除排序数组中的重复项
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 25 K个一组翻转链表
    Java实现 LeetCode 24 两两交换链表中的节点
    Java实现 LeetCode 24 两两交换链表中的节点
    Java实现 LeetCode 24 两两交换链表中的节点
  • 原文地址:https://www.cnblogs.com/waw/p/2157069.html
Copyright © 2011-2022 走看看