欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

程序员文章站 2023-11-23 00:03:04
WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展 原文:https://www.cnblogs.com/anding/p/4970845.html 一.前言.预览 申明:WPF自定义控件与样式是一个系列文章,前后 ......

一.前言.预览

  申明:wpf自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接。

本文主要是对文本输入控件进行样式开发,及相关扩展功能开发,主要内容包括:

  • 基本文本框textbox控件样式及扩展功能,实现了样式、水印、label标签、功能扩展;
  • 富文本框richtextbox控件样式;
  • 密码输入框passwordbox控件样式及扩展功能;

效果图:

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

二.基本文本框textbox控件样式及扩展功能

2.1 textbox基本样式

样式代码如下:  

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展
    <!--textbox默认样式-->
    <style targettype="{x:type textbox}" x:key="defaulttextbox">
        <setter property="contextmenu" value="{dynamicresource textboxcontextmenu}" />
        <setter property="selectionbrush" value="{staticresource textselectionbrush}" />
        <setter property="fontfamily" value="{staticresource fontfamily}" />
        <setter property="fontsize" value="{staticresource fontsize}" />
        <setter property="borderthickness" value="1" />
        <setter property="minheight" value="26" />
        <setter property="width" value="100" />
        <setter property="background" value="{staticresource textbackground}" />
        <setter property="foreground" value="{staticresource textforeground}" />
        <setter property="padding" value="0" />
        <setter property="borderbrush" value="{staticresource controlborderbrush}" />
        <setter property="local:controlattachproperty.focusborderbrush" value="{staticresource focusborderbrush}" />
        <setter property="local:controlattachproperty.mouseoverborderbrush" value="{staticresource mouseoverborderbrush}" />
        <setter property="verticalcontentalignment" value="center" />
        <!-- change snapstodevicepixels to true to view a better border and validation error -->
        <setter property="snapstodevicepixels" value="true" />
        <!--英 ['kærət]  美 ['kærət]  插入符号-->
        <setter property="caretbrush" value="{staticresource textforeground}" />
        <setter property="template">
            <setter.value>
                <controltemplate targettype="{x:type textbox}">
                    <grid x:name="part_root">
                        <border x:name="bg" snapstodevicepixels="{templatebinding snapstodevicepixels}"
                                cornerradius="{templatebinding local:controlattachproperty.cornerradius}"
                                borderbrush="{templatebinding borderbrush}" borderthickness="{templatebinding borderthickness}" background="{templatebinding background}" />
                        <grid x:name="part_innergrid">
                            <grid.columndefinitions>
                                <columndefinition  width="auto" />
                                <columndefinition width="*" />
                                <columndefinition  width="auto" />
                            </grid.columndefinitions>
                            <!--label区域-->
                            <contentcontrol x:name="label" margin="1" template="{templatebinding local:controlattachproperty.labeltemplate}"
                                            content="{templatebinding local:controlattachproperty.label}"/>
                            <!--内容区域-->
                            <scrollviewer x:name="part_contenthost" borderthickness="0" grid.column="1" istabstop="false" margin="2"
                                          verticalalignment="stretch" background="{x:null}" />
                            <!--水印-->
                            <textblock x:name="message"  padding="{templatebinding padding}" visibility="collapsed"
                                       text="{templatebinding local:controlattachproperty.watermark}" grid.column="1"
                                       foreground="{templatebinding foreground}" ishittestvisible="false" opacity="{staticresource watermarkopacity}"
                                       horizontalalignment="{templatebinding horizontalcontentalignment}"
                                       verticalalignment="{templatebinding verticalcontentalignment}" margin="5,2,5,2" />
                            <!--附加内容区域-->
                            <border x:name="part_attachcontent" grid.column="2" margin="2" verticalalignment="center" horizontalalignment="center" >
                                <contentcontrol verticalalignment="center" verticalcontentalignment="center" template="{templatebinding local:controlattachproperty.attachcontent}" />
                            </border>
                        </grid>
                    </grid>
                    <controltemplate.triggers>
                        <!--显示水印-->
                        <datatrigger binding="{binding relativesource={relativesource self}, path=text}" value="">
                            <setter targetname="message" property="visibility" value="visible" />
                        </datatrigger>
                        
                        <trigger property="ismouseover" value="true">
                            <setter property="borderbrush" value="{binding path=(local:controlattachproperty.mouseoverborderbrush),relativesource={relativesource self}}"/>
                        </trigger>
                        <trigger property="isfocused" value="true">
                            <setter  property="borderbrush" value="{binding path=(local:controlattachproperty.focusborderbrush),relativesource={relativesource self}}"/>
                        </trigger>
                        <!--不可用-->
                        <trigger property="isenabled" value="false">
                            <setter targetname="part_root" property="opacity" value="{staticresource disableopacity}" />
                        </trigger>
                        <!--只读时,禁用part_attachcontent-->
                        <trigger property="isreadonly" value="true">
                            <setter targetname="part_attachcontent" property="isenabled" value="false" />
                            <setter targetname="bg" property="opacity" value="{staticresource readonlyopacity}" />
                            <setter targetname="part_contenthost" property="opacity" value="{staticresource readonlyopacity}" />
                            <setter targetname="label" property="opacity" value="{staticresource readonlyopacity}" />
                        </trigger>
                    </controltemplate.triggers>
                </controltemplate>
            </setter.value>
        </setter>
    </style>
WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

  模板内容主要包含四部分:

  • 用于实现label标签的预留区域;
  • textbox本身的文本输入显示部分;
  • 水印显示部分;
  • 功能扩展的预留区域;

  其中label标签、功能扩展,还有输入框的不同状态显示效果都是通过附加属性来实现的,其实从本质上附加属性和控件上定义的依赖属性是同一个概念,有些时候附加属性会更加方便,对于一些可共用的属性,就比较方便,这一点怎本文是有体现的。上面代码使用到的附加属性代码:

 

2.2 水印效果实现

  通过2.1的代码示例,可以看出,水印是内置了一个textblock,用附加属性controlattachproperty.watermark设置水印内容,在触发器中检测,当textbox中有输入值,则隐藏水印的textblock,使用示例:  

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展
        <stackpanel>
            <textbox width="140" height="40" margin="3" textwrapping="wrap" verticalscrollbarvisibility="visible">333333333333333</textbox>
            <textbox width="150" height="30" margin="3" core:controlattachproperty.watermark="我是水印" core:controlattachproperty.cornerradius="2"></textbox>
            <textbox width="150" height="30" margin="3" isreadonly="true" core:controlattachproperty.cornerradius="15" snapstodevicepixels="true" >我是只读的</textbox>
            <textbox width="150" height="30" margin="3" isenabled="false">isenabled="false"</textbox>
            <textbox width="150" height="30" core:controlattachproperty.watermark="我是水印"></textbox>
        </stackpanel>
WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

  效果:

  WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

2.3 label标签实现

  参考2.1的代码,预留了label的区域,通过设置附加属性local:controlattachproperty.label设置标签文本,local:controlattachproperty.labeltemplate设置label标签的模板样式,即可自定义实现label标签,自定义样式:

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展
    <!--textbox包含附加属性label的样式-->
    <style targettype="{x:type textbox}" x:key="labeltextbox" basedon="{staticresource defaulttextbox}">
        <setter property="local:controlattachproperty.labeltemplate" >
            <setter.value>
                <controltemplate targettype="contentcontrol">
                    <border width="60" background="{staticresource textlabelbackground}">
                        <textblock verticalalignment="center" horizontalalignment="right" margin="3" text="{templatebinding content}"></textblock>
                    </border>
                </controltemplate>
            </setter.value>
        </setter>
    </style>
WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

  使用示例及效果: 

            <textbox width="200" height="30" margin="3" core:controlattachproperty.watermark="请输入姓名" 
                         style="{staticresource labeltextbox}" core:controlattachproperty.label="姓名:"></textbox>

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

