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

Комментарии 20

Мы для похожих проблем используем Behavior-ы.
Поддерживаю. Оставлю свою реализацию для наглядности:
public class PasswordBehavior : Behavior<PasswordBox>
{
	public static readonly DependencyProperty PasswordProperty =
		DependencyProperty.Register("Password", typeof(string), typeof(PasswordBehavior), new PropertyMetadata(default(string)));

	private bool _skipUpdate;

	public string Password
	{
		get { return (string)GetValue(PasswordProperty); }
		set { SetValue(PasswordProperty, value); }
	}

	protected override void OnAttached()
	{
		AssociatedObject.PasswordChanged += PasswordBox_PasswordChanged;
	}

	protected override void OnDetaching()
	{
		AssociatedObject.PasswordChanged -= PasswordBox_PasswordChanged;
	}

	protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
	{
		base.OnPropertyChanged(e);

		if (e.Property == PasswordProperty)
		{
			if (!_skipUpdate)
			{
				_skipUpdate = true;
				AssociatedObject.Password = e.NewValue as string;
				_skipUpdate = false;
			}
		}
	}

	private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
	{
		_skipUpdate = true;
		Password = AssociatedObject.Password;
		_skipUpdate = false;
	}
}

И использование:
<PasswordBox>
	<i:Interaction.Behaviors>
		<Behaviors:PasswordBehavior Password="{Binding Password, Mode=TwoWay}" />
	</i:Interaction.Behaviors>
</PasswordBox>
Суть описанного метода состоит в том, чтобы не хранить пароль в открытом виде ни в каком из объектов, а получать его по требованию. В вашем же примере пароль будет находится в VM все время пока существует сама VM.
Если вас беспокоит вопрос безопасности, то можно вместо свойства PasswordBox.Password использовать PasswordBox.SecurePassword. Как вариант, можно реализовать IPasswordSupplier в Behavior'е, забиндить ViewModel.Password на Behavior и оставить в покое контейнер и контрол.
С SecureString не хочется связываться, потому что для дешифрации пароля нужно работать с неуправляемыми типами данных. Во втором варианте вы предлагаете избежать хранения пароля в открытом виде в классе PasswordBehavior вашего примера (т.к. он будет дешифроваться с помощью интерфейса), но после биндинга к свойству ViewModel он опять же будет храниться в открытом виде. Биндинг который в конечном итоге работает со свойством PasswordBox.Password нельзя использовать вообще.
View
<PasswordBox Name="PasswordBox" PasswordChar="*" />
<Button Content="Login" Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=PasswordBox}" IsDefault="True" />

ViewModel
private void OnLoginCommand(object commandParameter) 
{
   var password = ((PasswordBox)commandParameter).Password;
}

А еще можно для взаимодействия ViewModel и View делать behavior'ы.
В данном решении ViewModel Жестко связан с View. В VM вообще не должно быть даже упоминания элементов управления.
Да, поэтому я и написал про behavior'ы как вариант. VM знает о них и через них взаимодействует с вьюхой.
Кроме того, если нет необходимости выполнения команды Login а нужно просто получить данные из контрола и сохранить, например, в конфиг, то к паролю вообще нет доступа.
Спасибо, рецензию я читал, а вот книгу пока еще нет. В моем случае действительно можно было сразу передать интерфейс в VM и тогда unity в самом VM вообще не нужен. Я описал более общий подход, который можно использовать в разных сценариях. Например, если ViewModel и View создаются в разных местах, или же View вообще явно не создается, а биндится в ресурсах:

    <DataTemplate DataType="{x:Type my:LoginViewModel}">
        <my:LoginControl />
    </DataTemplate>


В этом случае передать интерфейс в VM нет возможности.
Если нет возможности использовать Constructor Injection, Вам придёться использовать контейнер как Service Locator внутри этой VM, а это уже попахивает.
я был очень вдохновлен философией MVVM и решил все будущие проекты писать исключительно с ее использованием.


А как Вы боритесь с жирной ViewModel? У нас ViewModel главной вьюхи настолько здоровая стала, что логику отвечающую за определенные действия, приходится пихать в partial классы :(
Как вариант элементы главного View можно сгрупировать в отдельные UserControl-ы и перенести логику связанную с ними в соответствующие ViewModel-ы, которые будут в виде свойств в главном ViewModel.
По-моему, основной способ борьбы с раздуванием ViewModel — следование принципу SRP и использование Dependency Injection.
Бороться с жирными вьюмоделями надо точно так же, как и с другими жирными классами. SOLID, в частности буква S.
Ещё помогает избегание реализации вызова PropertyChanged в сеттере каждого свойства, если вдуматься, то часто бывает, что в половине свойств это не нужно.
Интересно за что автору в карму отрицательно капнули?
Неудачное на ваш взгляд решение и тем более данная статья по-моему не повод…

П.С.: мне изложенное решение не нравится тоже, если что.
Судя по профилю у автора карма 2 и 2 голоса. Т.е. карму ему никто не портил. У него отрицательный рейтинг, т.к. минусовали статью. Статью минусовали, скорее всего, именно из-за того, что предлагаемое решение не оптимальное, и в комментариях описали почему.
И то правда. Не на ту цифру глянул. На момент каммента 1 голос был и отрицательная карма.
Согрешил по невнимательности. Извиняюсь.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории