简体   繁体   中英

Using a textbox as a passwordbox with a password char set in wpf

I'm having trouble trying to setup a textbox with a password char AND a placeholder. I've already done one for the username and that works fine but I'm stumpt over what I can do with the passwordbox.

I know you can have a passwordbox with the password char but you can't have a placeholder as well in wpf.

I have this for the username, I want the same done for the passwordbox:

this.usernameTxt.Foreground = Brushes.LightGray;

        this.usernameTxt.Text = "Username";

this.usernameTxt.GotKeyboardFocus += new KeyboardFocusChangedEventHandler(this.usernameTxt_Selected);

this.usernameTxt.PreviewLostKeyboardFocus += new KeyboardFocusChangedEventHandler(this.usernameTxt_Lostfocus);

public void usernameTxt_Lostfocus(object sender, EventArgs e)
    {
        if (string.IsNullOrEmpty(usernameTxt.Text))
                    {
                        this.usernameTxt.Text = "Username";
                        this.usernameTxt.Foreground = Brushes.LightGray;
                    }
    }

    public void usernameTxt_Selected(object sender, EventArgs e)
    {
            this.usernameTxt.Focus();
            this.usernameTxt.SelectAll();
            this.usernameTxt.Text = "";
            this.usernameTxt.Foreground = Brushes.Black;
    }

Here's a workaround that you can review. In this example, I rely on the TextChanged event of my textbox control that's embedded within the password control.

When the TextChanged event is triggered, I use a stack to capture the last character entered and then I maintain within my view-model an obfuscated password as well as an actual password. When the delete or back key is pressed, I pop the last character entered off the stack.

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        xmlns:converters="clr-namespace:WpfApplication1.Converters"
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>
    <Grid>
        <PasswordBox x:Name="passwordBox" Height="20" PasswordChar="*" Width="200" Background="LightYellow" >
            <PasswordBox.Template>
                <ControlTemplate>
                    <TextBox x:Name="PasswordTextbox" GotFocus="GotFocus" Text="{Binding ObfuscatedPassword, Mode=TwoWay}" KeyUp="PasswordTextbox_KeyUp" TextAlignment="Center" Foreground="LightGray" />
                </ControlTemplate>
            </PasswordBox.Template>
        </PasswordBox>
    </Grid>
</Window>

View-model:

namespace WpfApplication1
{
    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string obfuscatedPassword = "user name";

        public string ObfuscatedPassword
        {
            get { return obfuscatedPassword; }
            set 
            { 
                if (this.obfuscatedPassword != value)
                {
                    this.obfuscatedPassword = value;
                    OnPropertyChanged();
                }
            }
        }

        private string actualPassword = null;
        public string ActualPassword
        {
            get { return actualPassword; }
            set
            {
                if (this.actualPassword != value)
                {
                    this.actualPassword = value;
                    OnPropertyChanged();
                }
            }
        }

        private void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Code-behind:

public partial class MainWindow : Window
{
    ViewModel viewModel = null;
    public MainWindow()
    {
        InitializeComponent();

        this.viewModel = this.DataContext as ViewModel;
    }

    public string defaultText = "user name";

    Stack<string> charStack = new Stack<string>();


    private void PasswordTextbox_KeyUp(object sender, KeyEventArgs e)
    {
        var textbox = sender as TextBox;
        textbox.Text = string.Empty;

        var key = e.Key.ToString();

        if (this.viewModel.ObfuscatedPassword == defaultText)
        {
            this.viewModel.ObfuscatedPassword = string.Empty;
        }

        var deleteLastCharacter = (e.Key == Key.Delete || e.Key == Key.Back);

        if (deleteLastCharacter)
        {

            if (charStack.Count > 0)
            {
                charStack.Pop();
            }

            if (charStack.Count == 0)
            {
                textbox.Text = defaultText;
                textbox.CaretIndex = defaultText.Length;
                textbox.SelectAll();
                e.Handled = true;
                return;
            }
        }
        else if (IsTextAllowed(key))
        {
            charStack.Push(key);
        }
        else
        {
            e.Handled = true;
            return;
        }

        this.viewModel.ObfuscatedPassword = ObfuscatePassword();
        this.viewModel.ActualPassword = ActualizePassword();

        textbox.CaretIndex = this.viewModel.ObfuscatedPassword.Length;
        e.Handled = true;
    }

    private static bool IsTextAllowed(string text)
    {
        Regex regex = new Regex(@"^.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$"); //regex that matches disallowed text
        return !regex.IsMatch(text);
    }

    private string ActualizePassword()
    {
        var password = string.Empty;

        foreach (var character in charStack.Reverse())
        {
            password += character;
        }

        return password;
    }

    private string ObfuscatePassword()
    {
        var password = string.Empty;

        foreach (var character in charStack.Reverse())
        {
            password += passwordBox.PasswordChar;
        }

        return password;
    }

    private void GotFocus(object sender, RoutedEventArgs e)
    {
        if (this.viewModel.ObfuscatedPassword == defaultText)
        {
            this.viewModel.ObfuscatedPassword = string.Empty;
        }
    }

}

First download password dot font from this link , https://github.com/davidagraf/passwd/blob/master/public/ttf/password.ttf

Add folder named "AppFonts" under this put password.ttf file and add reference of this font to your WPF application App.xaml. So globally you can access this font.

<Application.Resources>
        <ResourceDictionary>            
            <Style x:Key="CustomFont">                
                <Setter Property="TextElement.FontFamily" Value="/AppFonts/#password"></Setter>
            </Style>
        </ResourceDictionary>
    </Application.Resources>

Create a PasswordViewModel and download nuget package MvvmLight of GalaSoft.

using System;
using GalaSoft.MvvmLight;

public class PasswordViewModel : ViewModelBase
    {
     private string _PlainPassword { get; set; }
     public string PlainPassword
        {
            get { 
               
                return _PlainPassword; 
            }
            set
            {
                _PlainPassword = value;
                RaisePropertyChanged("Password");
            }
        }
}

now create your view PasswordUserControl.xaml and view constructor add view model to DataContext.

  PasswordViewModel passwordModel= new PasswordViewModel();
  this.DataContext = passwordModel;

and in view xaml paste this

<Grid  Margin="20,0,15,0" Height="45">
              
                <TextBox Grid.Row="3" Name="txtPasswordEntry" Background="Transparent" Padding="12" Foreground="#787878" Height="45" 
                Text="{Binding PlainPassword,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" 
                FontFamily="/WpfApplication;component/AppFonts/#Password" FontSize="14" FontWeight="Medium">
                    <TextBox.InputBindings>
                        <!--Disable CTRL+C (COPY) -->
                        <KeyBinding Command="ApplicationCommands.NotACommand" Key="C" Modifiers="Control" />
                        <!--Disable CTRL+X (CUT) -->
                        <KeyBinding Command="ApplicationCommands.NotACommand" Key="X" Modifiers="Control" />
                    </TextBox.InputBindings>
                    <TextBox.ContextMenu>
                        <!--Hide context menu where you could copy/cut as well -->
                        <ContextMenu Visibility="Collapsed" />
                    </TextBox.ContextMenu>
                </TextBox>
                <TextBlock IsHitTestVisible="False" Padding="13" Text="Password" Foreground="#787878" Height="45" FontSize="14" FontWeight="Medium">
                <TextBlock.Style>
                    <Style TargetType="{x:Type TextBlock}">
                        <Setter Property="Visibility" Value="Collapsed"/>
                        <Style.Triggers>
                                <DataTrigger Binding="{Binding Text, ElementName=txtPasswordEntry}" Value="">
                                <Setter Property="Visibility" Value="Visible"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </TextBlock.Style>
            </TextBlock>
            </Grid>

Note: Hear FontFamily="/WpfApplication;component/AppFonts/#Password" WpfApplication is your Application Name. Or you can change font family from textbox property.

Now run the app . See password box has a place holder text Password and write your password it will disappear. Also its secure you can't copy cut of this password. 在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM