我正在尝试学习WPF和MVVM问题,但是遇到了障碍.这个问题与此问题相似但不完全相同(处理对话框-wpf-with-mvvm) ...
我有一个使用MVVM模式编写的"登录"表单.
此表单有一个ViewModel,它包含用户名和密码,这些用户名和密码使用普通数据绑定绑定到XAML中的视图.它还有一个"登录"命令,该命令绑定到表单上的"登录"按钮,使用正常数据绑定.
当"登录"命令触发时,它会调用ViewModel中的一个函数,该函数将关闭并通过网络发送数据以进行登录.当此函数完成时,有2个操作:
登录无效 - 我们只显示一个MessageBox,一切都很好
登录有效,我们需要关闭登录表单并让它返回true作为其DialogResult...
问题是,ViewModel对实际视图一无所知,那么如何关闭视图并告诉它返回一个特定的DialogResult?我可以在CodeBehind中粘贴一些代码,和/或将View传递给ViewModel,但这似乎完全打败了MVVM的全部内容......
最后,我刚刚违反了MVVM模式的"纯度",并让View发布了一个Closed事件,并公开了一个Close方法.ViewModel然后才会调用view.Close.该视图仅通过接口已知并通过IOC容器连接,因此不会丢失可测试性或可维护性.
接受的答案是-5票,这似乎很愚蠢!虽然我很清楚通过在"纯粹"时解决问题所获得的良好感受,当然我不是唯一一个认为200行事件,命令和行为只是为了避免单行方法"模式"和"纯度"的名称有点荒谬......
Joe*_*ite 314
我被Thejuan的答案所启发,写了一个更简单的附属物.没有风格,没有触发器; 相反,你可以这样做:
<Window ...
xmlns:xc="clr-namespace:ExCastle.Wpf"
xc:DialogCloser.DialogResult="{Binding DialogResult}">
Run Code Online (Sandbox Code Playgroud)
这几乎就像WPF团队做得对,并且首先使DialogResult成为依赖属性一样干净.只需bool? DialogResult在ViewModel上放置一个属性并实现INotifyPropertyChanged即可,您的ViewModel可以通过设置属性来关闭Window(并设置其DialogResult).应该是MVVM.
这是DialogCloser的代码:
using System.Windows;
namespace ExCastle.Wpf
{
public static class DialogCloser
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached(
"DialogResult",
typeof(bool?),
typeof(DialogCloser),
new PropertyMetadata(DialogResultChanged));
private static void DialogResultChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null)
window.DialogResult = e.NewValue as bool?;
}
public static void SetDialogResult(Window target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我也在我的博客上发布了这个.
Bud*_*dda 64
从我的角度来看,问题非常好,因为同样的方法不仅用于"登录"窗口,而且用于任何类型的窗口.我已经回顾了很多建议,没有一个对我来说没问题.请查看我从MVVM设计模式文章中获取的建议.
每个ViewModel类都应该继承WorkspaceViewModel具有该类型的RequestClose事件和CloseCommand属性的ICommand类.该CloseCommand属性的默认实现将引发该RequestClose事件.
为了关闭OnLoaded窗口,应该覆盖窗口的方法:
void CustomerWindow_Loaded(object sender, RoutedEventArgs e)
{
CustomerViewModel customer = CustomerViewModel.GetYourCustomer();
DataContext = customer;
customer.RequestClose += () => { Close(); };
}
Run Code Online (Sandbox Code Playgroud)
或OnStartup你的app方法:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
var viewModel = new MainWindowViewModel();
viewModel.RequestClose += window.Close;
window.DataContext = viewModel;
window.Show();
}
Run Code Online (Sandbox Code Playgroud)
我猜这个RequestClose事件和CloseCommand属性实现WorkspaceViewModel非常清楚,但我会证明它们是一致的:
public abstract class WorkspaceViewModel : ViewModelBase
// There's nothing interesting in ViewModelBase as it only implements the INotifyPropertyChanged interface
{
RelayCommand _closeCommand;
public ICommand CloseCommand
{
get
{
if (_closeCommand == null)
{
_closeCommand = new RelayCommand(
param => Close(),
param => CanClose()
);
}
return _closeCommand;
}
}
public event Action RequestClose;
public virtual void Close()
{
if ( RequestClose != null )
{
RequestClose();
}
}
public virtual bool CanClose()
{
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
和源代码RelayCommand:
public class RelayCommand : ICommand
{
#region Constructors
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
}
Run Code Online (Sandbox Code Playgroud)
PS不要因为这些来源对我不好!如果我昨天有他们,那将节省我几个小时......
PPS欢迎任何意见或建议.
Ada*_*lls 18
我使用附加行为来关闭窗口.将ViewModel上的"signal"属性绑定到附加行为(我实际使用触发器)当它设置为true时,该行为将关闭窗口.
http://adammills.wordpress.com/2009/07/01/window-close-from-xaml/
Eig*_*ite 15
有很多评论在这里争论MVVM的优点和缺点.对我来说,我同意Nir; 这是一个适当使用模式的问题,MVVM并不总是适合.人们似乎已经愿意牺牲所有最重要的软件设计原则,以使其适应MVVM.
那说,..我认为你的情况可能适合一点点重构.
在大多数情况下,我遇到过,WPF可以让你通过没有多个Windows.也许你可以尝试使用Frames和Pages而不是Windows DialogResult.
在你的情况下,我的建议是LoginFormViewModel处理LoginCommand,如果登录无效,请将属性设置为LoginFormViewModel适当的值(false或某些枚举值UserAuthenticationStates.FailedAuthentication).您可以为成功登录(true或其他一些枚举值)执行相同操作.然后,您将使用a DataTrigger响应各种用户身份验证状态,并可以使用简单的Setter方法更改其Source属性Frame.
让你的登录窗口返回一个DialogResult我认为是你感到困惑的地方; 这DialogResult实际上是ViewModel的一个属性.在我看来,WPF的经验有限,当一些事情感觉不对时,通常是因为我在考虑如何在WinForms中做同样的事情.
希望有所帮助.
假设您的登录对话框是第一个创建的窗口,请在LoginViewModel类中尝试:
void OnLoginResponse(bool loginSucceded)
{
if (loginSucceded)
{
Window1 window = new Window1() { DataContext = new MainWindowViewModel() };
window.Show();
App.Current.MainWindow.Close();
App.Current.MainWindow = window;
}
else
{
LoginError = true;
}
}
Run Code Online (Sandbox Code Playgroud)
小智 6
我处理它的方法是在我的ViewModel中添加一个事件处理程序.当用户成功登录后,我将触发该事件.在我的视图中,我会附加到此事件,当它被解雇时,我会关闭窗口.
这是我最初所做的,它确实有效,但看起来相当冗长且丑陋(全局静态任何东西都不好)
1:应用程序.xaml.cs
public partial class App : Application
{
// create a new global custom WPF Command
public static readonly RoutedUICommand LoggedIn = new RoutedUICommand();
}
Run Code Online (Sandbox Code Playgroud)
2:登录表单.xaml
// bind the global command to a local eventhandler
<CommandBinding Command="client:App.LoggedIn" Executed="OnLoggedIn" />
Run Code Online (Sandbox Code Playgroud)
3:LoginForm.xaml.cs
// implement the local eventhandler in codebehind
private void OnLoggedIn( object sender, ExecutedRoutedEventArgs e )
{
DialogResult = true;
Close();
}
Run Code Online (Sandbox Code Playgroud)
4:LoginFormViewModel.cs
// fire the global command from the viewmodel
private void OnRemoteServerReturnedSuccess()
{
App.LoggedIn.Execute(this, null);
}
Run Code Online (Sandbox Code Playgroud)
后来我删除了所有这些代码,只LoginFormViewModel在其视图上调用 Close 方法。它最终变得更好、更容易遵循。恕我直言,模式的要点是让人们更容易地理解你的应用程序在做什么,在这种情况下,MVVM 使它比我没有使用它时更难理解,现在是一种反模式。
| 归档时间: |
|
| 查看次数: |
101850 次 |
| 最近记录: |