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

智能推荐

MySQL的索引结构为什么是B+树?_navicat为什么只能创建的索引结构是b树-程序员宅基地

文章浏览阅读5.3k次,点赞9次,收藏59次。博客主页:看看是李XX还是李歘歘每天分享一些包括但不限于计算机基础、算法等相关的知识点点关注不迷路,总有一些知识点是你想要的️今天的内容是 MySQL的索引结构为什么是B+树? ️先来看一下树的演化:树:非线性结构,每个节点有唯一的一个父结点和多个子结点(子树),为一对多的关系。 二叉树:每个结点最多有两颗子树,并且子树有左右之分,不能颠倒。 满二叉树:每一层的结点个数都达到了当层能达到的最大结点数。 完全二叉树:除了最..._navicat为什么只能创建的索引结构是b树

主流的开发语言和开发环境介绍-程序员宅基地

文章浏览阅读1.2k次,点赞13次,收藏9次。主流的开发语言和开发环境介绍 _开发语言

WPF自定义控件——顶级控件-程序员宅基地

文章浏览阅读595次。作为一个WPF程序员,我最希望看到的是WPF的应用,或者更确切的说是绚丽的应用,虽然限于自身的实力还不能拿出成绩来,但看到别人的作品时,心里还是有很大的宽慰——WPF是可以做出更加动人地产品的,只要你坚定的走下去,带着不满现状的追求走下去。 下图是Telerik的WPF控件,我相信很多人也下过他的DEMO,研究过他的代码,并由此激起对WPF的信心。今天我们就来仿造他的Drag..._wpf 自定义控件示例

让pandas狠狠的玩转excel_pandas对于excel来说什么时候好用-程序员宅基地

文章浏览阅读380次。先来无事,开始总结自己最近学过做过的东西。想想,数据才是所有网络和编程的核心,包括人工智能。但是自己的数据处理能力,仅仅停留在excel基础,只用代码完成过64个表格的批量筛选和提取。后来阅读和搜索发现,Python里面处理数据,pandas和numpy才是王道。所以最近开始想办法学习这两个东西。怎么学?看代码,看书,太枯燥。买课又不想花钱。经过研究发现,pandas是可以处理类似excel结..._pandas对于excel来说什么时候好用

第十二周学习总结 Progress & Lack-程序员宅基地

文章浏览阅读955次。周计划和学习总结

多特征变量序列预测(六) CEEMDAN+CNN-Transformer风速预测模型-程序员宅基地

文章浏览阅读1.1k次,点赞15次,收藏20次。本文基于前期介绍的风速数据(文末附数据集),介绍一种多特征变量序列预测模型CEEMDAN + CNN-Transformer,以提高时间序列数据的预测性能。

随便推点

Windows环境中同时安装Oracle9i 和10g_大胖黑马(授权发布)-程序员宅基地

文章浏览阅读172次。Windows环境中同时安装Oracle9i 和10g                               原创者:大胖黑马(授权发布)简单说一下在windows的同一用户下,安装Oracle的9i、10g 的方法1、安装版本需要从低到高。也就是说先安装9i的数据库,然后安装10g的数据库2、安装目录分开。3、在低版本的数据库安装完成后,最好通过..._oracle 9i 和oracle 10g同时使用

OpenCV-Python官方教程-30- 支持向量机(support vector machines, SVM)_python opencv 计算向量-程序员宅基地

文章浏览阅读305次。使用SVM进行手写数据OCR在 kNN 中我们直接使用像素的灰度值作为特征向量。这次我们要使用方向梯度直方图Histogram of Oriented Gradients (HOG)作为特征向量。在计算 HOG 前我们使用图片的二阶矩对其进行抗扭斜(deskew)处理,也就是把歪了的图片摆正。所以我们首先要定义一个函数 deskew(),它可以对一个图像进行抗扭斜处理。下面就是 deskew() 函数:..._python opencv 计算向量

CUDA入门3.1——使用CUDA实现鱼眼转全景图(OpenCV环节)_鱼眼图像展开成全景图-程序员宅基地

文章浏览阅读2.7k次。思路1,通过某种方法获取图片数据,并且了解数据结构。 2,通过某种数学公式将鱼眼画面处理成全景图。 3,通过CUDA并行运算实现鱼眼转全景图功能。 本篇主要讲述OpenCV获取图片以及指针的使用,与CUDA无关。获取图片数据OpenCV环境配置1 下载OpenCVOpenCV 下载驿站(百度云盘下载,同步更新)2 配置OpenCV开发环境配置的方法网上很多,可以查找。我用的是 OpenCV环境_鱼眼图像展开成全景图

JAVA-SFTP秘钥连接下载和上传文件_如何用sftp密钥下载文件到本地密钥-程序员宅基地

文章浏览阅读2.1k次,点赞3次,收藏13次。最近做SFTP连接获取文件,遇到好多坑,记录一下防止以后重复踩坑无效秘钥文件1.版本低导致文件无效2.windows系统不支持ppk秘钥文件,必须转换成pem秘钥文件com.jcraft.jsch.JSchException: invalid privatekey: C:/upload/tpl/privateKey.ppk at com.jcraft.jsch.IdentityFile.<init>(IdentityFile.java:302) at com.jc..._如何用sftp密钥下载文件到本地密钥

RPM安装nginx_nginx rpm-程序员宅基地

这篇文章介绍了通过在阿里云镜像网站下载epel源来安装nginx的过程,包括下载epel源、安装nginx rpm包、访问nginx默认网页以及nginx的安装配置文件路径和默认文件位置。

oracle expdp/impdp 数据泵导入导出命令_impdb schemas-程序员宅基地

文章浏览阅读9.2k次。oracle expdp/impdp 数据泵导入导出# 创建directory,指定导入导出时的数据存储路径create directory dir_dp as 'd:/database/dir_dp';# 授权grant read,write on directory dir_dp to user_name;# 查看目录及权限select * from dba_directories;-_impdb schemas

推荐文章

热门文章

相关标签