我是MVVM的新手(使用WPF的一些经验),我对我认为的基础知识感到非常困惑。我试图制作一个简单的注册表格。用户输入他们的名字,用户名和密码。为了学习MVVM而不是过于复杂化,我对密码的唯一检查是它是否包含大写字母。暂时没有散列,加密等。
所以我有一个从实体框架生成的User
模型。这是我的第一点困惑。它看起来像这样:
public partial class User : INotifyPropertyChanged
{
public short Id { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public string Password { get; set; }
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName == value)
{
return;
}
_firstName = value;
OnPropertyChanged("FirstName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
正如您所看到的,它有点不匹配。第一部分是由Entity生成的,我对FirstName进行了修改,看起来就像我认为的MVVM更多。 Model是我创建get / set的位置,它们是否也需要在ViewModel中?
转到我的ViewModel,我很困惑我是否需要再次重新创建User
(FirstName,LastName ....)的属性,或者是否可以通过Model访问它们。我可以在ViewModel中创建User
来公开User
的所有属性吗?以下是我ViewModel
到目前为止的代码:
internal class NewUserViewModel : BaseViewModel
{
private User _newUser;
public User NewUser
{
get => _newUser;
set
{
if (_newUser == value)
{
return;
}
_newUser = value;
OnPropertyChanged("NewUser");
}
}
private string _password;
public string Password
{
get => _password;
set
{
if (_password == value)
return;
_password = value;
OnPasswordChanged();
OnPropertyChanged("Password");
}
}
#region RegisterCommand
private DelegateCommand _registerCommand;
public ICommand RegisterCommand
{
get
{
_registerCommand = new DelegateCommand(param => Register(), param => CanRegister());
return _registerCommand;
}
}
private bool CanRegister()
{
return _isPasswordValid;
}
private bool _isPasswordValid;
public void OnPasswordChanged()
{
_isPasswordValid = Password.Any(char.IsUpper);
}
private void Register()
{
using (var context = new WorkstreamContext())
{
var users = context.Set<User>();
users.Add(_newUser);
context.SaveChanges();
}
}
#endregion
}
到目前为止,我已经重新创建了Password
属性,因此我可以访问它,但这对我来说不安,我觉得我要么暴露所有属性还是使用NewUser
,但我不确定关于如何做到这一点。目前代码一半有效。 Save
按钮显示为灰色,但是当密码包含大写字母时,它不会启用,这是我所期望的。实际注册表格:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image Source="pack://application:,,,/Resources/NewUserForm/NewUser.jpg" HorizontalAlignment="Center" VerticalAlignment="Center" Height="100"/>
<TextBlock Grid.Row="1" Text="Please Enter Your Details" HorizontalAlignment="Center" Foreground="DarkSlateGray" FontSize="16"/>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Height="{Binding ActualHeight, ElementName=FirstNameTextBox}" Source="pack://application:,,,/Resources/NewUserForm/FirstName.jpg" Margin="5,5,0,5"/>
<xctk:WatermarkTextBox Grid.Column="1" x:Name="FirstNameTextBox" Watermark="first name" Text="{Binding NewUser.FirstName, Mode=TwoWay}" />
</Grid>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Height="{Binding ActualHeight, ElementName=LastNameTextBox}" Source="pack://application:,,,/Resources/NewUserForm/LastName.jpg" Margin="5,5,0,5"/>
<xctk:WatermarkTextBox Grid.Column="1" x:Name="LastNameTextBox" Watermark="last name" Text="{Binding NewUser.LastName, Mode=TwoWay}" />
</Grid>
<Grid Grid.Row="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Height="{Binding ActualHeight, ElementName=UsernameTextBox}" Source="pack://application:,,,/Resources/NewUserForm/User.jpg" Margin="5,5,0,5"/>
<xctk:WatermarkTextBox Grid.Column="1" x:Name="UsernameTextBox" Watermark="username" Text="{Binding NewUser.Username, Mode=TwoWay}" />
</Grid>
<Grid Grid.Row="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Height="{Binding ActualHeight, ElementName=PasswordTextBox}" Source="pack://application:,,,/Resources/NewUserForm/Password.jpg" Margin="5,5,0,5"/>
<xctk:WatermarkTextBox Grid.Column="1" x:Name="PasswordTextBox" Watermark="password" Text="{Binding NewUser.Password, Mode=TwoWay}"/>
</Grid>
<DockPanel Grid.Row="6">
<Button Content="Save" Command="{Binding RegisterCommand, Mode=OneWay, Source={StaticResource NewUserViewModel}}" HorizontalAlignment="Right"/>
<Button HorizontalAlignment="Right"/>
</DockPanel>
</Grid>
我将TextBoxes绑定到ViewModel上暴露的NewUser的方式是在MVVM中运行的正确方法吗?我很欣赏MVVM上有很多教程,我已阅读/浏览了很多。然而,我正处于一个让我感到越来越困惑的阶段,并且非常感谢有人会给我一个关于我的代码的细分和关于我哪里出错的指针,为什么代码不起作用以及我可以在哪里提高。
答案 0 :(得分:1)
你的问题很广泛。但是如果User
类实现了INotifyPropertyChanged
接口,它实际上是一种视图模型,你可以像你一样直接绑定到这个属性:
{Binding NewUser.FirstName}
如果NewUser
是某种DTO对象,您可以将其包装在视图模型中并绑定到视图模型属性:
public string Password
{
get { return _user.Password; }
set { return _user.Password = value; OnNotifyPropertyChanged(); }
}
真正的&#34;模型&#34;是一种服务或某种业务逻辑对象。
“保存”按钮显示为灰色,但是当密码包含大写字母时,它不会启用,这是我所期望的。
你的Password
财产的制定者是否会被击中?绑定到视图模型的Password
属性 :
Text="{Binding Password}"
...并调用命令的RaiseCanExecuteChanged()
来刷新其状态:
private string _password;
public string Password
{
get => _password;
set
{
if (_password == value)
return;
_password = value;
OnPasswordChanged();
OnPropertyChanged("Password");
_registerCommand.RaiseCanExecuteChanged(); //<--
}
}