zoukankan      html  css  js  c++  java
  • WPF自定义控件

    参考自:http://www.cnblogs.com/zhouyinhui/archive/2007/12/01/979715.html

    1. User ControlCustom Control

       在新建一个Project的时候,WPF提供了2种自定义控件的模板:WPF User Control LibraryWPF Custom Control LibraryUser Control就像是winform中的自定义控件方式,可以理解为将多个已有的控件拼凑起来,在后台代码中直接访问这些子元素,缺点是对模板和样式的支持不好。它自动的从System.Windows.Controls.UserControl继承。

       Custom Control,其开发出来的控件才真正具有WPF风格,其对模板样式有着很好的支持,这是因为打造CustomControl时做到了逻辑代码与外观相分离,即使换上一套完全不同的视觉树其同样能很好的工作,就像WPF内置的控件一样。
      
    在使用Visual Studio打造控件时,UserControlCustomControl的差别就更加明显,在项目中添加一个UserControl时,我们会发现设计器为我们添加了一个XAML文件以及一个对应的.CS文件,然后你就可以像设计普通窗体一样设计该UserControl;如果我们是在项目中添加CustomControl,情况却不是这样,设计器会为我们生成一个.CS文件,该文件用于编写控件的后台逻辑,而控件的外观却定义在了软件的应用主题(Theme)中了(如果你没有为软件定义通用主题,其会自动生成一个通用主题themes\generic.xaml,然后主题中会自动为你的控件生成一个Style),并将通用主题与该控件关联了起来。这也就是CustomControl对样式的支持度比UserControl好的原因。

    generic.xaml中我们需要定义该控件的默认主题,这就像是WPF内置的控件一样,他们有自己的默认的主题。然后我们需要做的工作就是在.cs文件中编写后台逻辑,在generic.xaml中编写默认的UI

    CustomControl会从System.Windows.Controls.Control继承,如下:

        public class CustomControl1 : Control

        {

            static CustomControl1()

            {

                DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));

            }

    }

    它总有一个Static的构造方法,并自动调用DefaultStyleKeyProperty.OverrideMetadata 方法。DefaultStyleKey是指当有theme style定义或应用的时候,用它来指定该控件的默认样式,为该metadatadefaultvalue指定为typeof(CustomControl1),这个值代表着,我们将在资源字典中查找一个键值为typeof(CustomControl1)Style来做为控件的默认样式.而这个样式刚好被我们定义在了Generic.xaml:

        <Style TargetType="{x:Type local:CustomControl1}">

            <Setter Property="Template">

                <Setter.Value>

                    <ControlTemplate TargetType="{x:Type local:CustomControl1}">

                        <Border Background="{TemplateBinding Background}"

                                BorderBrush="{TemplateBinding BorderBrush}"

                                BorderThickness="{TemplateBinding BorderThickness}">

     

                        </Border>

                    </ControlTemplate>

                </Setter.Value>

            </Setter>

        </Style>

    这是大家可能有个疑问,上面XAML中的Style并没有指定Key值啊,而我们的控件要求的默认样式Key值为typeof(CustomControl1),并且资源字典中的元素肯定是要有Key的? 这是Style的基本知识了,在WPF中,为Style指定Key时有两种方式:一是明确指定Key,而是在没有明确指定Key的情况下指定TargetTypeWPF会自动地将其可Key设置为typeof(TargetType)

    另外,如果不希望element或者control用默认的主题style,可以设置OverridesDefaultStyletrue

    2. 使用TemplatePartAttribute特性

       TemplatePartAttribute
    用在一个控件类的定义前面,控件的作者通过它来告诉template的作者在控件的模板中必须有TemplatePart中指定的类型和名称的元素,这个控件才能正常工作。当然该特性只是为了标识这一个行为,如果不写,也不会报错的。

       例如我们看WPF Toolkit中的Calendar类,定义如下:

        [TemplatePart(Name = Calendar.ElementRoot, Type = typeof(Panel))]

        [TemplatePart(Name = Calendar.ElementMonth, Type = typeof(CalendarItem))]

        public partial class Calendar : Control

        {

            #region Constants

     

            private const string ElementRoot = "PART_Root";

            private const string ElementMonth = "PART_CalendarItem";

    }

     

       generci.xaml中可以看到如何使用:

    <Setter Property="Template">

                <Setter.Value>

                    <ControlTemplate TargetType="local:Calendar">

                        <StackPanel Name="PART_Root" HorizontalAlignment="Center">

                            <primitives:CalendarItem

                                Name="PART_CalendarItem"

                                Style="{TemplateBinding CalendarItemStyle}"

                                Background="{TemplateBinding Background}"

                                BorderBrush="{TemplateBinding BorderBrush}"

                                BorderThickness="{TemplateBinding BorderThickness}"                           

                                />

                        </StackPanel>

                    </ControlTemplate>

                </Setter.Value>

            </Setter>
    另外我们可以在OnApplyTemplate 通过GetTemplateChild方法来得到相应的元素,注意GetTemplateChild方法过时了,用System.Windows.FrameworkTemplate.FindName(System.String,System.Windows.FrameworkElement)代替它。

          public override void OnApplyTemplate()

            {

                if (_monthControl != null)

                {

                    _monthControl.Owner = null;

                }

     

                base.OnApplyTemplate();

     

                _monthControl = GetTemplateChild(ElementMonth) as CalendarItem;

     

                if (_monthControl != null)

                {

                    _monthControl.Owner = this;

                }

     

                this.CurrentDate = this.DisplayDate;

                UpdateCellItems();

            }

     

       为了实现效果随着用户操作系统主题改变而动态改变,你至少有两种方法来实现:(1)监听系统消息WM_THEMECHANGE,然后切控件界面.(2)将系统主题对应的Style放置在控件解决方案的themes文件夹下,比如与Vista Aero向对应的放在themes\Aero.NormalColor.xaml,与蓝色的Windows XP主题对应的放在themes\Luna.NormalColor.xaml,Window经典主题相对应的放在themes\Classic.xmal,相信大家已经看出规律:themes\主题名.颜色名.xaml,其中经典主题没有颜色名.这样当用户切换主题时我们的控件就会切换到对应的Style,如果我们没有提供用户当前的主题所对应的样式则调用themes\Generic.xaml

     

       另外需要说明的是AssemblyInfo中的ThemeInfoAttribute,结构大概如下:

    [assembly:ThemeInfo(

        // Specifies the location of theme specific resources

        ResourceDictionaryLocation.SourceAssembly,

        // Specifies the location of non-theme specific resources:

    ResourceDictionaryLocation.SourceAssembly)]

     

    当一个Assembly被该特性标识后,我们可以称它为Themed assembly,注意ResourceDictionaryLocation有三个枚举值,NoneSourceAssemblyExternalAssemblyNone指不用themesSourceAssembly指定ResourceDictionaries存在于将要被themed的控件所在的Assembly中;ExternalAssemblyResourceDictionaries存在于其他程序集中,但不包括将要被themed的控件所在的Assembly

    ThemeInfo的第一个参数指定和操作系统主题相关的ResourceDictionaries,如果为None则表示不随操作系统的主题变化而变化。第二个参数指generic主题所在位置,也就是generic.xaml,注意第一个参数的优先级高,也就是说如果没有找到和操作系统相应的主题,才会去找有没有generic.xaml

    这里有一个例子,参考自http://www.cnblogs.com/yuxs/archive/2007/05/24/758863.html,是一个WPF 的日历控件,点击这里下载我的测试solution。

  • 相关阅读:
    18-10-11 关于触发器的学习
    18-10-29 关于设计器机器人等安装遇到的问题的解决方法
    18-10-25 全局函数测试总结 创建时间的目录 网页获取数据 写入数据
    18-09-08 关于Linux 的安装遇到的一些小坑
    18-08-27 机器人自动化之页面表格数据的定位拾取
    day 96 关于分页的使用
    day73 母版 中间件
    通过 U 盘启动重装 macOS 系统
    MAMP 环境下为 php 添加 pcntl 扩展
    使用 Composer 安装 Laravel 框架
  • 原文地址:https://www.cnblogs.com/bear831204/p/1360757.html
Copyright © 2011-2022 走看看