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(string), typeof(LabelTextBox), new PropertyMetadata(string.Empty));

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

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

        public string LabelPosition
        {
            get { return (string)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事件.

小结

    本文简单介绍了如何构建一个自定义控件,其中涉及到依赖项属性,控件模板,资源,事件的知识点.最后,如果您有更好的建议,请不吝指教.

  

时间: 2024-10-19 05:04:05

WPF之给TextBox添上Label的相关文章

给android layout 添上state的翅膀

在android中,可以很容易给一个视图的外观加上state,从而反映行为状态比如:激活.选中.悬浮等.但是对于视图布局的动态调整,比如说某一状态,需要显示按钮,下一个状态,不需要显示这个按钮,就显得苍白无力. 这种情况,大家会说我去改一下按钮的可见度就ok了呀.的确是这样,但是涉及到多个状态.多个控件的时候是不是很麻烦?现有解决办法主要问题有: (1)      多状态.多组件的难于管理. (2)      没用的组件只是看不见了,但是还存在,浪费资源. 有没有更好的方法?有,那就是引入sta

苹果给废墟人像添上一抹梦幻粉色朦胧美效果

这篇教程是向脚本之家的朋友介绍Photoshop给废墟人像添上一抹梦幻粉色朦胧美效果图,教程调出来的效果非常漂亮,而且方法也很简单,推荐给脚本之家的朋友,喜欢的朋友可以过来一起来学习! 宁国路废墟,是上海最近比较火的一个外拍地.不过随着废墟类型片越来越多,我们发现大多数片子走的都是颓废风,后期上也多是以低饱和甚至黑白,做旧等效果为主.令人不禁想问:废墟就一定得拍得这么颓废绝望吗?今天的教程就来教大家自己动手打造简单的道具,再辅以相应的后期,为废墟人像添上一抹朦胧美,让朦胧梦幻风的废墟人像走起~

为原本就冷清的娘家再次添上了阴影

兄嫂一家人的集体外出打工,为原本就冷清的娘家再次添上了阴影.习惯了一家人在一起生活的日子,不曾体会到嫂子长期一人呆在家里照顾老人孩子的孤单.同意他们一家人出去打工,断了大哥牵绊家人的想法,让他没有后顾之忧地在外面赚钱.父亲唯一没有考虑到他自己,没有想到一个人生活的日子会有多么孤苦无依.或许,在他心里,始终觉得,有女儿在身边陪伴也是一样的. 于是,距离娘家最近的我,成了回家陪伴父亲的不二人选.于是,我跟儿子在忙完了一天的事情后,收拾好行李开始了学校与娘家来回奔波的日程. http://ikando

在excel表格里,为所有数字添上绿色小三角

在excel表格里,为所有数字添上绿色小三角的方法有4种: 1. 为一个单元格添加:直接在单元格里添加一个英文的逗号 2. 为一列数据添加:选中要添加绿色小三角的列,选择 数据-->分列-->固定宽度-->下一步-->下一步-->文本-->完成 3.为所有数据添加: 3.1 选中所有要添加绿色小三角的数据,右键-->设置单元格格式-->文本-->确定 3.2 选中所有要添加绿色小三角的数据并剪切 3.3 选择开始-->剪贴板-->鼠标移动到

WPF 获得触笔悬停元素上

原文:WPF 获得触笔悬停元素上 触笔可以获得悬停在元素上,这时触笔没有碰到元素,没有碰到屏幕. 如果使用触笔,那么在悬停就需要显示光标位置,这时使用UIElement.StylusInAirMove 事件可以获得触笔悬停在元素上. 需要知道,这个事件是 .net Framework 3.0 之后添加的. UIElement.StylusInAirMove 事件 (System.Windows) 本文会经常更新,请阅读原文: https://blog.lindexi.com/post/WPF-%

WPF窗口默认TextBox焦点

原文:WPF窗口默认TextBox焦点 当WPF窗口显示后 如果想设置某个输入框为默认焦点 使用 FocusManager <Grid FocusManager.FocusedElement="{Binding ElementName=inputbox}"> <TextBox Name="inputbox" HorizontalAlignment="Left" Height="24" Margin="

WPF中的TextBox一些问题

1.如何判断TextBox是否为空? if(textbox.Text != string.empty) //或者 if(textbox.Text.Trim()!="") { } 2.设置TextBox为空时,背景文字提示 参考博客:http://www.cnblogs.com/tsunami/archive/2011/09/16/2179170.html <TextBox FontSize="17" Height="26" Margin=&

WPF学习二:TextBlock和Label的区别

TextBlock和Label都是用来显示少量数据的.好多文章对Label存在的描述都是它允许使用"快速获取"."快速获取"就是允许你用Alt加上其它的按键快速和UI界面的某个控件交互,比如你可以用ALT加上O键来点击一个OK按钮. TextBlock直接继承于FrameworkElement,而Label继承于ContentControl.这样看来,Label可以做这样的事情: 1.可以定义一个控件模板(通过Template属性) 2.可以显示出string以外的

WPF:警惕TextBox会占用过多内存

问题源自这篇文章:WPF的TextBox产生内存泄露的情况. 整个问题是这样的,文章作者演示使用类似下方的代码来不停地像WPF的TextBox控件赋值: for (int i = 0; i < 10000; i++) { //tbx是界面上的TextBox变量 tbx.Text += string.Format("{0}\n", i); } 然后会出现程序占用过多内存的问题. 很快在那篇文章的评论中有人指出这个和WPF没有关系,因为频繁得拼接字符串会产生过多重复字符串对象,即使不