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

问题描述:

我在一个名为CustomControls的新WPF项目中完成了以下操作。为什么我的自定义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不显示它?有人甚至可以调试这些东西?

+0

检查我的答案在这里http://stackoverflow.com/questions/37995806/textpreview-for-textbox/37997102#37997102 –

诀窍是使用名为混合为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相同的位置显示和隐藏标签会更容易。

+1

为什么你原来的实现失败的主要问题是,在资源内完成绑定往往无法正常工作。把它移到模板上就行了,就像你在这里做的一样。 – Jai

+0

是的,它的不幸。最终,它清楚地知道WPF在可扩展性方面是一团糟,但至少这项技术不仅仅是通过一种简单但严格遵循的编程模型来提供非常丰富的用户体验,并且我非常喜欢它。 – Alexandru