我使用a创建一个登录,window control以允许用户登录WPF我正在创建的应用程序.
到目前为止,我已经创建了一种方法,用于检查用户是否已输入正确的凭据,username并password在textbox登录屏幕上输入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}" />
当我输入用户名和密码时,它会执行适当的代码,无论是正确还是错误.但是,当用户名和密码都正确时,如何从ViewModel关闭此窗口?
我之前尝试使用过dialog modal但是它没有完全奏效.此外,在我的app.xaml中,我做了类似下面的操作,首先加载登录页面,然后加载实际的应用程序.
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);
    }
}
问题:如何Window control从ViewModel 关闭登录?
提前致谢.
Joe*_*oel 131
您可以使用窗口将窗口传递给ViewModel CommandParameter.请参阅下面的示例.
我已经实现了一个CloseWindow方法,它将Windows作为参数并关闭它.窗口通过传递给ViewModel CommandParameter.请注意,您需要为x:Name应该关闭的窗口定义一个.在我的XAML窗口中,我通过调用此方法Command并将窗口本身作为参数传递给ViewModel使用CommandParameter.
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模式IMHO,因为它会强制您的虚拟机知道它正在查看的内容.
您可以通过引入包含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();
     }
 }
Ste*_*eck 31
保持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())
Chr*_*isO 30
当我需要这样做时,我通常会在视图模型上放置一个事件,然后将它连接到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(); 
小智 18
可能会迟到,但这是我的答案
foreach (Window item in Application.Current.Windows)
{
    if (item.DataContext == this) item.Close();
}
DHN*_*DHN 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
}
Ras*_*ssK 12
简单的方法
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);
这个怎么样?
视图模型:
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());
}
我知道这是一篇旧帖子,可能没有人会滚动这么远,我知道我没有。所以,经过几个小时的尝试不同的东西,我找到了这个博客,伙计把它杀了。最简单的方法来做到这一点,试过了,它就像一个魅力。
在视图模型中:
...
public bool CanClose { get; set; }
private RelayCommand closeCommand;
public ICommand CloseCommand
{
    get
    {
        if(closeCommand == null)
        (
            closeCommand = new RelayCommand(param => Close(), param => CanClose);
        )
    }
}
public void Close()
{
    this.Close();
}
...
向 ViewModel 添加 Action 属性,但从 View 的代码隐藏文件中定义它。这将让我们在 ViewModel 上动态定义一个指向 View 的引用。
在 ViewModel 上,我们只需添加:
public Action CloseAction { get; set; }
在视图上,我们将其定义为:
public View()
{
    InitializeComponent() // this draws the View
    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());
}
| 归档时间: | 
 | 
| 查看次数: | 96544 次 | 
| 最近记录: |