Silverlight 用户控件与自定义控件详解-程序员宅基地

技术标签: c#  ui  runtime  

在Silverlight中你如果想把UI封装成单独的一部分或者创建一个新的页面,你可能会在Visual Studio中通过右击 “项目-> 添加-> 添加新项->Silverlight用户控件” 这样来创建控件。如果你是这么做的,那么这篇文章非常适合你。它将适用于任何基于XAML技术:WPF、silverlight、Windows Phone 和Windows 8 Runtime。

 

尽管用户控件很棒,它们能快速的拼在一起,或一次又一次的重复使用,这是它们的很大一个价值所在。但是如果我告诉你还有另一种控件类型,具有干净的代码、更强大性能更好,而且比用户控件的方式更加灵活、重复的使用,那它将会是大量开发人员的最爱吗?

 

其实这个你早就知道,因为你已经一直在使用他们:Button、ListBox、ItemsControls、Grid、StackPanel等。你可以查看Xaml Style彻底改变控件的外观和体验,而不触及任何代码。这是多么强大的想法,看看下面一个Silverlight ListBox 行星DEMO 。在左边,你会看到一个绑定了行星名单的ListBox。在右边,你能看到一个太阳系,但事实上,这也是一个ListBox。这里没有涉及到额外的代码,完全是由修改Template达到效果。你可以按上下键,它有正常ListBox的功能。

 

让我重复一遍:做到这一点我没有添加任何后台代码到ListBox。事实上,该页面后台代码完全是空的。如果你不相信,这里有源码下载 

解剖用户控件

 

首先,让我们解剖一个典型的用户控件看看,充分了解下它是怎么工作的这是关键。在下面我们控件中一部分XAML确定了布局,为了保持它是一个简单的例子,里有只一个Grid和一个Button。

复制代码
1  < UserControl  x:Class ="MyApp.SilverlightControl1"
2      xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3      xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" >
4     
5      < Grid  x:Name ="LayoutRoot"  Background ="White" >
6          < Button  Content ="Click Me"  Click ="Button_Click"  Opacity =".5"   />
7      </ Grid >
8  </ UserControl >
复制代码

 

我们控件的后台代码:

复制代码
 1  using System.Windows;
 2  using System.Windows.Controls;
 3  using System.Windows.Media;
 4 
 5  namespace SolarSystemRetemplate
 6 {
 7      public  partial  class SilverlightControl1 : UserControl
 8     {
 9          public SilverlightControl1()
10         {
11             InitializeComponent();
12         }
13 
14          private  void Button_Click( object sender, RoutedEventArgs e)
15         {
16             LayoutRoot.Background =  new SolidColorBrush(Colors.Red);
17         }
18     }
19 }
复制代码

 

这里有两个地方值得注意:”LayoutRoot”是在XAML中使用X:Name定义的,我们在后台代码中通过这个名字自动获取了这个变量。 而且Button的Click事件与后台代码中的事件处理程序奇迹般的挂接了。实际上这是编译程序和调用方法InitializeComponent处理了这一切--但是有趣的是这个方法在这里不存在。实际上为了表示这是一个局部类,Visual Studio为你私底下创建了一个小(秘密)文件。你可以右击方法选择“转到定义“。下面是该文件的内容:

复制代码
 1  namespace MyApp {    
 2     
 3      public  partial  class SilverlightControl1 : System.Windows.Controls.UserControl {
 4         
 5          internal System.Windows.Controls.Grid LayoutRoot;
 6         
 7          private  bool _contentLoaded;
 8         
 9          ///   <summary>
10           ///  InitializeComponent
11           ///   </summary>
12         [System.Diagnostics.DebuggerNonUserCodeAttribute()]
13          public  void InitializeComponent() {
14              if (_contentLoaded)
15                  return;
16             _contentLoaded =  true;
17             System.Windows.Application.LoadComponent( this, 
18                  new System.Uri( " /MyApp;component/SilverlightControl1.xaml ",
19                 System.UriKind.Relative));
20              this.LayoutRoot = ((System.Windows.Controls.Grid)( this.FindName( " LayoutRoot ")));
21         }
22     }

23 }

复制代码

 

你会注意到LayoutRoot在这里被定义成internal,并且它的赋值使用了“FindName”方法。

 

这就是使用用户控件的好处之一:它会自动为你做很多工作,但自定义控件则需要你自己来完成这些工作(但是如果考虑到你的效率的话,这并不是那么糟糕)。这里说明下:用户控件只是另一种自定义控件。

 

解剖自定义控件

