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

智能推荐

新型无线充电技术:能为人体植入设备充电_无线充电植入人体-程序员宅基地

文章浏览阅读2.2k次。原标题:无线充电技术获新突破 可给体内植入设备充电一项新的突破性的无线充电技术可以让新的健康跟踪监测工具更深地植入我们的体内——如肝脏、心脏,甚至大脑中。这项无线充电技术名为“中场无线传输”(mid-field wireless transfer),可以给深植入人体内的微型电子设备(如传感器、起搏器和神经刺激器)充电。它只要用一张信用卡大小的设备,就可以在体外对这些植入设备_无线充电植入人体

NIKE旗下品牌JORDAN发力新零售, 瞄准了天猫小黑盒-程序员宅基地

文章浏览阅读205次。8090一代,从小到大穿球鞋的时间,往多了说可能有20年,往少了大抵也得有个10年左右,但球鞋真正成了潮流圈口口相传的大众文化,还得是近几年的事情。当然,在这个有关鞋的文化里,还有一群更小众的群体——sneaker(sneaker,指热爱和收藏球鞋的人)。他们是sneaker文化的忠实追随者,他们眼中永恒的传奇,是Air Jordan。现如今,多了一个让万千sneaker欢欣的消息,他们终于可以在..._showcase jordan

opencv 图像梯度(python)_cv2.cv_64f-程序员宅基地

文章浏览阅读3.2k次,点赞6次,收藏20次。图像梯度图像梯度Sobel理论基础计算水平方向偏导数的近似值计算垂直方向偏导数的近似值Sobel算子及函数使用注意点:参数ddepth方向计算x方向和y方向的边缘叠加Scharr算子及函数使用Sobel算子和Scharr算子的比较Laplacian算子及函数使用算子总结图像梯度图像梯度计算的是图像变化的速度。对于图像的边缘部分,其灰度值变化较大,梯度值也较大;相反,对于图像中比较平滑的部分,其灰度值变化较小,相应的梯度值也较小。图像梯度计算需要求导数,但是图像梯度一般通过计算像素值的差来得到梯度的近_cv2.cv_64f

DataGrip 链接不上linux 上的mysql_在linux上安装了mysql在datagrip中连接后无法跟在finalshell中的数据同步-程序员宅基地

文章浏览阅读1.6k次。DataGrip 链接不上linux 上的mysql使用Centos7 linux上安装的MySQL 5.7.35安装步骤可参考 https://blog.csdn.net/qq_33554286/article/details/88357634安装完MySQL 若提示是这样的ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this stateme_在linux上安装了mysql在datagrip中连接后无法跟在finalshell中的数据同步

2023年Twitter营销应该知道的一些数据_twitter全球用户分布 2021-程序员宅基地

文章浏览阅读366次。不过,即使如此,Twitter 的用户群体仍然保持着强大的虽然目前拥有30亿活跃用户的Facebook方面稍显逊色,但Twitter在新闻传播领域的重要性不可低估,因为人们仍倾向于通过Twitter获取新闻趋势。较成熟的用户(25至49岁)占比高达59.2%,这最新显示于Snapchat和Instagram,Twitter更受到成熟用户的欢迎(Statista,2021 年数据)。另外,周末有趣,使用积极型的表情符号会有所增加,周五和周六的积极型表情符号最多达77.7%,比工作日增加了约1.9%。_twitter全球用户分布 2021

【玩转Linux】标准IO函数_linux io函数-程序员宅基地

文章浏览阅读657次,点赞2次,收藏2次。标准IO函数  1.fopen() / fclose()  2.fgetc()和getc()和getchar(),以及fputc()和putc()和putchar()  3.fgets()/gets()/fputs()/puts()  4.feof()/ferror()  5.fread(/fwrite()  6.fseek()/ftell()/rewind  7.printf()/fprintf()/sprintf()/snprintf()/scan_linux io函数

随便推点

2022/5/8 SSM框架整合增删改查(模糊查询+分页)(详细案例)_ssm模糊查询,反显,分页整合-程序员宅基地

文章浏览阅读835次。目录1丶项目结构2丶所需依赖3丶配置mybatis-config.xml文件4丶配置springmvc-servlet.xml文件5丶配置database.properties文件6丶配置applicationContext-mybatis.xml文件7丶配置web.xml文件8丶运行效果 8.1丶首页界面 8.2丶组合查询+分页 8.3丶添加 8.4丶修改 8.5丶删除..._ssm模糊查询,反显,分页整合

java开发怎么打补丁_[Java教程]【NC】出补丁与打补丁-程序员宅基地

文章浏览阅读1.6k次。[Java教程]【NC】出补丁与打补丁0 2021-01-02 20:00:06出补丁什么是补丁?如果我们的衣服上破了一个洞,可以拿块布给补上,这块布就是“补丁”。程序也是一样,如果系统有了漏洞或者想要完善某些功能,都需要打补丁。如何出补丁选中你需要打补丁的文件(一般你修改了哪些文件,就需要对哪些文件出补丁),右键选择“导出”,选中“UAPx6 Service Pack”,点击“下一..._java项目 补丁式开发 wrs

联想e470锁定计算机,联想ThinkPad笔记本Fn键关闭与启用方法-程序员宅基地

文章浏览阅读2.7k次。联想ThinkPad系列笔记本的F1-F12键跟传统的功能键是相反的,传统的功能键都是按Fn+功能键来实现快捷功能,ThinkPad默认设置是直接按功能键就实现快捷功能。不少用户使用很不习惯:比如按F5刷新网页,Alt+F4关闭窗口或关机等失效。既然大部分朋友都不习惯这个操作,那么小编就来教大家怎么关闭与启用Fn功能键,帮助大家把联想ThinkPad笔记本设置与一般的笔记本一样,希望大家喜欢。1...._thinkpad e470快捷键设置

vue中实现 图片的放大缩小和拖拽_vue图片放大缩小拖动-程序员宅基地

文章浏览阅读2.4k次。【代码】vue中实现 图片的放大缩小和拖拽。_vue图片放大缩小拖动

《FLUENT 14.0超级学习手册》——2.5 FLUENT 14.0的基本操作-程序员宅基地

文章浏览阅读1.9k次,点赞3次,收藏6次。本节书摘来自异步社区《FLUENT 14.0超级学习手册》一书中的第2章,第2.5节,作者: 唐家鹏 更多章节内容可以访问云栖社区“异步社区”公众号查看。2.5 FLUENT 14.0的基本操作FLUENT 14.0超级学习手册本节将介绍FLUENT 14.0的用户界面和一些基本操作。在本书中,若不作特殊说明,FLUENT均指FLUENT 14.0版..._fluent中参考值如何确定

理论计算机科学逻辑博导,软件学院研究生论文导师一览表-程序员宅基地

文章浏览阅读106次。软件学院研究生论文导师一览表软件学院研究生论文导师一览表更新日期:2012年9月19日导师序号单位123456789软件10学11院121314151617181920212223242526272829303132333435363738394041424344454647484950应用所件51525354姓名常会友朝红阳冯剑琳孙伟温武少李文军余阳高成英硕/博导专业研究方向博导博导博导博导博导..._理论计算机博导