2.4 扩展功能及自定义扩展

  思路和2.3的label标签实现相似,清除文本框内的内容是一个常用需求,我们就线扩展一个这么一个功能的textbox,通过附加属性controlattachproperty.attachcontent定义扩展功能的模板,模板内定义的是一个按钮fbutton(可参考上一篇,本文末尾附录中有链接)  

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展
    <!--textbox包含清除text按钮的样式-->
    <style targettype="{x:type textbox}" x:key="clearbuttontextbox" basedon="{staticresource defaulttextbox}">
        <setter property="local:controlattachproperty.attachcontent">
            <setter.value>
                <controltemplate>
                    <local:fbutton ficon="&#xe60a;" style="{staticresource fbutton_transparency}" istabstop="false" ficonmargin="0"
                                   local:controlattachproperty.iscleartextbuttonbehaviorenabled="true" command="local:controlattachproperty.cleartextcommand" 
                                   commandparameter="{binding relativesource={relativesource findancestor,ancestortype={x:type textbox}}}"
                               margin="1,3,1,4" ficonsize="14" foreground="{staticresource textforeground}" cursor="arrow"/>
                </controltemplate>
            </setter.value>
        </setter>
    </style>
WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

  这里定义的是显示效果,清除textbox内容的逻辑代码如何实现的呢?还是附加属性:

  • controlattachproperty.iscleartextbuttonbehaviorenabled="true" :注入事件到当前button
  • command="local:controlattachproperty.cleartextcommand":定义fbutton的命令对象实例command
  • commandparameter="{binding relativesource={relativesource findancestor,ancestortype={x:type textbox}}}":把textbox作为参数传入

  逻辑代码如下,从代码不难看出,它是支持多种输入控件的内容清除的,也就是说该扩展功能可以轻松支持其他输入控件,第四节密码数据的清除也是这样使用的。

 

  效果:

  WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

当然我们也可以自定义扩展其他功能,如:  

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展
            <textbox width="200" height="30" margin="3" core:controlattachproperty.watermark="查询关键词" isenabled="true">
                <core:controlattachproperty.attachcontent>
                    <controltemplate>
                        <stackpanel orientation="horizontal">
                            <core:fbutton ficon="&#xe60b;"  style="{staticresource fbutton_transparency}" istabstop="false" ficonmargin="0"
                                            ficonsize="18" margin="1,1,2,3" foreground="{staticresource textforeground}" cursor="arrow"/>
                            <core:fbutton ficon="&#xe628;"  style="{staticresource fbutton_transparency}" istabstop="false" ficonmargin="0"
                                            ficonsize="22" foreground="{staticresource textforeground}" cursor="arrow"/>
                        </stackpanel>
                    </controltemplate>
                </core:controlattachproperty.attachcontent>
            </textbox>
WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

  效果:

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

由上不难同时实现label标签和清除文本内容的样式:

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展
    <!--textbox包含附加属性label,以及cleartext按钮的样式-->
    <style targettype="{x:type textbox}" x:key="labelclearbuttontextbox" basedon="{staticresource defaulttextbox}">
        <setter property="local:controlattachproperty.labeltemplate" >
            <setter.value>
                <controltemplate targettype="contentcontrol">
                    <border width="60" background="{staticresource textlabelbackground}">
                        <textblock verticalalignment="center" horizontalalignment="right" margin="3" text="{templatebinding content}"></textblock>
                    </border>
                </controltemplate>
            </setter.value>
        </setter>
        <setter property="local:controlattachproperty.attachcontent">
            <setter.value>
                <controltemplate>
                    <local:fbutton ficon="&#xe60a;" style="{staticresource fbutton_transparency}" istabstop="false" ficonmargin="0"
                               local:controlattachproperty.iscleartextbuttonbehaviorenabled="true" command="local:controlattachproperty.cleartextcommand" 
                               commandparameter="{binding relativesource={relativesource findancestor,ancestortype={x:type textbox}}}"
                               margin="0,3,1,4" ficonsize="14" foreground="{staticresource textforeground}" cursor="arrow"/>
                </controltemplate>
            </setter.value>
        </setter>
    </style>
WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

2.6 文件选择输入相关扩展

  先看看效果,就明白了。

  WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展 