自定义控件不像用户控件会有一个xaml和一个后台代码组成,换成除了一个默认的XAML Template以外其余的全部是代码。你可以认为XAML Template和用户控件的XAML文件作用一样,但是这里要注意,XAML Template可以实现任何改变。这里要注意另外一件事件,因为Template不具有Visual Studio为您生成的隐藏代码局部类,所以任何事件处理程序不能在Template中定义。那么我们怎样重新创建上述用户控件为一个自定义控件呢?

 

对于Silverlight这是很容易的,右键单击您的项目,选择 “添加 -> 新建项 –> Silverlight模板化控件”。WPF 和Windows Phone不伴随此模板,所以你必须手工通过创建一个类和一个通用模板文件。你做到了这一点后你会发现两个新文件:首先一个简单的C#类,第二个是在\Themes\Generic.xaml下创建了一个新文件。第二个文件汇集了你所有控件的Template样式。它的名字必须是Generic.xaml而且必须在该目录下,这样自定义控件才能使用所有的Template。

 

下面让我们一起来看看Template是怎么写的,和上面用户控件一样也是添加了一个Button和一个Grid。

复制代码
 1  < ResourceDictionary
 2       xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3      xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
 4      xmlns:local ="clr-namespace:MyApp" >
 5 
 6      < Style  TargetType ="local:TemplatedControl1" >
 7          < Setter  Property ="Template" >
 8              < Setter.Value >
 9                  < ControlTemplate  TargetType ="local:TemplatedControl1" >
10                      < Border  Background =" {TemplateBinding Background} "
11                              BorderBrush =" {TemplateBinding BorderBrush} "
12                              BorderThickness =" {TemplateBinding BorderThickness} " >
13                          < Grid  x:Name ="LayoutRoot" >
14                              < Button  x:Name ="ClickButton"  Content ="Click me!"  Opacity =".5"   />
15                          </ Grid >
16                      </ Border >
17                  </ ControlTemplate >
18              </ Setter.Value >
19          </ Setter >
20      </ Style >
21  </ ResourceDictionary >
复制代码

 

首先第一,注意Border上TemplateBinding语句,它是控件中一个重要的功能。您可以直接在你的控件代码中定义一个依赖项属性绑定。由于自定义控件继承Control,你将自动继承Background、 BorderBrush、BorderThickness 和其他属性。请注意 我这里我没有给按钮添加click事件。如果这里添加了,模板将会加载失败。我们将在后台加上click处理程序,接下来,让我们一起看代码吧: 

复制代码
 1  using System.Windows;
 2  using System.Windows.Controls;
 3  using System.Windows.Controls.Primitives;
 4  using System.Windows.Media;
 5 
 6  namespace MyApp
 7 {
 8     [TemplatePart(Name= " LayoutRoot ", Type= typeof(Control))]
 9     [TemplatePart(Name =  " ClickButton ", Type =  typeof(ButtonBase))]
10      public  class TemplatedControl1 : Control
11     {
12         Control layoutRoot;
13         ButtonBase button;
14          public TemplatedControl1()
15         {
16              this.DefaultStyleKey =  typeof(TemplatedControl1);
17         }
18          public  override  void OnApplyTemplate()
19         {
20              if (button !=  null)  // unhook from previous template part
21             {
22                 button.Click -=  new RoutedEventHandler(button_Click);
23             }    
24             button = GetTemplateChild( " ClickButton ")  as ButtonBase;
25              if (button !=  null)
26             {
27                 button.Click +=  new RoutedEventHandler(button_Click);
28             }
29             layoutRoot = GetTemplateChild( " LayoutRoot ")  as Panel;
30              base.OnApplyTemplate();
31         }
32 
33          private  void button_Click( object sender, RoutedEventArgs e)
34         {
35             layoutRoot.Background =  new SolidColorBrush(Colors.Red);
36         }
37     } 38 } 
复制代码

 

 

首先在控件中声明”TemplatePart”,它指定预期元素的名称和和类型。在demo中 LayoutRoot的类型是Panel(Grid的类型是Control)、ClickButton的类型是ButtonBase。这些不是严格要求,但是当你调用写好的自定义控件时,它们能帮助Expression Blend了解模板的要求。我总是控件层次结构申明需要的最小类型,使Template更加灵活。比如我用ButtonBase而不是Button,因为我只要用到定义ButtonBase基类的Click事件。同样LayoutRoot也一样,我只需要它的BackGround 属性。 

 

在构造函数中,我定义了”DefaultStyleKey”,它告诉Framework我在Themes\Generic.xaml中定义了默认Template。 

 

