wpf控件开发基础(5) -依赖属性实践

知识回顾

接上篇,回顾这三篇讲了什么东西

首先说明了属性的现存问题,然后介绍了依赖属性的基本用法及其解决方案,由于依赖属性以静态属性的方式存在,进而又介绍了可重写的属性元数据的使用.这是以上三篇所说明的问题.当然依赖属性的特性依然没有说完整.这两天也一直在想依赖属性,然后就是头疼,呵呵.由于WPF的依赖属性系统与整体功能都有关联,所以接下来等讲到某个特性的时候然后再来讲依赖属性.这篇我们来个简单的实践,增加点乐趣.

定义RadioButtonList和CheckBoxList

WPF内置没有这两个控件,但实际开发时,用到的会比较多.下面我们一起来创建这两个控件.

  1. 明确控件需求
  2. 定义依赖属性
  3. 重写默认样式属性元数据
  4. 用xaml定义控件样式

一.明确控件需求

控件功能如下:

  1. 可以垂直,水平排列
  2. 可以分行,列
  3. 为子项设置Margin

二.定义依赖属性

为避免重复定义属性,为两个控件定义公共类ListControl,从ListBox继承

然后定义依赖属性

namespace WPF.Controls
{
    /// <summary>
    /// this is a base class for CheckBoxList and RadioButtonList
    /// this class define some common property
    /// </summary>
    public abstract class ListControl : ListBox
    {

        #region Orientation

        /// <summary>
        /// Gets or sets a value that indicates the dimension by which child elements are stacked.
        /// This is a dependency property.
        /// </summary>
        public Orientation Orientation
        {
            get { return (Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty,value); }
        }


        public static readonly DependencyProperty OrientationProperty =
            DependencyProperty.Register("Orientation",typeof(Orientation),typeof(ListControl),new UIPropertyMetadata(Orientation.Vertical,new PropertyChangedCallback(OrientationChangedCallback)));

        public static void OrientationChangedCallback(DependencyObject d,DependencyPropertyChangedEventArgs e)
        {
            ListControl control = d as ListControl;
            if (control.Orientation == Orientation.Horizontal)
            {
                control.Columns = 0;
                control.Rows = 1;
            }
            else if (control.Orientation == Orientation.Vertical)
            {
                control.Rows = 0;
                control.Columns = 1;
            }
        }

        #endregion

        #region Columns

        /// <summary>
        /// Get or set a value that indicates which Columns list item should appear in.
        /// This is a dependency property.
        /// </summary>
        public int Columns
        {
            get { return (int)GetValue(ColumnsProperty); }
            set { SetValue(ColumnsProperty,value); }
        }

        public static readonly DependencyProperty ColumnsProperty =
            DependencyProperty.Register("Columns",typeof(int),new UIPropertyMetadata(1));

        #endregion

        #region Rows

        /// <summary>
        /// Get or set a value that indicates which Rows list item should appear in.
        /// This is a dependency property.
        /// </summary>
        public int Rows
        {
            get { return (int)GetValue(RowsProperty); }
            set { SetValue(RowsProperty,value); }
        }

        public static readonly DependencyProperty RowsProperty =
            DependencyProperty.Register("Rows",new UIPropertyMetadata(0));

        #endregion

        public Thickness SubMargin
        {
            get { return (Thickness)GetValue(SubMarginProperty); }
            set { SetValue(SubMarginProperty,value); }
        }

        public static readonly DependencyProperty SubMarginProperty =
            DependencyProperty.Register("SubMargin",typeof(Thickness),new UIPropertyMetadata(new Thickness(2,2,0)));

    }
}

以上依赖属性定义的非常简单,之所以定义成依赖属性是因为可以进行属性绑定操作.

三.重写默认样式属性元数据

/// <summary>
/// Represents a control that a user can choose one from list options in a group radiobutton
/// </summary>
public class RadioButtonList : ListControl
{
    static RadioButtonList()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(RadioButtonList),new FrameworkPropertyMetadata(typeof(RadioButtonList)));
    }
}

/// <summary>
/// Contains a list of selectable CheckBox items 
/// Represents a control that a user can choose from a list options in a group of CheckBox
/// </summary>
public class CheckBoxList : ListControl
{
    static CheckBoxList()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CheckBoxList),new FrameworkPropertyMetadata(typeof(CheckBoxList)));
    }
}

四.用xaml定义控件样式

默认项目中会有一个Generic.xaml文件,我们要把样式定义在此文件中

控件样式定义

(1)ListControl

<Style TargetType="{x:Type local:ListControl}" BasedOn="{StaticResource {x:Type ListBox}}">
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <UniformGrid HorizontalAlignment="Left" VerticalAlignment="Top"
                    Rows="{Binding Path=Rows,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type local:ListControl}}}" 
                    Columns="{Binding Path=Columns,AncestorType={x:Type local:ListControl}}}"></UniformGrid>
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="Background" Value="Transparent" />
</Style>
(2) RadioButtonList
<Style TargetType="{x:Type local:RadioButtonList}" BasedOn="{StaticResource {x:Type local:ListControl}}">
    <Style.Resources>
        <Style TargetType="ListBoxItem">
            <Setter Property="Margin" Value="2,0" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                            <RadioButton 
                                Margin="{Binding Path=SubMargin,AncestorType={x:Type local:RadioButtonList}}}"
                                IsChecked="{Binding IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}">
                                <RadioButton.Content>
                                    <ContentPresenter/>
                                </RadioButton.Content>
                            </RadioButton>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Style.Resources>
</Style>
(3) CheckBoxList
<Style TargetType="{x:Type local:CheckBoxList}" BasedOn="{StaticResource {x:Type local:ListControl}}">
    <Setter Property="SelectionMode" Value="Multiple"></Setter>
    <Style.Resources>
        <Style TargetType="ListBoxItem">
            <Setter Property="Margin" Value="2,0" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                            <CheckBox
                                Margin="{Binding Path=SubMargin,Mode=TwoWay}">
                                <CheckBox.Content>
                                    <ContentPresenter/>
                                </CheckBox.Content>
                            </CheckBox>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Style.Resources>
</Style>

OK,到这里就完事了.我们发现我们并不需要写多少代码,就可以实现一个控件.而且RadioButtonList和CheckBoxList几乎没有代码,仅仅只是重写了样式而已,这也是WPF定义控件的基本概念,一些变的都是如此简单.

Demo下载

相关文章

什么是设计模式一套被反复使用、多数人知晓的、经过分类编目...
单一职责原则定义(Single Responsibility Principle,SRP)...
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强...
适配器模式将一个类的接口转换成客户期望的另一个接口,使得...
策略模式定义了一系列算法族,并封装在类中,它们之间可以互...
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,...