初學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的這種交互上。