具体实现原理和上面2.4差不多 ,实现了三个文件、文件夹选择相关的功能扩展,样式代码:

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展
    <!--labelopenfiletextbox-->
    <style targettype="{x:type textbox}" x:key="labelopenfiletextbox" basedon="{staticresource labelclearbuttontextbox}">
        <setter property="local:controlattachproperty.label" value="文件路径"/>
        <setter property="local:controlattachproperty.watermark" value="选择文件路径"/>
        <setter property="local:controlattachproperty.attachcontent">
            <setter.value>
                <controltemplate>
                    <local:fbutton ficon="&#xe64e;" style="{staticresource fbutton_transparency}" istabstop="false" ficonmargin="0"
                               local:controlattachproperty.isopenfilebuttonbehaviorenabled="true" command="local:controlattachproperty.openfilecommand" 
                               commandparameter="{binding relativesource={relativesource findancestor,ancestortype={x:type textbox}}}"
                               margin="0,1,0,1"  ficonsize="22" foreground="{staticresource textforeground}" cursor="arrow"/>
                </controltemplate>
            </setter.value>
        </setter>
    </style>

    <!--labelopenfoldertextbox-->
    <style targettype="{x:type textbox}" x:key="labelopenfoldertextbox" basedon="{staticresource labelclearbuttontextbox}">
        <setter property="local:controlattachproperty.label" value="设置路径"/>
        <setter property="local:controlattachproperty.watermark" value="选择文件夹路径"/>
        <setter property="local:controlattachproperty.attachcontent">
            <setter.value>
                <controltemplate>
                    <local:fbutton ficon="&#xe636;" style="{staticresource fbutton_transparency}" istabstop="false" ficonmargin="0"
                               local:controlattachproperty.isopenfolderbuttonbehaviorenabled="true" command="local:controlattachproperty.openfoldercommand" 
                               commandparameter="{binding relativesource={relativesource findancestor,ancestortype={x:type textbox}}}"
                               margin="0,1,0,1"  ficonsize="22" foreground="{staticresource textforeground}" cursor="arrow"/>
                </controltemplate>
            </setter.value>
        </setter>
    </style>

    <!--labelsavefiletextbox-->
    <style targettype="{x:type textbox}" x:key="labelsavefiletextbox" basedon="{staticresource labelclearbuttontextbox}">
        <setter property="local:controlattachproperty.label" value="保存路径"/>
        <setter property="local:controlattachproperty.watermark" value="选择文件保存路径"/>
        <setter property="local:controlattachproperty.attachcontent">
            <setter.value>
                <controltemplate>
                    <local:fbutton ficon="&#xe61a;" style="{staticresource fbutton_transparency}" istabstop="false" ficonmargin="0"
                               local:controlattachproperty.issavefilebuttonbehaviorenabled="true" command="local:controlattachproperty.savefilecommand" 
                               commandparameter="{binding relativesource={relativesource findancestor,ancestortype={x:type textbox}}}"
                               margin="0,1,0,1"  ficonsize="20" foreground="{staticresource textforeground}" cursor="arrow"/>
                </controltemplate>
            </setter.value>
        </setter>
    </style>
WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