最后,最重要的部分是”OnApplyTemplate”,此方法当Template加载完后被调用。这是我们早期的机会,抢先对Template中controls的引用,即控件中申明的TemplatePart。在这种情况下,我抢先引用在Template中定义ButtonBase,如果找到它,我将给它添加一个click事件处理程序。此外,如果一个新的Template被应用,一定要记住去除以前实例中的事情处理程序。同样重要要注意的是Template部件总是可选的!所以你要检查所有引用template的部件是否为null。 

 

添加Visual States到控件

 现在添加一些鼠标状态到我们的控件,并控制动画何时触发。在后台代码中我们定义的添加两个TemplateVisualState属性:

1 [TemplateVisualState(GroupName =  " HoverStates ", Name =  " MouseOver ")]
2 [TemplateVisualState(GroupName =  " HoverStates ", Name =  " Normal ")] 

 

接下来给控件添加visual state的触发:

复制代码
 1  bool isMouseOver;
 2  protected  override  void OnMouseEnter(System.Windows.Input.MouseEventArgs e)
 3 {
 4     isMouseOver =  true;
 5     ChangeVisualState( true);
 6      base.OnMouseEnter(e);
 7 }
 8  protected  override  void OnMouseLeave(System.Windows.Input.MouseEventArgs e)
 9 {
10     isMouseOver =  false;
11     ChangeVisualState( true);
12      base.OnMouseLeave(e);
13 }
14 
15  private  void ChangeVisualState( bool useTransitions)
16 {
17      if (isMouseOver)
18     {
19         GoToState(useTransitions,  " MouseOver ");
20     }
21      else
22     {
23         GoToState(useTransitions,  " Normal ");
24     }
25 }
26 
27  private  bool GoToState( bool useTransitions,  string stateName)
28 {
29      return VisualStateManager.GoToState( this, stateName, useTransitions);  30 }
复制代码

 

这正是我们需要的所有代码。它非常简单。如果鼠标停留,则触发MouseOver状态,否则则触发正常状态。请注意,实际上我们没有真正定义什么是”MouseOver”,这是Template的工作。好接下来让我们来定义:

复制代码
 1  < ControlTemplate  TargetType ="local:TemplatedControl1" >
 2      < Border  Background =" {TemplateBinding Background} "
 3              BorderBrush =" {TemplateBinding BorderBrush} "
 4              BorderThickness =" {TemplateBinding BorderThickness} " >
 5          < VisualStateManager.VisualStateGroups >
 6              < VisualStateGroup  x:Name ="HoverStates" >
 7                  < VisualState  x:Name ="MouseOver" >
 8                      < Storyboard >
 9                          < ColorAnimation
10                               Storyboard.TargetName ="BackgroundElement"
11                              Storyboard.TargetProperty ="(Rectangle.Fill).(SolidColorBrush.Color)"
12                              To ="Yellow"  Duration ="0:0:.5"   />
13                      </ Storyboard >
14                  </ VisualState >
15                  < VisualState  x:Name ="Normal" >
16                      < Storyboard >
17                          < ColorAnimation
18                               Storyboard.TargetName ="BackgroundElement"
19                              Storyboard.TargetProperty ="(Rectangle.Fill).(SolidColorBrush.Color)"
20                              To ="Transparent"  Duration ="0:0:.5"   />
21                      </ Storyboard >
22                  </ VisualState >
23              </ VisualStateGroup >
24          </ VisualStateManager.VisualStateGroups >
25          < Grid  x:Name ="LayoutRoot" >
26              < Rectangle  x:Name ="BackgroundElement"  Fill ="Transparent"   />
27              < Button  x:Name ="ClickButton"  
28                      Content ="Click me!"  Opacity =".5"   />
29          </ Grid >
30      </ Border >  31 </ ControlTemplate >
复制代码

 好了,你现在有一个控件,当ButtonBase被点击以及鼠标悬停或离开时,Panel的背景色会改变,这样可以解决于很多控件,不用重写代码。

 


版权申明
出处: http://lmyhao.cnblogs.com/
版权:本文版权归作者和博客园共有
转载:欢迎转载,为了保存作者的创作热情,请按要求【转载】,谢谢

要求:未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任 

  

转载于:https://www.cnblogs.com/yinxiangpei/articles/2617351.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30472035/article/details/99603731

智能推荐

python编码问题之encode、decode、codecs模块_python中encode在什么模块-程序员宅基地

