水印文本框的 WPF 水印密码框

发布于 2024-08-08 03:13:06 字数 710 浏览 3 评论 0原文

我正在使用水印文本框,如 WPF 中的水印文本框

 <Grid Grid.Row="0" Background="{StaticResource brushWatermarkBackground}" Style="{StaticResource EntryFieldStyle}" >
        <TextBlock Margin="5,2" Text="This prompt dissappears as you type..." Foreground="{StaticResource brushWatermarkForeground}"
                   Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" />
        <TextBox Name="txtUserEntry" Background="Transparent" BorderBrush="{StaticResource brushWatermarkBorder}" />
    </Grid>

我如何将其应用于密码框?

I am using a Watermark textbox as in Watermark TextBox in WPF

 <Grid Grid.Row="0" Background="{StaticResource brushWatermarkBackground}" Style="{StaticResource EntryFieldStyle}" >
        <TextBlock Margin="5,2" Text="This prompt dissappears as you type..." Foreground="{StaticResource brushWatermarkForeground}"
                   Visibility="{Binding ElementName=txtUserEntry, Path=Text.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}}" />
        <TextBox Name="txtUserEntry" Background="Transparent" BorderBrush="{StaticResource brushWatermarkBorder}" />
    </Grid>

How can I apply this for a PasswordBox?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

明媚殇 2024-08-15 03:13:06

一般方法是相同的:编写自定义控件样式,并在密码框为空时显示水印。这里唯一的问题是 PasswordBox.Password 不是依赖属性,您不能在触发器中使用它。此外,PasswordBox 是密封的,因此您无法覆盖此通知行为,但您可以在此处使用附加属性。

以下代码演示了如何操作。

XAML:

<Window x:Class="WpfTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:WpfTest="clr-namespace:WpfTest"
    Title="Password Box Sample" Height="300" Width="300">
  <Window.Resources>
    <Style x:Key="{x:Type PasswordBox}"
        TargetType="{x:Type PasswordBox}">
      <Setter Property="WpfTest:PasswordBoxMonitor.IsMonitoring"
              Value="True"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type PasswordBox}">
            <Border Name="Bd"
                    Background="{TemplateBinding Background}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    SnapsToDevicePixels="true">
              <Grid>
                <ScrollViewer x:Name="PART_ContentHost"
                              SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                <TextBlock Text="Please enter your password" 
                           Margin="4, 2, 0, 0"
                           Foreground="Gray" 
                           Visibility="Collapsed"
                           Name="txtPrompt" />
              </Grid>
            </Border>
            <ControlTemplate.Triggers>
              <Trigger Property="IsEnabled"
                                         Value="false">
                <Setter TargetName="Bd"
                                            Property="Background"
                                            Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                <Setter Property="Foreground"
                                            Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
              </Trigger>
              <Trigger Property="WpfTest:PasswordBoxMonitor.PasswordLength" Value="0">
                <Setter Property="Visibility" TargetName="txtPrompt" Value="Visible"/>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Window.Resources>
  <Grid>
    <PasswordBox VerticalAlignment="Top"/>
  </Grid>
</Window>

C#:

using System.Windows;
using System.Windows.Controls;

namespace WpfTest {
    public partial class Window1 : Window {
        public Window1() {
            InitializeComponent();
        }
    }

  public class PasswordBoxMonitor : DependencyObject {
    public static bool GetIsMonitoring(DependencyObject obj) {
      return (bool)obj.GetValue(IsMonitoringProperty);
    }

    public static void SetIsMonitoring(DependencyObject obj, bool value) {
      obj.SetValue(IsMonitoringProperty, value);
    }

    public static readonly DependencyProperty IsMonitoringProperty =
        DependencyProperty.RegisterAttached("IsMonitoring", typeof(bool), typeof(PasswordBoxMonitor), new UIPropertyMetadata(false, OnIsMonitoringChanged));

    public static int GetPasswordLength(DependencyObject obj) {
      return (int)obj.GetValue(PasswordLengthProperty);
    }

    public static void SetPasswordLength(DependencyObject obj, int value) {
      obj.SetValue(PasswordLengthProperty, value);
    }

    public static readonly DependencyProperty PasswordLengthProperty =
        DependencyProperty.RegisterAttached("PasswordLength", typeof(int), typeof(PasswordBoxMonitor), new UIPropertyMetadata(0));