当然实现原理和2.4一样,都是依赖属性来实现事件的注入和绑定的,所以就不多废话了:

 

 三.富文本框richtextbox控件样式

  richtextbox的样式比较简单:  

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展
    <!--***************************defaultrichtextbox***************************-->

    <style x:key="defaultrichtextbox" targettype="{x:type richtextbox}">
        <setter property="contextmenu" value="{dynamicresource textboxcontextmenu}" />
        <setter property="selectionbrush" value="{staticresource textselectionbrush}" />
        <setter property="fontfamily" value="{staticresource fontfamily}" />
        <setter property="fontsize" value="{staticresource fontsize}" />
        <setter property="borderthickness" value="1" />
        <setter property="borderbrush" value="{staticresource controlborderbrush}" />
        <setter property="minheight" value="26" />
        <setter property="minwidth" value="10" />
        <setter property="background" value="{staticresource textbackground}" />
        <setter property="foreground" value="{staticresource textforeground}" />
        <setter property="caretbrush" value="{staticresource textforeground}" />
        <setter property="local:controlattachproperty.focusborderbrush" value="{staticresource focusborderbrush}" />
        <setter property="local:controlattachproperty.mouseoverborderbrush" value="{staticresource mouseoverborderbrush}" />
        <setter property="padding" value="1" />
        <setter property="allowdrop" value="true" />
        <setter property="verticalscrollbarvisibility" value="auto" />
        <setter property="focusvisualstyle" value="{x:null}" />
        <setter property="scrollviewer.panningmode" value="verticalfirst" />
        <!--该值指示是否启用了笔势-->
        <setter property="stylus.isflicksenabled" value="false" />
        <!--snapstodevicepixels:该值来确定呈现此元素是否应使用特定于设备的像素设置-->
        <setter property="snapstodevicepixels" value="true" />
        <setter property="template">
            <setter.value>
                <controltemplate targettype="{x:type textboxbase}">
                    <grid>
                        <border x:name="bd"
                                borderbrush="{templatebinding borderbrush}" borderthickness="{templatebinding borderthickness}"
                                background="{templatebinding background}" snapstodevicepixels="{templatebinding snapstodevicepixels}">
                            <scrollviewer x:name="part_contenthost" snapstodevicepixels="{templatebinding snapstodevicepixels}" />
                        </border>
                    </grid>
                    <controltemplate.triggers>
                        <trigger property="ismouseover" value="true">
                            <setter property="borderbrush" value="{binding path=(local:controlattachproperty.mouseoverborderbrush),relativesource={relativesource self}}"/>
                        </trigger>
                        <trigger property="isfocused" value="true">
                            <setter  property="borderbrush" value="{binding path=(local:controlattachproperty.focusborderbrush),relativesource={relativesource self}}"/>
                        </trigger>
                        <trigger property="isenabled" value="false">
                            <setter targetname="bd" property="opacity" value="0.5" />
                        </trigger>
                        <trigger property="isreadonly" value="true">
                            <setter targetname="bd" property="opacity" value="0.85" />
                        </trigger>
                    </controltemplate.triggers>
                </controltemplate>
            </setter.value>
        </setter>
    </style>
WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

  使用实力及效果:  

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

