C# 如何绑定到 MVVM 中的 PasswordBox
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1483892/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to bind to a PasswordBox in MVVM
提问by mark smith
I have come across a problem with binding to a PasswordBox
. It seems it's a security risk but I am using the MVVM pattern so I wish to bypass this. I found some interesting code here (has anyone used this or something similar?)
我遇到了绑定到 P 的问题asswordBox
。这似乎是一个安全风险,但我使用的是 MVVM 模式,所以我希望绕过它。我在这里找到了一些有趣的代码(有没有人使用过这个或类似的东西?)
http://www.wpftutorial.net/PasswordBox.html
http://www.wpftutorial.net/PasswordBox.html
It technically looks great, but I am unsure of how to retrieve the password.
从技术上讲,它看起来很棒,但我不确定如何找回密码。
I basically have properties in my LoginViewModel
for Username
and Password
. Username
is fine and is working as it's a TextBox
.
我的LoginViewModel
forUsername
和 中基本上都有属性Password
。Username
很好,并且正在工作,因为它是TextBox
.
I used the code above as stated and entered this
我按照上面的说明使用了代码并输入了这个
<PasswordBox ff:PasswordHelper.Attach="True"
ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>
When I had the PasswordBox
as a TextBox
and Binding Path=Password
then the property in my LoginViewModel
was updated.
当我有PasswordBox
一个TextBox
和Binding Path=Password
然后我的财产LoginViewModel
进行了更新。
My code is very simple, basically I have a Command
for my Button
. When I press it CanLogin
is called and if it returns true it calls Login
.
You can see I check my property for Username
here which works great.
我的代码很简单,基本上Command
我的Button
. 当我按下它时,它CanLogin
被调用,如果它返回 true,它会调用Login
.
你可以看到我Username
在这里检查我的财产,这很好用。
In Login
I send along to my service a Username
and Password
, Username
contains data from my View
but Password
is Null|Empty
在Login
我发送给我的服务 a Username
and Password
,Username
包含来自我的数据View
但是Password
是Null|Empty
private DelegateCommand loginCommand;
public string Username { get; set; }
public string Password { get; set; }
public ICommand LoginCommand
{
get
{
if (loginCommand == null)
{
loginCommand = new DelegateCommand(
Login, CanLogin );
}
return loginCommand;
}
}
private bool CanLogin()
{
return !string.IsNullOrEmpty(Username);
}
private void Login()
{
bool result = securityService.IsValidLogin(Username, Password);
if (result) { }
else { }
}
This is what I am doing
这就是我正在做的
<TextBox Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged}"
MinWidth="180" />
<PasswordBox ff:PasswordHelper.Attach="True"
ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>
I have my TextBox
, this is no problem, but in my ViewModel
the Password
is empty.
我有我的TextBox
,这没问题,但在我ViewModel
的Password
是空的。
Am I doing something wrong or missing a step?
我做错了什么或错过了一步吗?
I put a breakpoint and sure enough the code enter the static helper class but it never updates my Password
in my ViewModel
.
我放了一个断点,果然代码进入了静态帮助器类,但它从不更新我Password
的ViewModel
.
采纳答案by JustinAngel
Sorry, but you're doing it wrong.
对不起,但你做错了。
People should have the following security guideline tattooed on the inside of their eyelids:
Never keep plain text passwords in memory.
人们应该在眼睑内侧纹上以下安全准则:
永远不要将纯文本密码保存在记忆中。
The reason the WPF/Silverlight PasswordBox
doesn't expose a DP for the Password
property is security related.
If WPF/Silverlight were to keep a DP for Password
it would require the framework to keep the password itself unencrypted in memory. Which is considered quite a troublesome security attack vector.
The PasswordBox
uses encrypted memory (of sorts) and the only way to access the password is through the CLR property.
WPF/SilverlightPasswordBox
不公开Password
属性的 DP的原因与安全相关。
如果 WPF/Silverlight 为Password
它保留一个 DP,它将要求框架在内存中保持密码本身未加密。这被认为是一个相当麻烦的安全攻击媒介。该PasswordBox
(的种类)使用加密存储和访问密码的唯一方法是通过CLR属性。
I would suggest that when accessing the PasswordBox.Password
CLR property you'd refrain from placing it in any variable or as a value for any property.
Keeping your password in plain text on the client machine RAM is a security no-no.
So get rid of that public string Password { get; set; }
you've got up there.
我建议在访问PasswordBox.Password
CLR 属性时不要将它放在任何变量中或作为任何属性的值。
将您的密码以纯文本形式保存在客户端计算机 RAM 上是一个安全禁忌。
所以摆脱public string Password { get; set; }
你已经到了那里。
When accessing PasswordBox.Password
, just get it out and ship it to the server ASAP.
Don't keep the value of the password around and don't treat it as you would any other client machine text. Don't keep clear text passwords in memory.
访问时PasswordBox.Password
,只需将其取出并尽快发送到服务器。不要保留密码的值,也不要像对待任何其他客户端机器文本一样对待它。不要在内存中保留明文密码。
I know this breaks the MVVM pattern, but you shouldn't ever bind to PasswordBox.Password
Attached DP, store your password in the ViewModel or any other similar shenanigans.
我知道这打破了 MVVM 模式,但您永远不应该绑定到PasswordBox.Password
附加的 DP,将您的密码存储在 ViewModel 或任何其他类似的恶作剧中。
If you're looking for an over-architected solution, here's one:
1. Create the IHavePassword
interface with one method that returns the password clear text.
2. Have your UserControl
implement a IHavePassword
interface.
3. Register the UserControl
instance with your IoC as implementing the IHavePassword
interface.
4. When a server request requiring your password is taking place, call your IoC for the IHavePassword
implementation and only than get the much coveted password.
如果您正在寻找一种过度架构的解决方案,这里有一个:
1.IHavePassword
使用一种返回密码明文的方法创建接口。
2. 让你UserControl
实现一个IHavePassword
接口。
3. 向UserControl
您的 IoC注册实例以实现IHavePassword
接口。
4. 当服务器请求需要您的密码时,调用您的 IoC 进行IHavePassword
实施,然后才获得梦寐以求的密码。
Just my take on it.
只是我的看法。
-- Justin
——贾斯汀
回答by Thomas Levesque
As you can see i am binding to Password, but maybe its bind it to the static class..
正如你所看到的,我绑定到密码,但也许它绑定到静态类..
It is an attached property. This kind of property can be applied to any kind of DependencyObject
, not just the type in which it is declared. So even though it is declared in the PasswordHelper
static class, it is applied to the PasswordBox
on which you use it.
它是附加财产。这种属性可以应用于任何类型的DependencyObject
,而不仅仅是声明它的类型。因此,即使它是在PasswordHelper
静态类中声明的,它也会应用于PasswordBox
您使用它的对象。
To use this attached property, you just need to bind it to the Password
property in your ViewModel :
要使用此附加属性,您只需将其绑定到Password
ViewModel 中的属性:
<PasswordBox w:PasswordHelper.Attach="True"
w:PasswordHelper.Password="{Binding Password}"/>
回答by Rangel
you can do it with attached property, see it.. PasswordBox with MVVM
你可以用附加的属性来做,看看它..带 MVVM 的 PasswordBox
回答by jbe
You find a solution for the PasswordBox in the ViewModel sample application of the WPF Application Framework (WAF)project.
您可以在WPF 应用程序框架 (WAF)项目的 ViewModel 示例应用程序中找到 PasswordBox 的解决方案。
However, Justin is right. Don't pass the password as plain text between View and ViewModel. Use SecureString instead (See MSDN PasswordBox).
然而,贾斯汀是对的。不要在 View 和 ViewModel 之间以纯文本形式传递密码。请改用 SecureString(请参阅 MSDN PasswordBox)。
回答by Vladislav Borovikov
This works just fine for me.
这对我来说很好用。
<Button Command="{Binding Connect}"
CommandParameter="{Binding ElementName=MyPasswordBox}"/>
回答by Taylor Leese
I posted a GIST herethat is a bindable password box.
using System.Windows;
using System.Windows.Controls;
namespace CustomControl
{
public class BindablePasswordBox : Decorator
{
/// <summary>
/// The password dependency property.
/// </summary>
public static readonly DependencyProperty PasswordProperty;
private bool isPreventCallback;
private RoutedEventHandler savedCallback;
/// <summary>
/// Static constructor to initialize the dependency properties.
/// </summary>
static BindablePasswordBox()
{
PasswordProperty = DependencyProperty.Register(
"Password",
typeof(string),
typeof(BindablePasswordBox),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnPasswordPropertyChanged))
);
}
/// <summary>
/// Saves the password changed callback and sets the child element to the password box.
/// </summary>
public BindablePasswordBox()
{
savedCallback = HandlePasswordChanged;
PasswordBox passwordBox = new PasswordBox();
passwordBox.PasswordChanged += savedCallback;
Child = passwordBox;
}
/// <summary>
/// The password dependency property.
/// </summary>
public string Password
{
get { return GetValue(PasswordProperty) as string; }
set { SetValue(PasswordProperty, value); }
}
/// <summary>
/// Handles changes to the password dependency property.
/// </summary>
/// <param name="d">the dependency object</param>
/// <param name="eventArgs">the event args</param>
private static void OnPasswordPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs eventArgs)
{
BindablePasswordBox bindablePasswordBox = (BindablePasswordBox) d;
PasswordBox passwordBox = (PasswordBox) bindablePasswordBox.Child;
if (bindablePasswordBox.isPreventCallback)
{
return;
}
passwordBox.PasswordChanged -= bindablePasswordBox.savedCallback;
passwordBox.Password = (eventArgs.NewValue != null) ? eventArgs.NewValue.ToString() : "";
passwordBox.PasswordChanged += bindablePasswordBox.savedCallback;
}
/// <summary>
/// Handles the password changed event.
/// </summary>
/// <param name="sender">the sender</param>
/// <param name="eventArgs">the event args</param>
private void HandlePasswordChanged(object sender, RoutedEventArgs eventArgs)
{
PasswordBox passwordBox = (PasswordBox) sender;
isPreventCallback = true;
Password = passwordBox.Password;
isPreventCallback = false;
}
}
}
回答by Konamiman
My 2 cents:
我的 2 美分:
I developed once a typical login dialog (user and password boxes, plus "Ok" button) using WPF and MVVM. I solved the password binding issue by simply passing the PasswordBox control itself as a parameter to the command attached to the "Ok" button. So in the view I had:
我曾经使用 WPF 和 MVVM 开发了一个典型的登录对话框(用户和密码框,加上“确定”按钮)。我通过简单地将 PasswordBox 控件本身作为参数传递给附加到“确定”按钮的命令来解决密码绑定问题。所以在我看来:
<PasswordBox Name="txtPassword" VerticalAlignment="Top" Width="120" />
<Button Content="Ok" Command="{Binding Path=OkCommand}"
CommandParameter="{Binding ElementName=txtPassword}"/>
And in the ViewModel, the Execute
method of the attached command was as follows:
而在ViewModel中,Execute
附加命令的方法如下:
void Execute(object parameter)
{
var passwordBox = parameter as PasswordBox;
var password = passwordBox.Password;
//Now go ahead and check the user name and password
}
This slightly violates the MVVM pattern since now the ViewModel knows something about how the View is implemented, but in that particular project I could afford it. Hope it is useful for someone as well.
这稍微违反了 MVVM 模式,因为现在 ViewModel 知道一些关于 View 是如何实现的,但在那个特定的项目中我可以负担得起。希望它对某人也有用。
回答by Legz
I used this method and passed the password box, although this does violate the MVVM it was essential for me because I was using a content control with data template for my login within my shell which is a complex shell enviroment. So accessing the code behind of the shell would have been crap.
我使用了这种方法并传递了密码框,尽管这确实违反了 MVVM,但这对我来说是必不可少的,因为我在 shell 中使用了带有数据模板的内容控件进行登录,这是一个复杂的 shell 环境。因此,访问 shell 背后的代码将是废话。
Passing the passwordbox I would think is same as accessing control from code behind as far as I know. I agree passwords, dont keep in memory etc In this implementation I don't have property for password in view model.
据我所知,传递密码框与从后面的代码访问控制相同。我同意密码,不保留在内存中等在这个实现中,我没有视图模型中的密码属性。
Button Command
按钮命令
Command="{Binding Path=DataContext.LoginCommand, ElementName=MyShell}" CommandParameter="{Binding ElementName=PasswordBox}"
ViewModel
视图模型
private void Login(object parameter)
{
System.Windows.Controls.PasswordBox p = (System.Windows.Controls.PasswordBox)parameter;
MessageBox.Show(p.Password);
}
回答by William Rawson
This implementation is slightly different. You pass a passwordbox to the View thru binding of a property in ViewModel, it doesn't use any command params. The ViewModel Stays Ignorant of the View. I have a VB vs 2010 Project that can be downloaded from SkyDrive. Wpf MvvM PassWordBox Example.zip https://skydrive.live.com/redir.aspx?cid=e95997d33a9f8d73&resid=E95997D33A9F8D73!511
这个实现略有不同。您通过 ViewModel 中的属性绑定将密码框传递给 View,它不使用任何命令参数。ViewModel 保持对视图的无知。我有一个可以从 SkyDrive 下载的 VB vs 2010 项目。Wpf MvvM PassWordBox Example.zip https://skydrive.live.com/redir.aspx?cid=e95997d33a9f8d73&resid=E95997D33A9F8D73!511
The way that I am Using PasswordBox in a Wpf MvvM Application is pretty simplistic and works well for Me. That does not mean that I think it is the correct way or the best way. It is just an implementation of Using PasswordBox and the MvvM Pattern.
我在 Wpf MvvM 应用程序中使用 PasswordBox 的方式非常简单,对我来说效果很好。这并不意味着我认为这是正确的方法或最好的方法。它只是 Using PasswordBox 和 MvvM Pattern 的一个实现。
Basicly You create a public readonly property that the View can bind to as a PasswordBox (The actual control) Example:
基本上,您创建一个公共只读属性,视图可以将其绑定为 PasswordBox(实际控件)示例:
Private _thePassWordBox As PasswordBox
Public ReadOnly Property ThePassWordBox As PasswordBox
Get
If IsNothing(_thePassWordBox) Then _thePassWordBox = New PasswordBox
Return _thePassWordBox
End Get
End Property
I use a backing field just to do the self Initialization of the property.
我使用后备字段只是为了进行属性的自初始化。
Then From Xaml you bind the Content of a ContentControl or a Control Container Example:
然后从 Xaml 绑定 ContentControl 或控件容器示例的内容:
<ContentControl Grid.Column="1" Grid.Row="1" Height="23" Width="120" Content="{Binding Path=ThePassWordBox}" HorizontalAlignment="Center" VerticalAlignment="Center" />
From there you have full control of the passwordbox I also use a PasswordAccessor (Just a Function of String) to return the Password Value when doing login or whatever else you want the Password for. In the Example I have a public property in a Generic User Object Model. Example:
从那里你可以完全控制密码框,我还使用 PasswordAccessor(只是一个字符串函数)在登录或其他任何你想要密码的时候返回密码值。在示例中,我在通用用户对象模型中有一个公共属性。例子:
Public Property PasswordAccessor() As Func(Of String)
In the User Object the password string property is readonly without any backing store it just returns the Password from the PasswordBox. Example:
在用户对象中,密码字符串属性是只读的,没有任何后备存储,它只从 PasswordBox 返回密码。例子:
Public ReadOnly Property PassWord As String
Get
Return If((PasswordAccessor Is Nothing), String.Empty, PasswordAccessor.Invoke())
End Get
End Property
Then in the ViewModel I make sure that the Accessor is created and set to the PasswordBox.Password property' Example:
然后在 ViewModel 中,我确保创建了访问器并将其设置为 PasswordBox.Password 属性'示例:
Public Sub New()
'Sets the Accessor for the Password Property
SetPasswordAccessor(Function() ThePassWordBox.Password)
End Sub
Friend Sub SetPasswordAccessor(ByVal accessor As Func(Of String))
If Not IsNothing(VMUser) Then VMUser.PasswordAccessor = accessor
End Sub
When I need the Password string say for login I just get the User Objects Password property that really invokes the Function to grab the password and return it, then the actual password is not stored by the User Object. Example: would be in the ViewModel
当我需要密码字符串表示登录时,我只获取真正调用函数以获取密码并返回它的用户对象密码属性,然后用户对象不存储实际密码。示例:将在 ViewModel 中
Private Function LogIn() as Boolean
'Make call to your Authentication methods and or functions. I usally place that code in the Model
Return AuthenticationManager.Login(New UserIdentity(User.UserName, User.Password)
End Function
That should Do It. The ViewModel doesn't need any knowledge of the View's Controls. The View Just binds to property in the ViewModel, not any different than the View Binding to an Image or Other Resource. In this case that resource(Property) just happens to be a usercontrol. It allows for testing as the ViewModel creates and owns the Property and the Property is independent of the View. As for Security I don't know how good this implementation is. But by using a Function the Value is not stored in the Property itself just accessed by the Property.
那应该这样做。ViewModel 不需要任何关于视图控件的知识。View 只是绑定到 ViewModel 中的属性,与绑定到图像或其他资源的视图没有任何不同。在这种情况下,资源(属性)恰好是一个用户控件。它允许进行测试,因为 ViewModel 创建并拥有属性,并且属性独立于视图。至于安全性,我不知道这个实现有多好。但是通过使用函数,值不会存储在属性本身中,而属性本身只是被属性访问。
回答by Jan Willem B
A simple solution without violating the MVVM pattern is to introduce an event (or delegate) in the ViewModel that harvests the password.
一个不违反 MVVM 模式的简单解决方案是在 ViewModel 中引入一个事件(或委托)来获取密码。
In the ViewModel:
在视图模型中:
public event EventHandler<HarvestPasswordEventArgs> HarvestPassword;
public event EventHandler<HarvestPasswordEventArgs> HarvestPassword;
with these EventArgs:
使用这些 EventArgs:
class HarvestPasswordEventArgs : EventArgs
{
public string Password;
}
in the View, subscribe to the event on creating the ViewModel and fill in the password value.
在View 中,订阅创建 ViewModel 的事件并填写密码值。
_viewModel.HarvestPassword += (sender, args) =>
args.Password = passwordBox1.Password;
In the ViewModel, when you need the password, you can fire the event and harvest the password from there:
在ViewModel 中,当您需要密码时,您可以触发事件并从那里获取密码:
if (HarvestPassword == null)
//bah
return;
var pwargs = new HarvestPasswordEventArgs();
HarvestPassword(this, pwargs);
LoginHelpers.Login(Username, pwargs.Password);