    private static void OnIsMonitoringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
      var pb = d as PasswordBox;
      if (pb == null) {
        return;
      }
      if ((bool) e.NewValue) {
        pb.PasswordChanged += PasswordChanged;
      } else {
        pb.PasswordChanged -= PasswordChanged;
      }
    }

    static void PasswordChanged(object sender, RoutedEventArgs e) {
      var pb = sender as PasswordBox;
      if (pb == null) {
        return;
      }
      SetPasswordLength(pb, pb.Password.Length);
    }
  }
}

请注意 XAML 代码中的 PasswordBoxMonitor

The general approach is the same: write a custom control style, and show the watermark whenever the password box is empty. The only problem here is that PasswordBox.Password is not a dependency property, and you can't use it in a trigger. Also PasswordBox is sealed, so you can't override this notification behavior, but you can use attached properties here.

The following code demonstrates how.

XAML:

<Window x:Class="WpfTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:WpfTest="clr-namespace:WpfTest"
    Title="Password Box Sample" Height="300" Width="300">
  <Window.Resources>
    <Style x:Key="{x:Type PasswordBox}"
        TargetType="{x:Type PasswordBox}">
      <Setter Property="WpfTest:PasswordBoxMonitor.IsMonitoring"
              Value="True"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type PasswordBox}">
            <Border Name="Bd"
                    Background="{TemplateBinding Background}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    SnapsToDevicePixels="true">
              <Grid>
                <ScrollViewer x:Name="PART_ContentHost"
                              SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                <TextBlock Text="Please enter your password" 
                           Margin="4, 2, 0, 0"
                           Foreground="Gray" 
                           Visibility="Collapsed"
                           Name="txtPrompt" />
              </Grid>
            </Border>
            <ControlTemplate.Triggers>
              <Trigger Property="IsEnabled"
                                         Value="false">
                <Setter TargetName="Bd"
                                            Property="Background"
                                            Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                <Setter Property="Foreground"
                                            Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
              </Trigger>
              <Trigger Property="WpfTest:PasswordBoxMonitor.PasswordLength" Value="0">
                <Setter Property="Visibility" TargetName="txtPrompt" Value="Visible"/>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Window.Resources>
  <Grid>
    <PasswordBox VerticalAlignment="Top"/>
  </Grid>
</Window>

C#:

using System.Windows;
using System.Windows.Controls;

namespace WpfTest {
    public partial class Window1 : Window {
        public Window1() {
            InitializeComponent();
        }
    }

  public class PasswordBoxMonitor : DependencyObject {
    public static bool GetIsMonitoring(DependencyObject obj) {
      return (bool)obj.GetValue(IsMonitoringProperty);
    }

    public static void SetIsMonitoring(DependencyObject obj, bool value) {
      obj.SetValue(IsMonitoringProperty, value);
    }

    public static readonly DependencyProperty IsMonitoringProperty =
        DependencyProperty.RegisterAttached("IsMonitoring", typeof(bool), typeof(PasswordBoxMonitor), new UIPropertyMetadata(false, OnIsMonitoringChanged));

    public static int GetPasswordLength(DependencyObject obj) {
      return (int)obj.GetValue(PasswordLengthProperty);
    }

    public static void SetPasswordLength(DependencyObject obj, int value) {
      obj.SetValue(PasswordLengthProperty, value);
    }

    public static readonly DependencyProperty PasswordLengthProperty =
        DependencyProperty.RegisterAttached("PasswordLength", typeof(int), typeof(PasswordBoxMonitor), new UIPropertyMetadata(0));

    private static void OnIsMonitoringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
      var pb = d as PasswordBox;
      if (pb == null) {
        return;
      }
      if ((bool) e.NewValue) {
        pb.PasswordChanged += PasswordChanged;
      } else {
        pb.PasswordChanged -= PasswordChanged;
      }
    }

    static void PasswordChanged(object sender, RoutedEventArgs e) {
      var pb = sender as PasswordBox;
      if (pb == null) {
        return;
      }
      SetPasswordLength(pb, pb.Password.Length);
    }
  }
}

Please notice PasswordBoxMonitor in XAML code.

云归处 2024-08-15 03:13:06

您可以自己显示/隐藏背景,而不是使用触发器:

XAML:

<PasswordBox x:Name="passwordBox" PasswordChanged="passwordChanged" 
        Background="{StaticResource PasswordHint}" />

代码隐藏:

// helper to hide watermark hint in password field
private void passwordChanged(object sender, RoutedEventArgs e)
{           
    if (passwordBox.Password.Length == 0)
        passwordBox.Background.Opacity = 1;
    else
        passwordBox.Background.Opacity = 0;
}