四.密码输入框passwordbox控件样式及扩展功能

  密码输入控件的样式和第二节文本框textbox基本一致,就不做详细的说明了,直接上样式的代码,相关逻辑(c#) 代码和上面是一样的(复用)。

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展
    <!--textbox默认样式-->
    <style targettype="{x:type passwordbox}" x:key="defaultpasswordbox">
        <setter property="contextmenu" value="{dynamicresource textboxcontextmenu}" />
        <setter property="selectionbrush" value="{staticresource textselectionbrush}" />
        <setter property="fontfamily" value="{staticresource fontfamily}" />
        <setter property="fontsize" value="{staticresource fontsize}" />
        <setter property="borderthickness" value="1" />
        <setter property="passwordchar" value="●"/>
        <setter property="height" value="30" />
        <setter property="width" value="200" />
        <setter property="background" value="{staticresource textbackground}" />
        <setter property="foreground" value="{staticresource textforeground}" />
        <setter property="padding" value="0" />
        <setter property="borderbrush" value="{staticresource controlborderbrush}" />
        <setter property="local:controlattachproperty.focusborderbrush" value="{staticresource focusborderbrush}" />
        <setter property="local:controlattachproperty.mouseoverborderbrush" value="{staticresource mouseoverborderbrush}" />
        <setter property="verticalcontentalignment" value="center" />
        <!-- change snapstodevicepixels to true to view a better border and validation error -->
        <setter property="snapstodevicepixels" value="true" />
        <!--英 ['kærət]  美 ['kærət]  插入符号-->
        <setter property="caretbrush" value="{staticresource textforeground}" />
        <setter property="template">
            <setter.value>
                <controltemplate targettype="{x:type passwordbox}">
                    <grid x:name="part_root">
                        <border x:name="bg" snapstodevicepixels="{templatebinding snapstodevicepixels}"
                                cornerradius="{templatebinding local:controlattachproperty.cornerradius}"
                                borderbrush="{templatebinding borderbrush}" borderthickness="{templatebinding borderthickness}" background="{templatebinding background}" />
                        <grid x:name="part_innergrid">
                            <grid.columndefinitions>
                                <columndefinition  width="auto" />
                                <columndefinition width="*" />
                                <columndefinition  width="auto" />
                            </grid.columndefinitions>
                            <!--label区域-->
                            <contentcontrol x:name="label" margin="1" template="{templatebinding local:controlattachproperty.labeltemplate}"
                                            content="{templatebinding local:controlattachproperty.label}"/>
                            <!--内容区域-->
                            <scrollviewer x:name="part_contenthost" borderthickness="0" grid.column="1" istabstop="false" margin="2"
                                          verticalalignment="stretch" background="{x:null}" />
                            <!--附加内容区域-->
                            <border x:name="part_attachcontent" grid.column="2" margin="2" verticalalignment="center" horizontalalignment="center" >
                                <contentcontrol verticalalignment="center" verticalcontentalignment="center" template="{templatebinding local:controlattachproperty.attachcontent}" />
                            </border>
                        </grid>
                    </grid>
                    <controltemplate.triggers>
                        <trigger property="ismouseover" value="true">
                            <setter property="borderbrush" value="{binding path=(local:controlattachproperty.mouseoverborderbrush),relativesource={relativesource self}}"/>
                        </trigger>
                        <trigger property="isfocused" value="true">
                            <setter  property="borderbrush" value="{binding path=(local:controlattachproperty.focusborderbrush),relativesource={relativesource self}}"/>
                        </trigger>
                        <!--不可用-->
                        <trigger property="isenabled" value="false">
                            <setter targetname="part_root" property="opacity" value="{staticresource disableopacity}"></setter>
                        </trigger>
                    </controltemplate.triggers>
                </controltemplate>
            </setter.value>
        </setter>
    </style>

    <!--textbox包含清除text按钮的样式-->
    <style targettype="{x:type passwordbox}" x:key="clearbuttonpasswordbox" basedon="{staticresource defaultpasswordbox}">
        <setter property="local:controlattachproperty.attachcontent">
            <setter.value>
                <controltemplate>
                    <local:fbutton ficon="&#xe60a;" style="{staticresource fbutton_transparency}" istabstop="false" ficonmargin="0"
                                   local:controlattachproperty.iscleartextbuttonbehaviorenabled="true" command="local:controlattachproperty.cleartextcommand" 
                                   commandparameter="{binding relativesource={relativesource findancestor,ancestortype={x:type passwordbox}}}"
                               margin="1,3,1,4" ficonsize="14" foreground="{staticresource textforeground}" cursor="arrow"/>
                </controltemplate>
            </setter.value>
        </setter>
    </style>

    <!--textbox包含附加属性label的样式-->
    <style targettype="{x:type passwordbox}" x:key="labelpasswordbox" basedon="{staticresource defaultpasswordbox}">
        <setter property="local:controlattachproperty.labeltemplate" >
            <setter.value>
                <controltemplate targettype="contentcontrol">
                    <border width="60" background="{staticresource textlabelbackground}">
                        <textblock verticalalignment="center" horizontalalignment="right" margin="3" text="{templatebinding content}"></textblock>
                    </border>
                </controltemplate>
            </setter.value>
        </setter>
    </style>

    <!--textbox包含附加属性label,以及cleartext按钮的样式-->
    <style targettype="{x:type passwordbox}" x:key="labelclearbuttonpasswordbox" basedon="{staticresource defaultpasswordbox}">
        <setter property="local:controlattachproperty.labeltemplate" >
            <setter.value>
                <controltemplate targettype="contentcontrol">
                    <border width="60" background="{staticresource textlabelbackground}">
                        <textblock verticalalignment="center" horizontalalignment="right" margin="3" text="{templatebinding content}"></textblock>
                    </border>
                </controltemplate>
            </setter.value>
        </setter>
        <setter property="local:controlattachproperty.attachcontent">
            <setter.value>
                <controltemplate>
                    <local:fbutton ficon="&#xe60a;" style="{staticresource fbutton_transparency}" istabstop="false" ficonmargin="0"
                               local:controlattachproperty.iscleartextbuttonbehaviorenabled="true" command="local:controlattachproperty.cleartextcommand" 
                               commandparameter="{binding relativesource={relativesource findancestor,ancestortype={x:type passwordbox}}}"
                               margin="0,3,1,4" ficonsize="14" foreground="{staticresource textforeground}" cursor="arrow"/>
                </controltemplate>
            </setter.value>
        </setter>
    </style>
WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

使用示例及效果:  

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展