问题描述
<Grid>
<Grid.RowDeFinitions>
<RowDeFinition Height="auto"/>
<RowDeFinition Height="auto"/>
<RowDeFinition Height="*"/>
</Grid.RowDeFinitions>
<Button Content="Login Page"
Command="{Binding UpdateCurrentviewmodelCommand}"
CommandParameter="LoginView"/>
<Button Content="Register Page"
Command="{Binding UpdateCurrentviewmodelCommand}"
CommandParameter="RegisterView"
Grid.Row="1"/>
<ContentControl Content="{Binding CurrentView}" Grid.Row="2"/>
</Grid>
public class Mainviewmodel : Baseviewmodel
{
private Baseviewmodel _currentView = new Loginviewmodel();
public Baseviewmodel CurrentView
{
get
{
return _currentView;
}
set
{
_currentView = value;
OnPropertyChanged(nameof(CurrentView));
}
}
public ICommand UpdateCurrentviewmodelCommand { get; set; }
public Mainviewmodel()
{
UpdateCurrentviewmodelCommand = new RelayCommand(UpdateCurrentView);
}
private void UpdateCurrentView(object obj)
{
if (obj.ToString() == "LoginView")
{
CurrentView = new Loginviewmodel();
}
else if (obj.ToString() == "RegisterView")
{
CurrentView = new Registerviewmodel();
}
else if (obj.ToString() == "DashboardView")
{
CurrentView = new Dashboardviewmodel();
}
}
}
这里,当用户登录时,它应该更新当前视图,它正在执行命令,我也正在获取命令参数中的值,并且还更新了Mainviewmodel中的属性CurrentView
,但是问题是,不是正在更新视图未显示的UI ...
public class Loginviewmodel : Baseviewmodel
{
private string _email;
public string Email
{
get
{
return _email;
}
set
{
_email = value;
OnPropertyChanged(nameof(Email));
}
}
private string _password;
public string Password
{
get
{
return _password;
}
set
{
_password = value;
OnPropertyChanged(nameof(Password));
}
}
public ICommand LoginCommand { get; set; }
private StringBuilder ErrorMessages { get; set; } = new StringBuilder();
public Loginviewmodel()
{
LoginCommand = new RelayCommandAsync(async (para) => await LoginUser(para));
}
private async Task LoginUser(object para)
{
sqlConnector sqlConnector = new sqlConnector();
if (ValidateForm() == false)
{
MessageBox.Show(ErrorMessages.ToString());
return;
}
User user = await sqlConnector.FindUserByEmail(Email);
if (user == null)
{
MessageBox.Show("Incorrect username or password");
return;
}
IPasswordHasher passwordHasher = new PasswordHasher();
var passwordResult = passwordHasher.VerifyHashedPassword(user.PasswordHash,Password);
if (passwordResult == PasswordVerificationResult.Success)
{
MessageBox.Show("Login success.");
//here is the problem...I am telling my Mainviewmodel's CurrentView property to update
but it's not listening to me.
//new Mainviewmodel().UpdateCurrentviewmodelCommand.Execute("DashboardView");
new Mainviewmodel().CurrentView = new Dashboardviewmodel();
}
else
{
MessageBox.Show("Incorrect username or password");
}
ClearProperties();
}
private bool ValidateForm()
{
ErrorMessages.Clear();
bool isValid = true;
if (string.IsNullOrWhiteSpace(Email))
{
isValid = false;
ErrorMessages.Append("Email cannot be empty\n");
}
if (string.IsNullOrWhiteSpace(Password))
{
isValid = false;
ErrorMessages.Append("Password cannot be empty\n");
}
return isValid;
}
private void ClearProperties()
{
Email = Password = null;
}
解决方法
这不起作用,因为成功登录后您正在创建MainViewModel
的新实例。这不是您的视图DataContext
的实例。
您可以例如通过构造函数将对MainViewModel
实例的引用传递给LoginViewModel
。但是由于LoginViewModel
并不真正依赖MainViewModel
,所以我不会这样做。
相反,我建议以下两种解决方案之一。通常,您的页面视图模型不应在乎内容导航。这应该仅是父级导航视图模型的责任,该模型已经知道导航详细信息,例如当前页面或下一页等。两个示例都遵循此想法。
还要注意,由于每次用户导航时都要创建新的页面视图模型,因此您将丢失所有内容。返回上一页将显示空白的初始页面。同样,switch
在可扩展性方面也很糟糕。添加新页面不是很好,并且会破坏您的UpdateCurrentView
方法。
我重构了您的代码,以显示一种简单的方法来保持页面导航的简单性和可扩展性。这只是很小的变化:添加enum
替换字符串以启用Intellisense和编译时类型检查支持,并添加Dictionary
替换switch
:
// Enum used to identify the requested page e.g as CommandParameter
public enum PageId
{
None = 0,LoginView,RegisterView,DashboardView
}
// Use a Dictionary to navigate to content based on the PageId enum
public class MainViewModel : BaseViewModel
{
private Dictionary<PageId,BaseViewModel> Pages { get; set; }
public MainViewModel()
{
this.Pages = new Dictionary<PageId,BaseViewModel>
{
{ PageId.LoginView,new LoginViewModel() },{ PageId.RegisterView,new RegisterViewModel() },{ PageId.DashboardView,new DashboardViewModel() }
};
}
private void UpdateCurrentView(object commandParameter)
{
if (commandParameter is PageId pageId
&& this.Pages.TryGetValue(pageId,out BaseViewModel nextPage))
{
this.CurrentView = nextPage;
}
}
}
<!-- Modified view to use the enum -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Content="Login Page"
Command="{Binding UpdateCurrentViewModelCommand}"
CommandParameter="{x:Static PageId.LoginView}"/>
<Button Content="Register Page"
Command="{Binding UpdateCurrentViewModelCommand}"
CommandParameter="{x:Static PageId.RegisterView}"
Grid.Row="1"/>
<ContentControl Content="{Binding CurrentView}" Grid.Row="2"/>
</Grid>
解决方案1:LoginSuccessful
事件
您可以公开一个LoginSuccessful
事件,导航视图模型可以侦听此事件:
MainViewModel.cs
public class MainViewModel : BaseViewModel
{
private Dictionary<PageId,BaseViewModel>
{
{ PageId.RegisterView,new DashboardViewModel() }
};
var loginView = new LoginViewModel();
loginView.LoginSuccessful += OnLoginSuccessful;
this.Pages.Add(PageId.LoginView,loginView);
}
private void UpdateCurrentView(object commandParameter)
{
if (commandParameter is PageId pageId
&& this.Pages.TryGetValue(pageId,out BaseViewModel nextPage))
{
this.CurrentView = nextPage;
}
}
private void OnLoginSuccessful(object sender,EventArgs e)
{
var loginViewModel = sender as LoginViewModel;
loginViewModel.LoginSuccessful -= OnLoginSuccessful;
UpdateCurrentView(PageId.LoginView);
}
}
LoginViewModel.cs
public class LoginViewModel : BaseViewModel
{
public event EventHandler LoginSuccessful;
private void OnLoginSuccessful() => this.LoginSuccessful?.Invoke(this,EventArgs.Empty);
private async Task LoginUser(object para)
{
SqlConnector sqlConnector = new SqlConnector();
if (ValidateForm() == false)
{
MessageBox.Show(ErrorMessages.ToString());
return;
}
User user = await sqlConnector.FindUserByEmail(Email);
if (user == null)
{
MessageBox.Show("Incorrect username or password");
return;
}
IPasswordHasher passwordHasher = new PasswordHasher();
var passwordResult = passwordHasher.VerifyHashedPassword(user.PasswordHash,Password);
if (passwordResult == PasswordVerificationResult.Success)
{
MessageBox.Show("Login success.");
OnLoginSuccessful();
}
else
{
MessageBox.Show("Incorrect username or password");
}
ClearProperties();
}
}
解决方案2:继续回调
或强制导航视图模式通过构造函数提供延续回调:
MainViewModel.cs
public class MainViewModel : BaseViewModel
{
private Dictionary<PageId,new LoginViewModel(() => UpdateCurrentView(PageId.LoginView)) },out BaseViewModel nextPage))
{
this.CurrentView = nextPage;
}
}
}
LoginViewModel.cs
public class LoginViewModel : BaseViewModel
{
public Action LoginSuccessfulContinuation { get; set; }
// Constructor
public LoginViewModel(Action loginSuccessfulContinuation) => this.LoginSuccessfulContinuation = loginSuccessfulContinuation;
private async Task LoginUser(object para)
{
SqlConnector sqlConnector = new SqlConnector();
if (ValidateForm() == false)
{
MessageBox.Show(ErrorMessages.ToString());
return;
}
User user = await sqlConnector.FindUserByEmail(Email);
if (user == null)
{
MessageBox.Show("Incorrect username or password");
return;
}
IPasswordHasher passwordHasher = new PasswordHasher();
var passwordResult = passwordHasher.VerifyHashedPassword(user.PasswordHash,Password);
if (passwordResult == PasswordVerificationResult.Success)
{
MessageBox.Show("Login success.");
this.LoginSuccessfulContinuation?.Invoke();
}
else
{
MessageBox.Show("Incorrect username or password");
}
ClearProperties();
}
}