zoukankan      html  css  js  c++  java
  • Silverlight 用户控件与自定义控件详解

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

     

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

     

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

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

    Get Microsoft 
Silverlight

    解剖用户控件

    首先,让我们解剖一个典型的用户控件看看,充分了解下它是怎么工作的这是关键。在下面我们控件中一部分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定义的,我们在后台代码中通过这个名字自动获取了这个变量。 而且ButtonClick事件与后台代码中的事件处理程序奇迹般的挂接了。实际上这是编译程序和调用方法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。

    按 Ctrl+C 复制代码
    按 Ctrl+C 复制代码

    首先第一,注意BorderTemplateBinding语句,它是控件中一个重要的功能。您可以直接在你的控件代码中定义一个依赖项属性绑定。由于自定义控件继承Control,你将自动继承Background BorderBrushBorderThickness 和其他属性。请注意 我这里我没有给按钮添加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的类型是PanelGrid的类型是Control)、ClickButton的类型是ButtonBase。这些不是严格要求,但是当你调用写好的自定义控件时,它们能帮助Expression Blend了解模板的要求。我总是控件层次结构申明需要的最小类型,使Template更加灵活。比如我用ButtonBase而不是Button,因为我只要用到定义ButtonBase基类的Click事件。同样LayoutRoot也一样,我只需要它的BackGround 属性。 

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

    最后,最重要的部分是”OnApplyTemplate”,此方法当Template加载完后被调用。这是我们早期的机会,抢先对Templatecontrols的引用,即控件中申明的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/

  • 相关阅读:
    基于SSM框架web搜索功能的实现
    使用APICloud打包webapp
    mac如何运行vue项目
    前端和算法实现:给网站上加上自己的水印(以后用上)
    jQuery常用代码片段
    关于vscode的个人配置
    听说你想用git,安装一下咯
    vue创建项目的一种方法
    用js控制css动画效果@keyframes
    axios的封装和拦截
  • 原文地址:https://www.cnblogs.com/jason_zhou/p/2548045.html
Copyright © 2011-2022 走看看