我使用window control
创建登录,以允许用户登录我正在创建的WPF
应用程序。
到目前为止,我已经创建了一种方法,用于检查用户是否在登录屏幕上的username
中输入了password
和textbox
的正确凭据,{{1两个binding
。
我通过创建properties
方法实现了这一点,就像这样;
bool
我还public bool CheckLogin()
{
var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault();
if (user == null)
{
MessageBox.Show("Unable to Login, incorrect credentials.");
return false;
}
else if (this.Username == user.Username || this.Password.ToString() == user.Password)
{
MessageBox.Show("Welcome " + user.Username + ", you have successfully logged in.");
return true;
}
else
{
MessageBox.Show("Unable to Login, incorrect credentials.");
return false;
}
}
public ICommand ShowLoginCommand
{
get
{
if (this.showLoginCommand == null)
{
this.showLoginCommand = new RelayCommand(this.LoginExecute, null);
}
return this.showLoginCommand;
}
}
private void LoginExecute()
{
this.CheckLogin();
}
command
bind
xaml
我的按钮<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" />
就像这样;
dialog modal
当我输入用户名和密码时,它会执行适当的代码,无论是正确还是错误。但是,当用户名和密码都正确时,如何从ViewModel关闭此窗口?
我之前尝试过使用private void ApplicationStart(object sender, StartupEventArgs e)
{
Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
var dialog = new UserView();
if (dialog.ShowDialog() == true)
{
var mainWindow = new MainWindow();
Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
Current.MainWindow = mainWindow;
mainWindow.Show();
}
else
{
MessageBox.Show("Unable to load application.", "Error", MessageBoxButton.OK);
Current.Shutdown(-1);
}
}
,但实际上并没有成功。此外,在我的app.xaml中,我做了类似下面的操作,首先加载登录页面,然后加载实际的应用程序。
Window control
问题:如何从ViewModel关闭登录{{1}}?
提前致谢。
答案 0 :(得分:124)
您可以使用CommandParameter
将窗口传递给ViewModel。请参阅下面的示例。
我已经实现了一个CloseWindow
方法,该方法将Windows作为参数并关闭它。该窗口通过CommandParameter
传递给ViewModel。请注意,您需要为应该关闭的窗口定义x:Name
。在我的XAML窗口中,我通过Command
调用此方法,并使用CommandParameter
将窗口本身作为参数传递给ViewModel。
Command="{Binding CloseWindowCommand, Mode=OneWay}"
CommandParameter="{Binding ElementName=TestWindow}"
视图模型
public RelayCommand<Window> CloseWindowCommand { get; private set; }
public MainViewModel()
{
this.CloseWindowCommand = new RelayCommand<Window>(this.CloseWindow);
}
private void CloseWindow(Window window)
{
if (window != null)
{
window.Close();
}
}
视图的
<Window x:Class="ClientLibTestTool.ErrorView"
x:Name="TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:localization="clr-namespace:ClientLibTestTool.ViewLanguages"
DataContext="{Binding Main, Source={StaticResource Locator}}"
Title="{x:Static localization:localization.HeaderErrorView}"
Height="600" Width="800"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen">
<Grid>
<Button Content="{x:Static localization:localization.ButtonClose}"
Height="30"
Width="100"
Margin="0,0,10,10"
IsCancel="True"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Command="{Binding CloseWindowCommand, Mode=OneWay}"
CommandParameter="{Binding ElementName=TestWindow}"/>
</Grid>
</Window>
请注意,我使用的是MVVM轻量级框架,但主体适用于每个wpf应用程序。
此解决方案违反了MVVM模式,因为视图模型不应该知道有关UI实现的任何信息。如果要严格遵循MVVM编程范例,则必须使用接口抽象视图类型。
MVVM符合解决方案(前EDIT2)
用户 Crono 在评论部分提及有效点:
将Window对象传递给视图模型会破坏MVVM模式 恕我直言,因为它迫使你的虚拟对象知道它正在被查看的内容。
您可以通过引入包含close方法的接口来解决此问题。
接口:
public interface ICloseable
{
void Close();
}
您重构的ViewModel将如下所示:
视图模型
public RelayCommand<ICloseable> CloseWindowCommand { get; private set; }
public MainViewModel()
{
this.CloseWindowCommand = new RelayCommand<IClosable>(this.CloseWindow);
}
private void CloseWindow(ICloseable window)
{
if (window != null)
{
window.Close();
}
}
您必须在视图中引用并实施ICloseable
界面
查看(代码隐藏)
public partial class MainWindow : Window, ICloseable
{
public MainWindow()
{
InitializeComponent();
}
}
回答原始问题:(前EDIT1)
您的登录按钮(已添加CommandParameter):
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/>
您的代码:
public RelayCommand<Window> CloseWindowCommand { get; private set; } // the <Window> is important for your solution!
public MainViewModel()
{
//initialize the CloseWindowCommand. Again, mind the <Window>
//you don't have to do this in your constructor but it is good practice, thought
this.CloseWindowCommand = new RelayCommand<Window>(this.CloseWindow);
}
public bool CheckLogin(Window loginWindow) //Added loginWindow Parameter
{
var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault();
if (user == null)
{
MessageBox.Show("Unable to Login, incorrect credentials.");
return false;
}
else if (this.Username == user.Username || this.Password.ToString() == user.Password)
{
MessageBox.Show("Welcome "+ user.Username + ", you have successfully logged in.");
this.CloseWindow(loginWindow); //Added call to CloseWindow Method
return true;
}
else
{
MessageBox.Show("Unable to Login, incorrect credentials.");
return false;
}
}
//Added CloseWindow Method
private void CloseWindow(Window window)
{
if (window != null)
{
window.Close();
}
}
答案 1 :(得分:26)
保留MVVM,我认为使用Blend SDK中的行为(System.Windows.Interactivity)或Prism的自定义交互请求可以很好地适应这种情况。
如果走行为路线,这是一般性的想法:
public class CloseWindowBehavior : Behavior<Window>
{
public bool CloseTrigger
{
get { return (bool)GetValue(CloseTriggerProperty); }
set { SetValue(CloseTriggerProperty, value); }
}
public static readonly DependencyProperty CloseTriggerProperty =
DependencyProperty.Register("CloseTrigger", typeof(bool), typeof(CloseWindowBehavior), new PropertyMetadata(false, OnCloseTriggerChanged));
private static void OnCloseTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = d as CloseWindowBehavior;
if (behavior != null)
{
behavior.OnCloseTriggerChanged();
}
}
private void OnCloseTriggerChanged()
{
// when closetrigger is true, close the window
if (this.CloseTrigger)
{
this.AssociatedObject.Close();
}
}
}
然后在您的窗口中,您只需将CloseTrigger绑定到一个布尔值,该值将在您希望窗口关闭时设置。
<Window x:Class="TestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="clr-namespace:TestApp"
Title="MainWindow" Height="350" Width="525">
<i:Interaction.Behaviors>
<local:CloseWindowBehavior CloseTrigger="{Binding CloseTrigger}" />
</i:Interaction.Behaviors>
<Grid>
</Grid>
</Window>
最后,您希望窗口关闭时,您的DataContext / ViewModel将具有您设置的属性:
public class MainWindowViewModel : INotifyPropertyChanged
{
private bool closeTrigger;
/// <summary>
/// Gets or Sets if the main window should be closed
/// </summary>
public bool CloseTrigger
{
get { return this.closeTrigger; }
set
{
this.closeTrigger = value;
RaisePropertyChanged("CloseTrigger");
}
}
public MainWindowViewModel()
{
// just setting for example, close the window
CloseTrigger = true;
}
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
(设置你的Window.DataContext = new MainWindowViewModel())
答案 2 :(得分:25)
我通常在需要执行此操作时在视图模型上放置一个事件,然后在将视图模型绑定到窗口时将其挂钩到Window.Close()
public class LoginViewModel
{
public event EventHandler OnRequestClose;
private void Login()
{
// Login logic here
OnRequestClose(this, new EventArgs());
}
}
创建登录窗口时
var vm = new LoginViewModel();
var loginWindow = new LoginWindow
{
DataContext = vm
};
vm.OnRequestClose += (s, e) => loginWindow.Close();
loginWindow.ShowDialog();
答案 3 :(得分:12)
这是我在几个项目中使用的东西。它可能看起来像一个黑客,但它工作正常。
public class AttachedProperties : DependencyObject //adds a bindable DialogResult to window
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached("DialogResult", typeof(bool?), typeof(AttachedProperties),
new PropertyMetaData(default(bool?), OnDialogResultChanged));
public bool? DialogResult
{
get { return (bool?)GetValue(DialogResultProperty); }
set { SetValue(DialogResultProperty, value); }
}
private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window == null)
return;
window.DialogResult = (bool?)e.NewValue;
}
}
现在,您可以将DialogResult
绑定到VM并设置其属性值。设置值后,Window
将关闭。
<!-- Assuming that the VM is bound to the DataContext and the bound VM has a property DialogResult -->
<Window someNs:AttachedProperties.DialogResult={Binding DialogResult} />
这是我们生产环境中正在运行的内容的摘要
<Window x:Class="AC.Frontend.Controls.DialogControl.Dialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:DialogControl="clr-namespace:AC.Frontend.Controls.DialogControl"
xmlns:hlp="clr-namespace:AC.Frontend.Helper"
MinHeight="150" MinWidth="300" ResizeMode="NoResize" SizeToContent="WidthAndHeight"
WindowStartupLocation="CenterScreen" Title="{Binding Title}"
hlp:AttachedProperties.DialogResult="{Binding DialogResult}" WindowStyle="ToolWindow" ShowInTaskbar="True"
Language="{Binding UiCulture, Source={StaticResource Strings}}">
<!-- A lot more stuff here -->
</Window>
如您所见,我首先声明命名空间xmlns:hlp="clr-namespace:AC.Frontend.Helper"
,然后声明绑定hlp:AttachedProperties.DialogResult="{Binding DialogResult}"
。
AttachedProperty
看起来像这样。这与我昨天发布的不一样,但恕我直言,它应该没有任何效果。
public class AttachedProperties
{
#region DialogResult
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached("DialogResult", typeof (bool?), typeof (AttachedProperties), new PropertyMetadata(default(bool?), OnDialogResultChanged));
private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var wnd = d as Window;
if (wnd == null)
return;
wnd.DialogResult = (bool?) e.NewValue;
}
public static bool? GetDialogResult(DependencyObject dp)
{
if (dp == null) throw new ArgumentNullException("dp");
return (bool?)dp.GetValue(DialogResultProperty);
}
public static void SetDialogResult(DependencyObject dp, object value)
{
if (dp == null) throw new ArgumentNullException("dp");
dp.SetValue(DialogResultProperty, value);
}
#endregion
}
答案 4 :(得分:10)
简单方法
public interface IRequireViewIdentification
{
Guid ViewID { get; }
}
实施到ViewModel
public class MyViewVM : IRequireViewIdentification
{
private Guid _viewId;
public Guid ViewID
{
get { return _viewId; }
}
public MyViewVM()
{
_viewId = Guid.NewGuid();
}
}
添加常规窗口管理器帮助程序
public static class WindowManager
{
public static void CloseWindow(Guid id)
{
foreach (Window window in Application.Current.Windows)
{
var w_id = window.DataContext as IRequireViewIdentification;
if (w_id != null && w_id.ViewID.Equals(id))
{
window.Close();
}
}
}
}
在viewmodel
中将其关闭WindowManager.CloseWindow(ViewID);
答案 5 :(得分:9)
可能会迟到,但这是我的回答
abbr
答案 6 :(得分:4)
this怎么样?
视图模型:
class ViewModel
{
public Action CloseAction { get; set; }
private void Stuff()
{
// Do Stuff
CloseAction(); // closes the window
}
}
在ViewModel中使用CloseAction()来关闭窗口,就像上面的示例一样。
查看:
public View()
{
InitializeComponent();
ViewModel vm = new ViewModel (); // this creates an instance of the ViewModel
this.DataContext = vm; // this sets the newly created ViewModel as the DataContext for the View
if (vm.CloseAction == null)
vm.CloseAction = new Action(() => this.Close());
}
答案 7 :(得分:3)
这是我非常简单的方式:
<强> YourWindow.xaml.cs 强>
//In your constructor
public YourWindow()
{
InitializeComponent();
DataContext = new YourWindowViewModel(this);
}
<强> YourWindowViewModel.cs 强>
private YourWindow window;//so we can kill the window
//In your constructor
public YourWindowViewModel(YourWindow window)
{
this.window = window;
}
//to close the window
public void CloseWindow()
{
window.Close();
}
我没有看到你选择的答案有什么问题,我只是觉得这可能是一种更简单的方法!
答案 8 :(得分:2)
这是一个使用MVVM Light Messenger而不是事件的简单示例。单击按钮时,视图模型会发送关闭消息:
public MainViewModel()
{
QuitCommand = new RelayCommand(ExecuteQuitCommand);
}
public RelayCommand QuitCommand { get; private set; }
private void ExecuteQuitCommand()
{
Messenger.Default.Send<CloseMessage>(new CloseMessage());
}
然后在窗口后面的代码中收到它。
public Main()
{
InitializeComponent();
Messenger.Default.Register<CloseMessage>(this, HandleCloseMessage);
}
private void HandleCloseMessage(CloseMessage closeMessage)
{
Close();
}
答案 9 :(得分:1)
您可以像这样在ViewModel中创建新的事件处理程序。
$i=0;
while($row = $result->fetch_array(MYSQLI_BOTH) and ($counter < $max)){
$url=$row['url'];
$class = ($i%2 == 0) ? 'first' : 'second';
echo "<img src='$url' class='$class'>";
++$i;
}
然后为ExitCommand定义RelayCommand。
public event EventHandler RequestClose;
protected void OnRequestClose()
{
if (RequestClose != null)
RequestClose(this, EventArgs.Empty);
}
然后在XAML文件集中
private RelayCommand _CloseCommand;
public ICommand CloseCommand
{
get
{
if(this._CloseCommand==null)
this._CloseCommand=new RelayCommand(CloseClick);
return this._CloseCommand;
}
}
private void CloseClick(object obj)
{
OnRequestClose();
}
在xaml.cs文件中设置DataContext并订阅我们创建的事件。
<Button Command="{Binding CloseCommand}" />
答案 10 :(得分:1)
我提供的方法是在ViewModel中声明事件并使用如下的混合InvokeMethodAction。
示例ViewModel
public class MainWindowViewModel : BindableBase, ICloseable
{
public DelegateCommand SomeCommand { get; private set; }
#region ICloseable Implementation
public event EventHandler CloseRequested;
public void RaiseCloseNotification()
{
var handler = CloseRequested;
if (handler != null)
{
handler.Invoke(this, EventArgs.Empty);
}
}
#endregion
public MainWindowViewModel()
{
SomeCommand = new DelegateCommand(() =>
{
//when you decide to close window
RaiseCloseNotification();
});
}
}
I Closeable界面如下所示,但不需要执行此操作。 ICloseable将有助于创建通用视图服务,因此如果您通过依赖注入构造视图和ViewModel,那么您可以做的是
internal interface ICloseable
{
event EventHandler CloseRequested;
}
使用ICloseable
var viewModel = new MainWindowViewModel();
// As service is generic and don't know whether it can request close event
var window = new Window() { Content = new MainView() };
var closeable = viewModel as ICloseable;
if (closeable != null)
{
closeable.CloseRequested += (s, e) => window.Close();
}
以下是Xaml,即使您没有实现界面,也可以使用此xaml,它只需要您的视图模型来提升CloseRquested。
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFRx"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:ViewModels="clr-namespace:WPFRx.ViewModels" x:Name="window" x:Class="WPFRx.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
d:DataContext="{d:DesignInstance {x:Type ViewModels:MainWindowViewModel}}">
<i:Interaction.Triggers>
<i:EventTrigger SourceObject="{Binding Mode=OneWay}" EventName="CloseRequested" >
<ei:CallMethodAction TargetObject="{Binding ElementName=window}" MethodName="Close"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<Button Content="Some Content" Command="{Binding SomeCommand}" Width="100" Height="25"/>
</Grid>
答案 11 :(得分:1)
您可以使用MVVMLight工具包中的Messenger
。在ViewModel
发送如下信息:
Messenger.Default.Send(new NotificationMessage("Close"));
然后在你的Windows代码后面InitializeComponent
之后,注册这样的消息:
Messenger.Default.Register<NotificationMessage>(this, m=>{
if(m.Notification == "Close")
{
this.Close();
}
});
您可以在此处找到有关MVVMLight工具包的更多信息: MVVMLight toolkit on Codeplex
请注意,在所有规则中都没有&#34;没有代码隐藏&#34;在MVVM中,您可以在代码隐藏视图中注册消息。
答案 12 :(得分:0)
在 MVVM WPF 中,我通常将视图设计为 UserControl。这只是你想如何显示它的问题。如果你想让它在一个窗口中,那么你可以做一个 WindowService 类:
public class WindowService
{
//...
public void Show_window(object viewModel, int height, int width, string title)
{
var window = new Window
{
Content = viewModel,
Title = title,
Height = height,
Width = width,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = Application.Current.MainWindow,
Style = (Style)Application.Current.FindResource("Window_style") //even style can be added
};
//If you own custom window style, then you can bind close/minimize/maxmize/restore buttons like this
window.CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, OnCloseWindow));
window.CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, OnMaximizeWindow, OnCanResizeWindow));
window.CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, OnMinimizeWindow, OnCanMinimizeWindow));
window.CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, OnRestoreWindow, OnCanResizeWindow));
window.ShowDialog();
}
public void Close_window(object viewmodel)
{
//Close window
foreach (Window item in Application.Current.Windows)
{
if (item.Content == viewmodel) item.Close();
}
}
}
使用我的方法很简单。通常你想在它发生某些事情时关闭 Window。因此,当它这样做时,只需从相应的 ViewModel 调用 Close_window 方法 - 这是显示在窗口中的 UserControl 的 DataContext 。看底部示例:
1.) 我们从一些 Viewmodel 打开 Window:
public class MyViewModel // ViewModel where you open window
{
private readonly WindowService _windowservice // or inject/inherit from Base
public MyViewModel()
{
_windowservice = new WindowService();
}
private void Example_method()
{
//...Open window
_windowservice.Show_window(new WindowViewModel(),100,100,"Test window");
}
}
2.) 我们的窗口已经打开,现在我们要关闭它:
public class WindowViewModel // ViewModel which is your Window content!
{
private readonly WindowService _windowservice // or inject/inherit from Base
public MyViewModel()
{
_windowservice = new WindowService();
}
private void Example_method()
{
//Close window
_windowservice.Close(this); //Pass a reference of viewmodel to method
}
}
这个解决方案远没有其他公认的答案那么优雅,但对我来说它有效。我在项目中广泛使用它,到目前为止它没有问题。但我敢肯定有人会过来说“这违反了 MVVM 原则”。
答案 13 :(得分:0)
您可以将窗口视为服务(例如UI服务),然后通过界面将其自身传递给视图模型,例如:
public interface IMainWindowAccess
{
void Close(bool result);
}
public class MainWindow : IMainWindowAccess
{
// (...)
public void Close(bool result)
{
DialogResult = result;
Close();
}
}
public class MainWindowViewModel
{
private IMainWindowAccess access;
public MainWindowViewModel(IMainWindowAccess access)
{
this.access = access;
}
public void DoClose()
{
access.Close(true);
}
}
此解决方案具有将视图本身传递给viewmodel的最大优势,而不会破坏MVVM,因为尽管物理视图传递给viewmodel,但后者仍然不了解前者,它只能看到一些{{1} }。因此,例如,如果我们想将此解决方案迁移到其他平台,则仅是为IMainWindowAccess
正确实施IMainWindowAccess
的问题。
我在这里发布解决方案以提出一种不同于事件的方法(尽管实际上非常相似),因为它似乎比实现事件(附加/分离等)要简单一些,但是仍然与MVVM保持一致模式。
答案 14 :(得分:0)
我知道这是一篇过时的文章,可能没有人会滚动到这么远,我知道我没有。因此,经过数小时的尝试,我找到了这个博客,并把它杀死了。进行此操作的最简单方法是尝试一下,它就像一种魅力。
答案 15 :(得分:0)
您只需使用以下代码关闭当前窗口:
Application.Current.Windows[0].Close();
答案 16 :(得分:0)
这很简单。 您可以为Login创建自己的ViewModel类 - LoginViewModel。 您可以创建view var dialog = new UserView();在您的LoginViewModel中。 您可以将Command LoginCommand设置为按钮。
<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding LoginCommand}" />
和
<Button Name="btnCancel" IsDefault="True" Content="Login" Command="{Binding CancelCommand}" />
ViewModel类:
public class LoginViewModel
{
Window dialog;
public bool ShowLogin()
{
dialog = new UserView();
dialog.DataContext = this; // set up ViewModel into View
if (dialog.ShowDialog() == true)
{
return true;
}
return false;
}
ICommand _loginCommand
public ICommand LoginCommand
{
get
{
if (_loginCommand == null)
_loginCommand = new RelayCommand(param => this.Login());
return _loginCommand;
}
}
public void CloseLoginView()
{
if (dialog != null)
dialog.Close();
}
public void Login()
{
if(CheckLogin()==true)
{
CloseLoginView();
}
else
{
// write error message
}
}
public bool CheckLogin()
{
// ... check login code
return true;
}
}
答案 17 :(得分:-4)
System.Environment.Exit(0);在视图模型中可以工作。