初学WPF,网上资料都说用MVVM模式开发WPF程序是最好的,就顺带了解了下MVVM,不过实践的时候遇到个小问题:ViewModel中要弹出提示时该如何处理,是在ViewModel中写弹出框还是怎么滴?登录成功后,关闭登录窗口打开主窗口,这样的逻辑该写到哪,View还是ViewModel中控制?
在我的理解中,ViewModel中是不知道具体的View的,所以在ViewModel中去关闭View或者打开某个View好像不是很好。于是想到在ViewModel中定义事件,在View中对ViewModel中的事件进行处理。可是如果要弹出询问框,又该如何处理?望园里的大神门指点12。
自定义登录控件:

<UserControl x:Class="LoginDemo.Views.LoginControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:v="clr-namespace:LoginDemo.Views" mc:Ignorable="d" d:DesignHeight="160" d:DesignWidth="341"> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="80"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Label Name="lblAccount" Content="账号" Style="{StaticResource TipsLabel}" Grid.Row="0" Grid.Column="0"></Label> <Label Name="lblPassword" Content="密码" Style="{StaticResource TipsLabel}" Grid.Row="1" Grid.Column="0"></Label> <TextBox Name="txtAccount" Style="{StaticResource InputBox}" Grid.Row="0" Grid.Column="1" Text="{Binding LoginID}"></TextBox> <PasswordBox Name="txtPassword" Style="{StaticResource InputBox}" Grid.Row="1" Grid.Column="1" v:PasswordBoxHelper.Attach="True" v:PasswordBoxHelper.Password="{Binding Password, Mode=TwoWay}"></PasswordBox> <StackPanel Grid.Row="2" Grid.Column="1" Style="{StaticResource CommandPanel}"> <Button Name="btnLogin" Content="登录" Command="{Binding Login}" Style="{StaticResource LoginCommand}"></Button> <Button Name="btnClose" Content="退出" Command="Close" Style="{StaticResource CloseCommand}"></Button> </StackPanel> </Grid> </UserControl>
由于PasswordBox.Password是不支持Binding的,所以在网上找了个辅助绑定的代码,这里就不贴出来了。

public interface ILoginViewModel { string LoginID { get; set; } string Password { get; set; } ICommand Login { get; } }

public class LoginViewModel : ObservableObject, ILoginViewModel { private LoginModel loginModel = new LoginModel(); private ICommand _LoginCommand; public event EventHandler LoginSuccess; public event TipsEventHandler LoginFailed; protected virtual void OnLoginSuccess() { var handler = this.LoginSuccess; if (handler != null) handler(this, EventArgs.Empty); } protected virtual void OnLoginFailed(string tips) { var handler = this.LoginFailed; if (handler != null) handler(this, new TipsEventArgs(tips)); } public string LoginID { get { return this.loginModel.LoginID; } set { this.loginModel.LoginID = value; RaisePropertyChanged(() => LoginID); } } public string Password { get { return this.loginModel.Password; } set { this.loginModel.Password = value; RaisePropertyChanged(() => Password); } } public ICommand Login { get { if (this._LoginCommand == null) { this._LoginCommand = new CommandProxy(LoginAction); } return this._LoginCommand; } } private void LoginAction(object parameter) { if (this.LoginID != "admin" || this.Password != "123") { OnLoginFailed("用户名或密码错误!\n正确的用户名是admin\n正确的密码是123"); return; } OnLoginSuccess(); } }
登录界面:

<Window x:Class="LoginDemo.Views.Login" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:LoginDemo.Views" xmlns:vm="clr-namespace:LoginDemo.ViewModels" Title="登录" Height="303" Width="470"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="50"/> <RowDefinition/> <RowDefinition Height="50"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="20"/> <ColumnDefinition/> <ColumnDefinition Width="30"/> </Grid.ColumnDefinitions> <c:LoginControl x:Name="loginControl" Grid.Row="1" Grid.Column="1"> <c:LoginControl.DataContext> <vm:LoginViewModel LoginID="admin1" Password="123" LoginSuccess="LoginViewModel_LoginSuccess" LoginFailed="LoginViewModel_LoginFailed"/> </c:LoginControl.DataContext> </c:LoginControl> </Grid> </Window>

/// <summary> /// Login.xaml 的交互逻辑 /// </summary> public partial class Login : Window { public Login() { InitializeComponent(); // 如果不添加这行代码,则退出按钮是不可用的,因为内置的Close命令是没有实现的,要自己实现 this.CommandBindings.Add(new CloseCommandBindingProxy(this)); } private void LoginViewModel_LoginSuccess(object sender, EventArgs e) { MainWindow mainWindow = new MainWindow(); this.Close(); mainWindow.ShowDialog(); } private void LoginViewModel_LoginFailed(object sender, ViewModels.TipsEventArgs e) { MessageBox.Show(e.Tips, "提示"); } }
登录失败、登录成功的处理不知道这样行不行,现在就纠结在View与ViewModel的这种交互上。