依賴屬性2

首先說明了屬性的現存問題,然後介紹了依賴屬性的基本用法及其解決方案,由於依賴屬性以靜態屬性的方式存在,進而又介紹了可重寫的屬性元數據的使用.這是以上三篇所說明的問題.當然依賴屬性的特性依然沒有說完整.這兩天也一直在想依賴屬性,然後就是頭疼,呵呵.由於WPF的依賴屬性系統與整體功能都有關聯,所以接下來等講到某個特性的時候然後再來講依賴屬性.這篇我們來個簡單的實踐,增加點樂趣.

定義RadioButtonList和CheckBoxList

image image

WPF內置沒有這兩個控件,但實際開發時,用到的會比較多.下面我們一起來創建這兩個控件.

  1. 明確控件需求
  2. 定義依賴屬性
  3. 重寫默認樣式屬性元數據
  4. 用xaml定義控件樣式

一.明確控件需求

控件功能如下:

  1. 可以垂直,水平排列
  2. 可以分行,列
  3. 爲子項設置Margin

二.定義依賴屬性

爲避免重複定義屬性,爲兩個控件定義公共類ListControl,從ListBox繼承

image

然後定義依賴屬性

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), typeof(ListControl), 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", typeof(int), typeof(ListControl), 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), typeof(ListControl), 
            new UIPropertyMetadata(new Thickness(2,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文件,我們要把樣式定義在此文件中

image

控件樣式定義

(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,RelativeSource={RelativeSource FindAncestor,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, 2, 2, 0" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                            <RadioButton 
                                Margin="{Binding Path=SubMargin,RelativeSource={RelativeSource FindAncestor,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, 2, 2, 0" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                            <CheckBox
                                Margin="{Binding Path=SubMargin,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type local:RadioButtonList}}}"
                                IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}">
                                <CheckBox.Content>
                                    <ContentPresenter/>
                                </CheckBox.Content>
                            </CheckBox>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Style.Resources>
</Style>

OK,到這裏就完事了.我們發現我們並不需要寫多少代碼,就可以實現一個控件.而且RadioButtonList和CheckBoxList幾乎沒有代碼,僅僅只是重寫了樣式而已,這也是WPF定義控件的基本概念,一些變的都是如此簡單.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章