【WPF】給TextBox添上Label


引言

    在客戶端開發中,要說出現頻率大的控件,必定有TextBox的身影.然而在TextBox的旁邊通常得有個基友Label,形影不離.為此,我們都要寫兩個控件,布局兩次,這樣麻煩且有點浪費時間.不如,我們做狠點,將它們兩個綁在一起算了.

簡單需求

    我們需要的控件該是怎樣的.首先,它應該有TextBox的所有特性,其次,它的上方或者左邊應該有個Label.我們可以通過設置屬性,顯示Label的內容.大體上就是這樣.

構建方式的選取

   WPF的控件開發有兩種,分為用戶控件和自定義控件.在這次的控件開發中,自定義控件又可以細分為三種,如下:

   1.首先是用戶控件,通過繼承UserControl,直接在xaml上布局設計.這種方式開發上比較方便,適用於多個界面上重用不變的模塊.但是專業的控件開發一般不采取這種方式.

   2.自定義控件之一,通過繼承Control和模板上采用TextBox和Label的布局構建控件.這種方式功能上的自由度很高,例如可以自定義text屬性,但是要構建大量的依賴項屬性和路由事件.

   3.自定義控件之二,通過繼承TextBox和修改采用msdn上提供TextBox的默認模板,這種方式輕量級些,但是要深入理解默認模板的設計,和需要重定義模板的一些觸發器和效果等.

   4.自定義控件之三,上面兩種都是比較復雜,我們有折中的做法.通過繼承TextBox和模板上采用TextBox和Label的布局構建控件,本文就是介紹一下這種做法.

新建項目

  首先,新建一個WPF用戶控件庫的項目,VS已經幫我們添加了一些東西,如圖:

   

    Themes文件夾下面的特定的Generic.xaml里面就是放我們的模板文件的了,一般我們不直接將模板直接寫在里面,而是每個控件模板分別放在一個資源文件中,再合並在Generic里面.而真正的控件是CustomControl1.cs,里面包含着我們熟悉的各種依賴項屬性和事件.當然,我們得重命名一下,就叫LabelTextBox吧.

無外觀的LabelTextBox

  在靜態構造函數中,調用 DefaultStyleKeyProperty.OverrideMetadata,告訴WPF為此控件應用一個新樣式,再新建一個LabelProperty和LabelPosition的依賴項屬性,如下:

 

public class LabelTextBox : TextBox
    {
        static LabelTextBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(LabelTextBox), new FrameworkPropertyMetadata(typeof(LabelTextBox)));
        }

        public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("Label", typeof(Object), typeof(LabelTextBox), new PropertyMetadata(string.Empty));

        public static readonly DependencyProperty LabelPositionProperty = DependencyProperty.Register("LabelPosition", typeof(Position), typeof(LabelTextBox), new PropertyMetadata(Position.Top));

        public Object Label
        {
            get { return (Object)GetValue(LabelProperty); }
            set { SetValue(LabelProperty, value); }
        }

        public Position LabelPosition
        {
            get { return (Position)GetValue(LabelPositionProperty); }
            set { SetValue(LabelPositionProperty, value); }
        }

    }

    public enum Position
    {
        Top,
        Left

    }

 

 

