Как стать автором
Обновить

Доступ к PasswordBox в условиях MVVM

Время на прочтение3 мин
Количество просмотров24K
Впервые прочитав знаменитую статью Джоша Смита WPF Apps With The Model-View-ViewModel Design Pattern я был очень вдохновлен философией MVVM и решил все будущие проекты писать исключительно с ее использованием. По мере «внедрения» MVVM я столкнулся с определенными трудностями и недостатками (на мой субъективный взгляд). В одном из проектов мне был необходим UserControl, который бы позволял авторизироваться на веб-сервисе. При попытке забиндить свойство ViewModel к элементу управления PasswordBox я получил такую ошибку:
A 'Binding' cannot be set on the 'Password' property of type 'PasswordBox'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject

Причина, по которой свойство Password не сделано DependencyProperty объясняется соображениями безопасности. Если бы можно было забиндить свойство к PasswordBox, то оно бы хранилось в памяти в открытом виде, а это не очень хорошая идея с точки зрения Microsoft. Поэтому пароль в PasswordBox хранится в зашифрованном виде и дешифруется при вызове свойства Password.

Я нашел несколько решений этой проблемы. Есть способы, которые сохраняют принципы MVVM, но создают описанную выше проблему. Если требования не предусматривают такого рода безопасности, то можно пойти против системы при помощи Attached Properties все-таки забиндить свойство ViewModel. Я же опишу другой способ, который, возможно, слегка нарушает принципы MVVM, но сохраняет принцип безопасности хранения пароля.

Создаем простой котрол:

        <Label Content="Login:" HorizontalAlignment="Right"/>
        <TextBox Grid.Column="1" Margin="3" Text="{Binding UserName, Mode=TwoWay}"/>
        <Label Grid.Row="1" Content="Password:" HorizontalAlignment="Right"/>
        <PasswordBox Grid.Row="1" Grid.Column="1" Margin="3" Name="pwdBox"/>
        <Button Grid.Row="2" Grid.ColumnSpan="2" Width="70" Height="25" 
               HorizontalAlignment="Right" Content="Login"
               Command="{Binding LoginCommand}"/>

Для получения пароля нам понадобится интерфейс:

    public interface IPasswordSupplier
    {
        string GetPassword();
    }


Наследуем контрол от этого интерфейса и возвращаем в функции значение пароля:

    public partial class LoginControl : UserControl, IPasswordSupplier
    {
        
        public LoginControl()
        {
            InitializeComponent();
        }

        public string GetPassword()
        {
            return pwdBox.Password;
        }
    }


Неприятная особенность метода заключается в том, что LoginControl придется создавать в коде, т.к. необходимо получить IPasswordSupplier. C помощью IoC контейнера регистрируем экземпляр котрола в App.xaml.cs:

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            IUnityContainer container = new UnityContainer();
            LoginControl loginControl = new LoginControl();
            container.RegisterInstance<IPasswordSupplier>(loginControl);

            LoginViewModel loginViewModel = new LoginViewModel(container);
            loginControl.DataContext = loginViewModel;

            MainWindow mainWindow = new MainWindow(loginControl);
            MainWindowViewModel windowViewModel = new MainWindowViewModel(loginViewModel);
            mainWindow.DataContext = windowViewModel;
            mainWindow.Show();
        }


Теперь во ViewModel контрола мы можем получить доступ к Password свойству PasswordBox:

        public string Password
        {
            get
            {
                IPasswordSupplier passwordSupplier = container.Resolve<IPasswordSupplier>();
                return passwordSupplier.GetPassword();
            }
        }


Таким образом, свойство Password нашего ViewModel не хранит значение пароля а получает его при каждом обращении из PasswordBox, который в свою очередь дешифрует его для нас. Возможно, это нарушает MVVM, т.к. теперь наш ViewModel косвенно связан с View посредством данного интерфейса, но все же мне этот способ кажется наиболее подходящим для решения проблемы.
Теги:
Хабы:
Всего голосов 6: ↑1 и ↓5-4
Комментарии20

Публикации

Истории

Работа

.NET разработчик
80 вакансий

Ближайшие события

19 августа – 20 октября
RuCode.Финал. Чемпионат по алгоритмическому программированию и ИИ
МоскваНижний НовгородЕкатеринбургСтавропольНовосибрискКалининградПермьВладивостокЧитаКраснорскТомскИжевскПетрозаводскКазаньКурскТюменьВолгоградУфаМурманскБишкекСочиУльяновскСаратовИркутскДолгопрудныйОнлайн
24 – 25 октября
One Day Offer для AQA Engineer и Developers
Онлайн
25 октября
Конференция по росту продуктов EGC’24
МоскваОнлайн
26 октября
ProIT Network Fest
Санкт-Петербург
7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань