PasswordBox中的水印

本文关键字:PasswordBox | 更新日期: 2023-09-27 17:58:44

我实现了以下转换器和代码,目的是创建某种水印。此代码适用于TextBlock + TextBox,但不适用于TextBlock + PasswordBox。你知道这个转换器为什么不工作吗?

XAML

 <Helpers:HintConverter x:Key="hint" />
 <TextBlock Height="30" Text="                             password" Foreground="LightGray" Margin="274,264,278,306" Width="248">
        <TextBlock.Visibility>
            <MultiBinding Converter="{StaticResource hint}">
                <Binding ElementName="txtPassword" Path="Text.IsEmpty" />
                <Binding ElementName="txtPassword" Path="IsFocused" />
            </MultiBinding>
        </TextBlock.Visibility>
    </TextBlock>
    <PasswordBox PasswordChanged="PasswordBox_PasswordChanged" Name="txtPassword" BorderThickness="2" Height="30" Margin="273,264,275,306" Background="Transparent">
        <PasswordBox.BorderBrush>
            <LinearGradientBrush EndPoint="1,1" StartPoint="1,0">
                <GradientStop Color="White" Offset="0" />
                <GradientStop Color="White" Offset="0.75" />
                <GradientStop Color="Green" Offset="0.75" />
                <GradientStop Color="#FF0D9ECD" Offset="1" />
            </LinearGradientBrush>
        </PasswordBox.BorderBrush>
    </PasswordBox>

转换器

public class HintConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (values[0] is bool && values[1] is bool)
        {
            bool hasText = !(bool)values[0];
            bool hasFocus = (bool)values[1];
            if (hasFocus || hasText)
                return Visibility.Collapsed;
        }
        return Visibility.Visible;
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

PasswordBox中的水印

PasswordBox没有Text属性,它有PasswordSecurePassword属性,它们不是依赖属性,因此您不会收到任何更改通知。

您可以定义一个附加属性,订阅PasswordChanged事件并绑定到它:

public static class PasswordBoxExtensions
{
    public static readonly DependencyProperty IsActiveProperty = 
        DependencyProperty.RegisterAttached(
            "IsActive", typeof(bool), typeof(PasswordBoxExtensions), 
            new FrameworkPropertyMetadata(OnIsActiveChanged));
    private static void OnIsActiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var passwordBox = d as PasswordBox;
        if (passwordBox == null) return;
        passwordBox.PasswordChanged -= OnPasswordChanged;
        if ((bool)e.NewValue)
        {
            SetIsPasswordEmpty(passwordBox);
            passwordBox.PasswordChanged += OnPasswordChanged;
        }
    }
    private static void OnPasswordChanged(object sender, RoutedEventArgs e)
    {
        SetIsPasswordEmpty((PasswordBox)sender);
    }
    public static void SetIsActive(PasswordBox element, bool value)
    {
        element.SetValue(IsActiveProperty, value);
    }
    public static bool GetIsActive(PasswordBox element)
    {
        return (bool)element.GetValue(IsActiveProperty);
    }
    public static readonly DependencyPropertyKey IsPasswordEmptyPropertyKey = 
        DependencyProperty.RegisterAttachedReadOnly(
            "IsPasswordEmpty", typeof(bool), typeof(PasswordBoxExtensions),
            new FrameworkPropertyMetadata());
    public static readonly DependencyProperty IsPasswordEmptyProperty =
        IsPasswordEmptyPropertyKey.DependencyProperty;
    private static void SetIsPasswordEmpty(PasswordBox element)
    {
        element.SetValue(IsPasswordEmptyPropertyKey, element.SecurePassword.Length == 0);
    }
    public static bool GetIsPasswordEmpty(PasswordBox element)
    {
        return (bool)element.GetValue(IsPasswordEmptyProperty);
    }
}

用法:

<PasswordBox Name="txtPassword" app:PasswordBoxExtensions.IsActive="True" />

<Binding ElementName="txtPassword" Path="(app:PasswordBoxExtensions.IsPasswordEmpty)" />

我做下一个

XAML:

<PasswordBox Name="PasswordBox"
             PasswordChanged="PasswordBox_OnPasswordChanged"/>
<TextBlock Text="PASSWORD"
           IsHitTestVisible="False">
    <TextBlock.Style>
        <Style TargetType="TextBlock" BasedOn="{StaticResource WatermarkStyle}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsPasswordWatermarkVisible}" Value="False">
                    <Setter Property="Visibility" Value="Collapsed" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBlock.Style>
</TextBlock>

代码背后:

    private MyViewModel ViewModel
    {
        get { return (MyViewModel) DataContext; }
    }
    private void PasswordBox_OnPasswordChanged(object sender, RoutedEventArgs e)
    {
        ViewModel.Password = PasswordBox.Password;
    }

ViewModel:

    public string Password
    {
        get { return _password; }
        set
        {
            _password = value;
            IsPasswordWatermarkVisible = string.IsNullOrEmpty(_password);
        }
    }
    public bool IsPasswordWatermarkVisible
    {
        get { return _isPasswordWatermarkVisible; }
        set
        {
            if (value.Equals(_isPasswordWatermarkVisible)) return;
            _isPasswordWatermarkVisible = value;
            OnPropertyChanged();
        }
    }