I have next xaml code.
<StackPanel Grid.Row="1" x:Name="spLogin">
<TextBlock Text="E-mail:"></TextBlock>
<TextBox Name="tbLogin" Text="{Binding User.Email, Mode=TwoWay}"></TextBox>
<TextBlock Text="Password:"></TextBlock>
<TextBox Name="tbPassword" Text="{Binding User.Password, Mode=TwoWay}"></TextBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Name="btnLogin" Content="Login">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="Click"
x:Name开发者_如何学编程="SelectChangeEvent">
<Command:EventToCommand
Command="{Binding Login, Mode=TwoWay}" PassEventArgsToCommand="False" CommandParameter="{Binding}" />
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</Button>
<Button Name="btnClear" Content="Clear"/>
</StackPanel>
</StackPanel>
</Grid>
How I can pass tbLogin.Text and tbPassword.Text to ViewModel like as a field of one object User
public class User
{
public string Login{get;set;}
public string Password(get;set;)
}
Normally I would just say ... you don't need to, as the your ViewModel (deduced from your bindings) should contain both, a User
property, and the Login
command iself, so that you just can access the User
property from your command's Excecute
method.
However, I noticed that the user class your presented user class does not implement INotifyPropertyChanged
. Therefore, the binding will not work correctly. To make it work you have two possibilities:
- Implement
INotifyPropertyChanged
on your user class. -- Depending on your model and how it is generated this is not allways possible. - Duplicate the properties on your ViewModel and implementing
INotifyPropertyChanged
there. -- If you don't have control over the definition of you model (e.g. as it is generated by a web service proxy) this is the only way of doing it. But, even if you have control over your model this option is worth considering as gives your greater control over what is passed to your View.
So, the following sample assumes yoou go for the second option:
public class MvvmViewModel1 : ViewModelBase
{
private User _user;
#region [Login]
public const string LoginPropertyName = "Login";
public string Login {
get {
return this._user.Login;
}
set {
if (this._user.Login == value) {
return;
}
var oldValue = this._user.Login;
this._user.Login = value;
RaisePropertyChanged(LoginPropertyName);
}
}
#endregion
#region [Password]
public const string PasswordPropertyName = "Password";
public string Password {
get {
return this._user.Password;
}
set {
if (this._user.Password == value) {
return;
}
var oldValue = this._user.Password;
this._user.Password = value;
RaisePropertyChanged(PasswordPropertyName);
}
}
#endregion
#region [LoginCommand]
public RelayCommand _loginCommand;
public RelayCommand LoginCommand {
get {
return _loginCommand ?? (_loginCommand = new RelayCommand(
() => {
// perform your login action here
// access Login with: this.Login
// access Password with: this.Password
},
() => {
// can execute method - sample implementation
return (!string.IsNullOrEmpty(this.Login) && !string.IsNullOrEmpty(this.Password));
}
));
}
}
#endregion
}
Obviously you will have to modify your XAML to match this ViewModel:
<StackPanel Grid.Row="1" x:Name="spLogin">
<TextBlock Text="E-mail:"></TextBlock>
<TextBox Name="tbLogin" Text="{Binding Login, Mode=TwoWay}"></TextBox>
<TextBlock Text="Password:"></TextBlock>
<TextBox Name="tbPassword" Text="{Binding Password, Mode=TwoWay}"></TextBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Name="btnLogin" Content="Login">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="Click"
x:Name="SelectChangeEvent">
<Command:EventToCommand
Command="{Binding LoginCommand}" PassEventArgsToCommand="False" />
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</Button>
<Button Name="btnClear" Content="Clear"/>
</StackPanel>
</StackPanel>
Normally you only need to use CommandParameter
if you are inside a data template (e.g. an item template inside a ListBox) and/or your command and your properties are not in the same ViewModel. In this case you have to define your command to accept a parameter:
#region [LoginCommand]
public RelayCommand _loginCommand;
public RelayCommand LoginCommand {
get {
return _loginCommand ?? (_loginCommand = new RelayCommand(
(p) => {
// perform your login action here
},
(p) => {
// can execute method - sample implementation
return (!string.IsNullOrEmpty(this.Login) && !string.IsNullOrEmpty(this.Password));
}
));
}
}
#endregion
And now you can use
CommandParameter="{Binding}"
inside your XAML to pass in the data context of the data template to your ViewModel.
And another side note: You normally do not need two way binding - except in the case when you want to write information back from your View to your ViewModel.
Also as a side note: You cannot generate/convert classes in XAML; so if you have a User class with Email/Password properties there in your ViewModel there is no way of creating a new user class with Login/Password properties out of thin air in XAML and pass that to your ViewModel. Binding is powerful but not allmighty ... ;-)
Since your tbLogin.Text and tbPassword.Text two-way bound to User.Email and User.Password correspondently you can simply bind CommandParameter to the User. Bigger question is: why do you need CommandParameter at all then your view model can access User property directly?
精彩评论