├── .gitignore ├── example ├── PasswordBoxMVVMExample.csproj.user ├── App.xaml ├── PasswordBoxMVVMExample.csproj ├── ViewModels │ ├── ViewModelBase.cs │ └── LoginViewModel.cs ├── AssemblyInfo.cs ├── MainWindow.xaml ├── Views │ ├── LoginView.xaml.cs │ └── LoginView.xaml ├── App.xaml.cs ├── MainWindow.xaml.cs └── Commands │ └── LoginCommand.cs ├── command-reference.txt ├── PasswordBoxMVVM ├── PasswordBoxMVVM.csproj.user ├── AssemblyInfo.cs ├── Components │ ├── BindablePasswordBox.xaml │ └── BindablePasswordBox.xaml.cs └── PasswordBoxMVVM.csproj ├── .github └── workflows │ └── nuget.yml └── PasswordBoxMVVM.sln /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | obj/ 3 | bin/ 4 | secrets.txt 5 | -------------------------------------------------------------------------------- /example/PasswordBoxMVVMExample.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /command-reference.txt: -------------------------------------------------------------------------------- 1 | # PACKING 2 | dotnet pack -p:PackageVersion=1.0.0 --configuration Release 3 | 4 | # PUSHING 5 | dotnet nuget push ./bin/Release/PasswordBoxMVVM.1.0.0.nupkg -s https://api.nuget.org/v3/index.json -k 6 | 7 | -------------------------------------------------------------------------------- /PasswordBoxMVVM/PasswordBoxMVVM.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Designer 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/PasswordBoxMVVMExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | netcoreapp3.1 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/ViewModels/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace PasswordBoxMVVM.ViewModels 4 | { 5 | public class ViewModelBase : INotifyPropertyChanged 6 | { 7 | public event PropertyChangedEventHandler PropertyChanged; 8 | 9 | public virtual void OnPropertyChanged(string propertyName) 10 | { 11 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /example/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /PasswordBoxMVVM/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /example/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /PasswordBoxMVVM/Components/BindablePasswordBox.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/Views/LoginView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Data; 7 | using System.Windows.Documents; 8 | using System.Windows.Input; 9 | using System.Windows.Media; 10 | using System.Windows.Media.Imaging; 11 | using System.Windows.Navigation; 12 | using System.Windows.Shapes; 13 | 14 | namespace PasswordBoxMVVM.Views 15 | { 16 | /// 17 | /// Interaction logic for LoginView.xaml 18 | /// 19 | public partial class LoginView : UserControl 20 | { 21 | public LoginView() 22 | { 23 | InitializeComponent(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using PasswordBoxMVVM.ViewModels; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Configuration; 5 | using System.Data; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | 10 | namespace PasswordBoxMVVM 11 | { 12 | /// 13 | /// Interaction logic for App.xaml 14 | /// 15 | public partial class App : Application 16 | { 17 | protected override void OnStartup(StartupEventArgs e) 18 | { 19 | MainWindow = new MainWindow() 20 | { 21 | DataContext = new LoginViewModel() 22 | }; 23 | 24 | MainWindow.Show(); 25 | 26 | base.OnStartup(e); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /example/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace PasswordBoxMVVM 17 | { 18 | /// 19 | /// Interaction logic for MainWindow.xaml 20 | /// 21 | public partial class MainWindow : Window 22 | { 23 | public MainWindow() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /PasswordBoxMVVM/PasswordBoxMVVM.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | true 6 | PasswordBoxMVVM 7 | SingletonSean 8 | A password box that allows binding. 9 | password box wpf binding mvvm 10 | https://github.com/SingletonSean/wpf-mvvm-password-box 11 | https://github.com/SingletonSean/wpf-mvvm-password-box 12 | git 13 | true 14 | snupkg 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/ViewModels/LoginViewModel.cs: -------------------------------------------------------------------------------- 1 | using PasswordBoxMVVM.Commands; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Windows.Input; 6 | 7 | namespace PasswordBoxMVVM.ViewModels 8 | { 9 | public class LoginViewModel : ViewModelBase 10 | { 11 | private string _username; 12 | public string Username 13 | { 14 | get 15 | { 16 | return _username; 17 | } 18 | set 19 | { 20 | _username = value; 21 | OnPropertyChanged(nameof(Username)); 22 | } 23 | } 24 | 25 | private string _password; 26 | public string Password 27 | { 28 | get 29 | { 30 | return _password; 31 | } 32 | set 33 | { 34 | _password = value; 35 | OnPropertyChanged(nameof(Password)); 36 | } 37 | } 38 | 39 | public ICommand LoginCommand { get; } 40 | 41 | public LoginViewModel() 42 | { 43 | LoginCommand = new LoginCommand(this); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/nuget.yml: -------------------------------------------------------------------------------- 1 | name: "Deploy to NuGet" 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | env: 9 | PROJECT_PATH: 'PasswordBoxMVVM/PasswordBoxMVVM.csproj' 10 | PACKAGE_OUTPUT_DIRECTORY: ${{ github.workspace }}\output 11 | NUGET_SOURCE_URL: 'https://api.nuget.org/v3/index.json' 12 | 13 | jobs: 14 | deploy: 15 | name: 'Deploy' 16 | runs-on: 'windows-latest' 17 | steps: 18 | - name: 'Checkout' 19 | uses: actions/checkout@v2 20 | 21 | - name: 'Install dotnet' 22 | uses: actions/setup-dotnet@v1 23 | with: 24 | dotnet-version: '3.1.x' 25 | 26 | - name: 'Restore packages' 27 | run: dotnet restore ${{ env.PROJECT_PATH }} 28 | 29 | - name: 'Build project' 30 | run: dotnet build ${{ env.PROJECT_PATH }} --no-restore --configuration Release 31 | 32 | - name: 'Get Version' 33 | id: version 34 | uses: battila7/get-version-action@v2 35 | 36 | - name: 'Pack project' 37 | run: dotnet pack ${{ env.PROJECT_PATH }} --no-restore --no-build --configuration Release --include-symbols -p:PackageVersion=${{ steps.version.outputs.version-without-v }} --output ${{ env.PACKAGE_OUTPUT_DIRECTORY }} 38 | 39 | - name: 'Push package' 40 | run: dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY }}\*.nupkg -k ${{ secrets.NUGET_AUTH_TOKEN }} -s ${{ env.NUGET_SOURCE_URL }} -------------------------------------------------------------------------------- /example/Commands/LoginCommand.cs: -------------------------------------------------------------------------------- 1 | using PasswordBoxMVVM.ViewModels; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Text; 6 | using System.Windows; 7 | using System.Windows.Input; 8 | 9 | namespace PasswordBoxMVVM.Commands 10 | { 11 | public class LoginCommand : ICommand 12 | { 13 | private readonly LoginViewModel _viewModel; 14 | 15 | public LoginCommand(LoginViewModel viewModel) 16 | { 17 | _viewModel = viewModel; 18 | 19 | _viewModel.PropertyChanged += ViewModel_PropertyChanged; 20 | } 21 | 22 | public event EventHandler CanExecuteChanged; 23 | 24 | public bool CanExecute(object parameter) 25 | { 26 | return !string.IsNullOrEmpty(_viewModel.Username) && 27 | !string.IsNullOrEmpty(_viewModel.Password); 28 | } 29 | 30 | public void Execute(object parameter) 31 | { 32 | MessageBox.Show($"Username: {_viewModel.Username}\nPassword: {_viewModel.Password}", "Info", 33 | MessageBoxButton.OK, MessageBoxImage.Information); 34 | } 35 | 36 | private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) 37 | { 38 | CanExecuteChanged?.Invoke(this, new EventArgs()); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /PasswordBoxMVVM.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30413.136 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PasswordBoxMVVMExample", "example\PasswordBoxMVVMExample.csproj", "{36EF3498-9140-43BA-9B9E-B342E77B8DFD}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PasswordBoxMVVM", "PasswordBoxMVVM\PasswordBoxMVVM.csproj", "{832A17EA-C363-4A48-B27E-C011B62DC09A}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {36EF3498-9140-43BA-9B9E-B342E77B8DFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {36EF3498-9140-43BA-9B9E-B342E77B8DFD}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {36EF3498-9140-43BA-9B9E-B342E77B8DFD}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {36EF3498-9140-43BA-9B9E-B342E77B8DFD}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {832A17EA-C363-4A48-B27E-C011B62DC09A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {832A17EA-C363-4A48-B27E-C011B62DC09A}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {832A17EA-C363-4A48-B27E-C011B62DC09A}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {832A17EA-C363-4A48-B27E-C011B62DC09A}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {B48F9BFD-E3AE-40DC-9CFB-A3B50DCC7FBF} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /PasswordBoxMVVM/Components/BindablePasswordBox.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using System.Windows.Data; 4 | 5 | namespace PasswordBoxMVVM.Components 6 | { 7 | public partial class BindablePasswordBox : UserControl 8 | { 9 | private bool _isPasswordChanging; 10 | 11 | public static readonly DependencyProperty PasswordProperty = 12 | DependencyProperty.Register("Password", typeof(string), typeof(BindablePasswordBox), 13 | new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 14 | PasswordPropertyChanged, null, false, UpdateSourceTrigger.PropertyChanged)); 15 | 16 | private static void PasswordPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 17 | { 18 | if(d is BindablePasswordBox passwordBox) 19 | { 20 | passwordBox.UpdatePassword(); 21 | } 22 | } 23 | 24 | public string Password 25 | { 26 | get { return (string)GetValue(PasswordProperty); } 27 | set { SetValue(PasswordProperty, value); } 28 | } 29 | 30 | public BindablePasswordBox() 31 | { 32 | InitializeComponent(); 33 | } 34 | 35 | private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) 36 | { 37 | _isPasswordChanging = true; 38 | Password = passwordBox.Password; 39 | _isPasswordChanging = false; 40 | } 41 | 42 | private void UpdatePassword() 43 | { 44 | if(!_isPasswordChanging) 45 | { 46 | passwordBox.Password = Password; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /example/Views/LoginView.xaml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 57 | 58 | 59 | 60 | 61 |