you can show/hide the background by yourself instead of using triggers:

XAML:

<PasswordBox x:Name="passwordBox" PasswordChanged="passwordChanged" 
        Background="{StaticResource PasswordHint}" />

Code behind:

// helper to hide watermark hint in password field
private void passwordChanged(object sender, RoutedEventArgs e)
{           
    if (passwordBox.Password.Length == 0)
        passwordBox.Background.Opacity = 1;
    else
        passwordBox.Background.Opacity = 0;
}
挽清梦 2024-08-15 03:13:06

您可以使用我的方法来实现水印行为。您所要做的就是复制并粘贴 TextBoxWatermarkBehavior 并将 Behavior 更改为 Behavior

您可以在此处找到演示项目

you can use my approach for a watermark behavior. all you have to do is copy and paste the TextBoxWatermarkBehavior and the change the Behavior<TextBox> to Behavior<PasswordBox>.

you can find a demo project here

柠栀 2024-08-15 03:13:06

@blindmeis 的建议很好。对于PasswordBox,该类如下。

public class PasswordBoxWatermarkBehavior : System.Windows.Interactivity.Behavior<PasswordBox>
{
    private TextBlockAdorner adorner;
    private WeakPropertyChangeNotifier notifier;

    #region DependencyProperty's

    public static readonly DependencyProperty LabelProperty =
        DependencyProperty.RegisterAttached("Label", typeof(string), typeof(PasswordBoxWatermarkBehavior));

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

    public static readonly DependencyProperty LabelStyleProperty =
        DependencyProperty.RegisterAttached("LabelStyle", typeof(Style), typeof(PasswordBoxWatermarkBehavior));

    public Style LabelStyle
    {
        get { return (Style)GetValue(LabelStyleProperty); }
        set { SetValue(LabelStyleProperty, value); }
    }

    #endregion

    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.Loaded += this.AssociatedObjectLoaded;
        this.AssociatedObject.PasswordChanged += AssociatedObjectPasswordChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        this.AssociatedObject.Loaded -= this.AssociatedObjectLoaded;
        this.AssociatedObject.PasswordChanged -= this.AssociatedObjectPasswordChanged;

        this.notifier = null;
    }

    private void AssociatedObjectPasswordChanged(object sender, RoutedEventArgs e)
    {
        this.UpdateAdorner();
    }

    private void AssociatedObjectLoaded(object sender, System.Windows.RoutedEventArgs e)
    {
        this.adorner = new TextBlockAdorner(this.AssociatedObject, this.Label, this.LabelStyle);

        this.UpdateAdorner();

        //AddValueChanged for IsFocused in a weak manner
        this.notifier = new WeakPropertyChangeNotifier(this.AssociatedObject, UIElement.IsFocusedProperty);
        this.notifier.ValueChanged += new EventHandler(this.UpdateAdorner);
    }

    private void UpdateAdorner(object sender, EventArgs e)
    {
        this.UpdateAdorner();
    }


    private void UpdateAdorner()
    {
        if (!String.IsNullOrEmpty(this.AssociatedObject.Password) || this.AssociatedObject.IsFocused)
        {
            // Hide the Watermark Label if the adorner layer is visible
            this.AssociatedObject.TryRemoveAdorners<TextBlockAdorner>();
        }
        else
        {
            // Show the Watermark Label if the adorner layer is visible
            this.AssociatedObject.TryAddAdorner<TextBlockAdorner>(adorner);
        }
    }
}

@blindmeis's suggestion is good. For PasswordBox the class would be as follows.

public class PasswordBoxWatermarkBehavior : System.Windows.Interactivity.Behavior<PasswordBox>
{
    private TextBlockAdorner adorner;
    private WeakPropertyChangeNotifier notifier;

    #region DependencyProperty's

    public static readonly DependencyProperty LabelProperty =
        DependencyProperty.RegisterAttached("Label", typeof(string), typeof(PasswordBoxWatermarkBehavior));

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

    public static readonly DependencyProperty LabelStyleProperty =
        DependencyProperty.RegisterAttached("LabelStyle", typeof(Style), typeof(PasswordBoxWatermarkBehavior));

    public Style LabelStyle
    {
        get { return (Style)GetValue(LabelStyleProperty); }
        set { SetValue(LabelStyleProperty, value); }
    }

    #endregion

    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.Loaded += this.AssociatedObjectLoaded;
        this.AssociatedObject.PasswordChanged += AssociatedObjectPasswordChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        this.AssociatedObject.Loaded -= this.AssociatedObjectLoaded;
        this.AssociatedObject.PasswordChanged -= this.AssociatedObjectPasswordChanged;

        this.notifier = null;
    }

    private void AssociatedObjectPasswordChanged(object sender, RoutedEventArgs e)
    {
        this.UpdateAdorner();
    }

    private void AssociatedObjectLoaded(object sender, System.Windows.RoutedEventArgs e)
    {
        this.adorner = new TextBlockAdorner(this.AssociatedObject, this.Label, this.LabelStyle);

        this.UpdateAdorner();

        //AddValueChanged for IsFocused in a weak manner
        this.notifier = new WeakPropertyChangeNotifier(this.AssociatedObject, UIElement.IsFocusedProperty);
        this.notifier.ValueChanged += new EventHandler(this.UpdateAdorner);
    }

    private void UpdateAdorner(object sender, EventArgs e)
    {
        this.UpdateAdorner();
    }


    private void UpdateAdorner()
    {
        if (!String.IsNullOrEmpty(this.AssociatedObject.Password) || this.AssociatedObject.IsFocused)
        {
            // Hide the Watermark Label if the adorner layer is visible
            this.AssociatedObject.TryRemoveAdorners<TextBlockAdorner>();
        }
        else
        {
            // Show the Watermark Label if the adorner layer is visible
            this.AssociatedObject.TryAddAdorner<TextBlockAdorner>(adorner);
        }
    }
}
倾城°AllureLove 2024-08-15 03:13:06

Werner Wichtig 的方法简单又好,但不适用于带有某些文本的 Background 属性。我已将它与 Style 属性一起使用。

PasswordBox > 的 XAML 代码Style 资源:

<Window.Resources>
    <Style x:Key="PasswordStyle" TargetType="PasswordBox" >
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="PasswordBox">
                    <Border MinWidth="{TemplateBinding MinWidth}">
                        <Grid>
                            <StackPanel Margin="8">
                                <ScrollViewer x:Name="PART_ContentHost"/>
                            </StackPanel>
                            <StackPanel>
                                <TextBlock Name="PART_TempText" Text="Enter Password" Foreground="#807C7C"
    Visibility="Collapsed" TextAlignment="Center" HorizontalAlignment="Center" Padding="8" />
                            </StackPanel>
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding Password.Length, RelativeSource={RelativeSource Self}}" Value="0">
                            <Setter TargetName="PART_TempText" Property="Visibility" Value="Visible" />
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

您的目标 PasswordBox 代码:

<PasswordBox x:Name="txtPasswordBox" PasswordChanged="txtPasswordBox_PasswordChanged" 
    Style="{StaticResource PasswordStyle}" />

PasswordBoxPasswordChanged 事件上实现的代码隐藏逻辑:

private void txtPasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (txtPasswordBox.Password.Length == 0)
        txtPasswordBox.Style = Resources["PasswordStyle"] as Style;
    else
        txtPasswordBox.Style = null;
}

这是最终的结果:

在此输入图像描述

Werner Wichtig 's approach is simple and good, but not applicable on Background property with some text. I have used it with Style property.

XAML code for PasswordBox > Style resource:

<Window.Resources>
    <Style x:Key="PasswordStyle" TargetType="PasswordBox" >
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="PasswordBox">
                    <Border MinWidth="{TemplateBinding MinWidth}">
                        <Grid>
                            <StackPanel Margin="8">
                                <ScrollViewer x:Name="PART_ContentHost"/>
                            </StackPanel>
                            <StackPanel>
                                <TextBlock Name="PART_TempText" Text="Enter Password" Foreground="#807C7C"
    Visibility="Collapsed" TextAlignment="Center" HorizontalAlignment="Center" Padding="8" />
                            </StackPanel>
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding Password.Length, RelativeSource={RelativeSource Self}}" Value="0">
                            <Setter TargetName="PART_TempText" Property="Visibility" Value="Visible" />
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

Your targeted PasswordBox code:

<PasswordBox x:Name="txtPasswordBox" PasswordChanged="txtPasswordBox_PasswordChanged" 
    Style="{StaticResource PasswordStyle}" />

Code-behind logic implemented on PasswordChanged event of PasswordBox:

private void txtPasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (txtPasswordBox.Password.Length == 0)
        txtPasswordBox.Style = Resources["PasswordStyle"] as Style;
    else
        txtPasswordBox.Style = null;
}

Here is final result:

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文