为什么我的自定义WPF文本框控件不显示此水印?

时间:2016-06-21 20:02:09

标签: c# .net wpf xaml

我在一个名为CustomControls的新WPF项目中完成了以下工作。

第1步:创建了Controls文件夹。添加了一个名为WatermarkTextBox.cs的新文件。在文件中添加了这个C#代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace Controls
{
    public class WatermarkTextBox : TextBox
    {
        public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register("Watermark", typeof(String), typeof(WatermarkTextBox), new PropertyMetadata(String.Empty));

        public String Watermark
        {
            get { return (String)GetValue(WatermarkProperty); }
            set { SetValue(WatermarkProperty, value); }
        }
    }
}

第2步:在Controls文件夹中,添加了一个名为WatermarkTextBox.xaml的新文件。在文件中添加了此XAML:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:controls="clr-namespace:Controls">
    <Style TargetType="{x:Type controls:WatermarkTextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
        <Style.Resources>
            <VisualBrush x:Key="WatermarkBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
                <VisualBrush.Visual>
                    <Label Content="{Binding Watermark}" FontFamily="Segoe UI" FontSize="20" Foreground="LightGray" Padding="5" />
                </VisualBrush.Visual>
            </VisualBrush>
        </Style.Resources>
        <Style.Triggers>
            <Trigger Property="Text" Value="{x:Static sys:String.Empty}">
                <Setter Property="Background" Value="{StaticResource WatermarkBrush}" />
            </Trigger>
            <Trigger Property="Text" Value="{x:Null}">
                <Setter Property="Background" Value="{StaticResource WatermarkBrush}" />
            </Trigger>
            <Trigger Property="IsKeyboardFocused" Value="True">
                <Setter Property="Background" Value="White" />
            </Trigger>
        </Style.Triggers>
    </Style>
</ResourceDictionary>

第3步:创建Themes文件夹。添加一个标题为Generic.xaml的新文件。在文件中添加此XAML:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/CustomControls;component/Controls/WatermarkTextBox.xaml" />
</ResourceDictionary.MergedDictionaries>

第4步:在MainWindow.xaml中:添加了xmlns:controls="clr-namespace:Controls",并定义了新的WatermarkTextBox

<controls:WatermarkTextBox x:Name="Hostname" Height="40" FontFamily="Segoe UI" FontSize="20" VerticalContentAlignment="Center" Watermark="Hello, world."/>

我看到此自定义控件所基于的文本框,但我看不到我为其扩展的水印。为什么这样,我怎么能渲染我的水印?附:我应该注意,如果我将XAML中的{Binding Watermark}更改为硬编码字符串,则会显示水印。此外,如果我调试我的代码,我看到WatermarkTextBox获取正确的值...那么为什么XAML不显示它?有人甚至可以调试这些东西吗?

1 个答案:

答案 0 :(得分:0)

诀窍是使用名为 Blend for Visual Studio 的Windows应用程序来编辑现有WatermarkTextBox的默认模板,并将其扩展为引入Grid,其中不包括只有内容滚动视图,但也有一个新的标签来容纳水印。总之,我不得不对WatermarkTextBox.xaml进行此更改:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:system="clr-namespace:System;assembly=mscorlib"
                xmlns:controls="clr-namespace:Controls">
    <Style TargetType="{x:Type controls:WatermarkTextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="controls:WatermarkTextBox">
                    <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                        <Grid>
                            <ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                            <Label Cursor="IBeam" Visibility="Hidden" x:Name="WatermarkText" Content="{TemplateBinding Watermark}" FontFamily="Segoe UI" FontSize="20" Foreground="LightGray" Padding="5" />
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter Property="Opacity" TargetName="border" Value="0.56"/>
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/>
                        </Trigger>
                        <Trigger Property="IsKeyboardFocused" Value="True">
                            <Setter Property="BorderBrush" TargetName="border" Value="#FF569DE5"/>
                        </Trigger>
                        <Trigger Property="Text" Value="{x:Static system:String.Empty}">
                            <Setter Property="Visibility" Value="Visible" TargetName="WatermarkText" />
                        </Trigger>
                        <Trigger Property="Text" Value="{x:Null}">
                            <Setter Property="Visibility" Value="Visible" TargetName="WatermarkText" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

编辑:如果您正考虑在制作中执行此操作,请再想一想。我心想,“哦,扩展我需要的所有控件来提供这样的功能真是太好了。”不幸的是,PasswordBox等一些控制类是密封,这在这些情况下成为一个有争议的问题。 事实上,以TextBox 在同一位置以编程方式显示和隐藏标签会更容易,因为大多数黑客或扩展都会这样做。