文章浏览阅读2.1k次。原文链接先说说编解码问题编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码。 Eg:str1.decode('gb2312') #将gb2312编码的字符串转换成unicode编码str2.encode('gb2312') #将unicode编码..._python中encode在什么模块

Java数据流-程序员宅基地

文章浏览阅读949次,点赞21次,收藏15次。本文介绍了Java中的数据输入流(DataInputStream)和数据输出流(DataOutputStream)的使用方法。

ie浏览器无法兼容的问题汇总_ie 浏览器 newdate-程序员宅基地

文章浏览阅读111次。ie无法兼容_ie 浏览器 newdate

想用K8s,还得先会Docker吗?其实完全没必要-程序员宅基地

文章浏览阅读239次。这篇文章把 Docker 和 K8s 的关系给大家做了一个解答,希望还在迟疑自己现有的知识储备能不能直接学 K8s 的,赶紧行动起来,K8s 是典型的入门有点难,后面越用越香。

ADI中文手册获取方法_adi 如何查看数据手册-程序员宅基地

文章浏览阅读561次。ADI中文手册获取方法_adi 如何查看数据手册

React 分页-程序员宅基地

文章浏览阅读1k次,点赞4次,收藏3次。React 获取接口数据实现分页效果以拼多多接口为例实现思路加载前 加载动画加载后 判断有内容的时候 无内容的时候用到的知识点1、动画效果(用在加载前,加载之后就隐藏或关闭,用开关效果即可)2、axios请求3、map渲染页面4、分页插件(antd)代码实现import React, { Component } from 'react';//引入axiosimport axios from 'axios';//引入antd插件import { Pagination }_react 分页

随便推点

关于使用CryPtopp库进行RSA签名与验签的一些说明_cryptopp 签名-程序员宅基地

文章浏览阅读449次,点赞9次,收藏7次。这个变量与验签过程中的SignatureVerificationFilter::PUT_MESSAGE这个宏是对应的,SignatureVerificationFilter::PUT_MESSAGE,如果在签名过程中putMessage设置为true,则在验签过程中需要添加SignatureVerificationFilter::PUT_MESSAGE。项目中使用到了CryPtopp库进行RSA签名与验签,但是在使用过程中反复提示无效的数字签名。否则就会出现文章开头出现的数字签名无效。_cryptopp 签名

新闻稿的写作格式_新闻稿时间应该放在什么位置-程序员宅基地

文章浏览阅读848次。新闻稿是新闻从业者经常使用的一种文体,它的格式与内容都有着一定的规范。本文将从新闻稿的格式和范文两个方面进行介绍,以帮助读者更好地了解新闻稿的写作_新闻稿时间应该放在什么位置

Java中的转换器设计模式_java转换器模式-程序员宅基地

文章浏览阅读1.7k次。Java中的转换器设计模式 在这篇文章中,我们将讨论 Java / J2EE项目中最常用的 Converter Design Pattern。由于Java8 功能不仅提供了相应类型之间的通用双向转换方式,而且还提供了转换相同类型对象集合的常用方法,从而将样板代码减少到绝对最小值。我们使用Java8 功能编写了..._java转换器模式

应用k8s入门-程序员宅基地

文章浏览阅读150次。1,kubectl run创建pods[root@master ~]# kubectl run nginx-deploy --image=nginx:1.14-alpine --port=80 --replicas=1[root@master ~]# kubectl get podsNAME READY STATUS REST...

PAT菜鸡进化史_乙级_1003_1003 pat乙级 最优-程序员宅基地

文章浏览阅读128次。PAT菜鸡进化史_乙级_1003“答案正确”是自动判题系统给出的最令人欢喜的回复。本题属于 PAT 的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”,否则输出“答案错误”。得到“答案正确”的条件是: 1. 字符串中必须仅有 P、 A、 T这三种字符,不可以包含其它字符; 2. 任意形如 xPATx 的字符串都可以获得“答案正确”,其中 x 或者是空字符串,或..._1003 pat乙级 最优

CH340与Android串口通信_340串口小板 安卓给安卓发指令-程序员宅基地

文章浏览阅读5.6k次。CH340与Android串口通信为何要将CH340的ATD+Eclipse上的安卓工程移植到AndroidStudio移植的具体步骤CH340串口通信驱动函数通信过程中重难点还存在的问题为何要将CH340的ATD+Eclipse上的安卓工程移植到AndroidStudio为了在这个工程基础上进行改动,验证串口的数据和配置串口的参数,我首先在Eclipse上配置了安卓开发环境,注意在配置环境是..._340串口小板 安卓给安卓发指令