LabelTextBox的控件模板

     上面已經完成了我們的LabelTextBox,但是它還沒有樣式模板.接下來我們來構建它的控件模板.在Themes文件夾下面新建一個資源字典名為LabelTextBox.xaml,其實模板的TextBox和LabelTextBox沒有什么關聯,所以我們給控件模板寫上各種綁定和設置樣式觸發器,如下:

 

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:local="clr-namespace:WpfCustomControl">

    <ControlTemplate x:Key="TopLabelTextBoxTemplate" TargetType="{x:Type local:LabelTextBox}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Label 
                Content="{Binding Label, RelativeSource={RelativeSource TemplatedParent}}" Grid.Row="0"/>
            <TextBox Grid.Row="1"
                Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                HorizontalContentAlignment="{Binding HorizontalContentAlignment,RelativeSource={RelativeSource TemplatedParent}}"
                VerticalContentAlignment="{Binding VerticalContentAlignment,RelativeSource={RelativeSource TemplatedParent}}"
                Background="{Binding Background,RelativeSource={RelativeSource TemplatedParent}}"
                Foreground="{Binding Foreground,RelativeSource={RelativeSource TemplatedParent}}"
                TextWrapping="{Binding TextWrapping,RelativeSource={RelativeSource TemplatedParent}}"
                TextAlignment="{Binding TextAlignment,RelativeSource={RelativeSource TemplatedParent}}" 
                HorizontalScrollBarVisibility="{Binding HorizontalScrollBarVisibility,RelativeSource={RelativeSource TemplatedParent}}"
                VerticalScrollBarVisibility="{Binding VerticalScrollBarVisibility,RelativeSource={RelativeSource TemplatedParent}}"
                                         MaxLength="{Binding MaxLength,RelativeSource={RelativeSource TemplatedParent}}"
                                         IsReadOnly="{Binding IsReadOnly,RelativeSource={RelativeSource TemplatedParent}}"
                                         IsEnabled="{Binding IsEnabled,RelativeSource={RelativeSource TemplatedParent}}"/>
        </Grid>
    </ControlTemplate>

    <ControlTemplate x:Key="LeftLabelTextBoxTemplate" TargetType="{x:Type local:LabelTextBox}">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label HorizontalAlignment="Center" VerticalAlignment="Center"
                Content="{Binding Label, RelativeSource={RelativeSource TemplatedParent}}" Grid.Column="0"/>
            <TextBox Grid.Column="1"
                Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                HorizontalContentAlignment="{Binding HorizontalContentAlignment,RelativeSource={RelativeSource TemplatedParent}}"
                VerticalContentAlignment="{Binding VerticalContentAlignment,RelativeSource={RelativeSource TemplatedParent}}"
                Background="{Binding Background,RelativeSource={RelativeSource TemplatedParent}}"
                Foreground="{Binding Foreground,RelativeSource={RelativeSource TemplatedParent}}"
                TextWrapping="{Binding TextWrapping,RelativeSource={RelativeSource TemplatedParent}}"
                TextAlignment="{Binding TextAlignment,RelativeSource={RelativeSource TemplatedParent}}" 
                HorizontalScrollBarVisibility="{Binding HorizontalScrollBarVisibility,RelativeSource={RelativeSource TemplatedParent}}"
                VerticalScrollBarVisibility="{Binding VerticalScrollBarVisibility,RelativeSource={RelativeSource TemplatedParent}}"
                                         MaxLength="{Binding MaxLength,RelativeSource={RelativeSource TemplatedParent}}"
                                         IsReadOnly="{Binding IsReadOnly,RelativeSource={RelativeSource TemplatedParent}}"
                                         IsEnabled="{Binding IsEnabled,RelativeSource={RelativeSource TemplatedParent}}"/>
        </Grid>
    </ControlTemplate>

    <Style TargetType="{x:Type local:LabelTextBox}"  >
        <Setter Property="Template" Value="{StaticResource TopLabelTextBoxTemplate}"/>
        <Style.Triggers>
            <Trigger Property="LabelPosition" Value="Left">
                <Setter Property="Template" Value="{StaticResource ResourceKey=LeftLabelTextBoxTemplate}"></Setter>
            </Trigger>
        </Style.Triggers>
    </Style>

</ResourceDictionary>

 

      接下來,將資源字典添加到Generic.xaml,如下:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfCustomControl">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/WpfCustomControl;component/themes/LabelTextBox.xaml" ></ResourceDictionary>    
    </ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

   編譯通過后就得到我們的控件庫WpfCustomControl.dll.

LabelTextBox的使用

   在自己項目引用WpfCustomControl.dll,在xaml文件中添加標記引用,然后就可以直接使用,如下:

<Window x:Class="ControlsTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:control="clr-namespace:WpfCustomControl;assembly=WpfCustomControl"
        Title="MainWindow" Height="350" Width="525">
    <Grid>    
        <control:LabelTextBox Name="txtbox" LabelPosition="Top"  Label="標題" Width="100" TextChanged="LabelTextBox_TextChanged_1" Margin="209,136,209.4,136.4"/>
    </Grid>
</Window>

   可以看到TextChanged等事件能正常觸發,但是上面說到TextBox和LabelTextBox沒什么關聯,要手動綁定屬性.然而我們沒有為LabelTextBox事件綁定過什么,卻依然生效了.那是因為它們公用一個事件路由,實質是TextBox觸發了TextChanged事件,冒泡到LabelTextBox,觸發了LabelTextBox的TextChanged事件.

小結

    本文簡單介紹了如何構建一個自定義控件,其中涉及到依賴項屬性,控件模板,資源,事件的知識點.最后,如果您有更好的建議,請不吝指教.

 

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM