Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# WPF MVVM从icommand执行更改父窗口视图模型_C#_Wpf_Mvvm_Icommand - Fatal编程技术网

C# WPF MVVM从icommand执行更改父窗口视图模型

C# WPF MVVM从icommand执行更改父窗口视图模型,c#,wpf,mvvm,icommand,C#,Wpf,Mvvm,Icommand,我目前正在掌握C#WPF MVVM模式,遇到了一个相当大的障碍 我试图做的是触发一个LoginCommand,当成功执行该命令时,将允许我更改父窗口的viewmodel。唯一的问题是,我想不出一种方法来更改父窗口的viewmodel而不破坏MVVM设计模式,因为我无法访问父窗口的ContentControl,该控件将其路径设置为窗口中的活动UserControlViewModel 以下是场景: 在我们的App.xaml中,我们有两个数据模板: 在我们的主窗口中我们有: main窗口将设置V

我目前正在掌握C#WPF MVVM模式,遇到了一个相当大的障碍

我试图做的是触发一个
LoginCommand
,当成功执行该命令时,将允许我更改父窗口的viewmodel。唯一的问题是,我想不出一种方法来更改父窗口的viewmodel而不破坏MVVM设计模式,因为我无法访问父窗口的
ContentControl
,该控件将其路径设置为窗口中的活动
UserControlViewModel

以下是场景:

在我们的
App.xaml
中,我们有两个数据模板:

在我们的
主窗口中
我们有:

main窗口将设置
ViewModel=LoginViewModel

在我们的
LoginViewModel
中,我们有:

现在为了钱。。。
LoginCommand

public void Execute(对象参数)
{
//做一些验证
//异步登录任务
// ...
//已登录…将主窗口的ViewModel更改为LoggedInViewModel
}

如何使Execute方法在不破坏MVVM模式的情况下更改窗口的viewmodel

到目前为止我已经尝试过的事情:

  • 使主窗口有一个静态实例单例,我可以访问它,然后从命令中更改
    ViewModel
    属性
  • 试图在主窗口中实现某种形式的路由命令侦听器,然后让命令触发要由父窗口处理的路由命令事件

我做了一个快速演示,展示了一种方法。我尽可能简单地给出了大概的想法。完成同一件事有很多不同的方法(例如,您可以在
LoginViewModel
中保存对
MainWindowViewModel
的引用,处理那里的一切,然后调用
MainWindowViewModel
上的方法来触发工作区更改,或者您可以使用事件/消息等)

不过你一定要读一读。这是一个非常好的介绍,在我开始使用它时,我发现它很有用

关键的一点是要有一个外部的
MainWindowViewModel
ApplicationViewModel
,用于处理导航、保存对工作区的引用等。然后,您可以选择如何与之交互

在下面的代码中,我省略了定义
窗口
用户控件
等过程中的杂乱内容,以使其更短

窗口:

<DockPanel>
    <ContentControl Content="{Binding CurrentWorkspace}"/>
</DockPanel>
LoginView:

在本例中,我将
登录视图上的
按钮
绑定到
窗口上的
登录命令
(即
主窗口视图模型

LoggedInView:

<StackPanel Orientation="Vertical">
    <TextBox Text="{Binding RestrictedData}"/>
</StackPanel>
工作空间视图模型:

public class LoginViewModel : WorkspaceViewModel
{
    private string userName;
    public string UserName
    {
        get { return userName; }
        set
        {
            if (userName != value)
            {
                userName = value;
                OnPropertyChanged();
            }
        }
    }

    public bool Validate()
    {
        if (UserName == "bob")
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}
public class LoggedInViewModel : WorkspaceViewModel
{
    private string restrictedData = "Some restricted data";
    public string RestrictedData
    {
        get { return restrictedData; }
        set
        {
            if (restrictedData != value)
            {
                restrictedData = value;
                OnPropertyChanged();
            }
        }
    }
}
public abstract class WorkspaceViewModel : ObservableObject
{
}

然后是一些您可能已经实现的其他类(或替代类)

可观察对象:

public abstract class ObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this,
            new PropertyChangedEventArgs(propertyName));
    }
}
中继命令:

public class RelayCommand : ICommand
{
    private readonly Action<object> execute;
    private readonly Predicate<object> canExecute;

    public RelayCommand(Action<object> execute)
        : this(execute, null)
    { }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        this.execute = execute;
        this.canExecute = canExecute;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return canExecute == null ? true : canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        execute(parameter);
    }
}
公共类RelayCommand:ICommand
{
私有只读操作执行;
私有只读谓词canExecute;
公共中继命令(操作执行)
:此(执行,空)
{ }
公共RelayCommand(操作执行,谓词canExecute)
{
if(execute==null)
{
抛出新的ArgumentNullException(“执行”);
}
this.execute=execute;
this.canExecute=canExecute;
}
公共事件事件处理程序CanExecuteChanged
{
添加{CommandManager.RequerySuggested+=value;}
删除{CommandManager.RequerySuggested-=value;}
}
[调试步骤至]
公共布尔CanExecute(对象参数)
{
返回canExecute==null?true:canExecute(参数);
}
public void Execute(对象参数)
{
执行(参数);
}
}
App.Xaml:

    <DataTemplate DataType="{x:Type ViewModels:LoginViewModel}">
        <Views:LoginView />
    </DataTemplate>
    <DataTemplate DataType="{x:Type ViewModels:LoggedInViewModel}">
        <Views:LoggedInView />
    </DataTemplate>


抱歉,这没有帮助可能我不理解这个问题,我建议您需要一个
MainWindowViewModel
ApplicationViewModel
,它处理导航并设置当前的
工作区
LoginViewModel
LoggedInViewModel
)。这里有一个很好的例子。在这种情况下,各种视图都绑定到按钮,但在您的情况下,您只需要让登录按钮触发一个命令进行验证,然后根据需要进行切换。(这里也有示例代码)好的,但是LoginCommand如何获取ApplicationViewModel的实例来执行ChangeViewModel命令呢@t
LoginCommand
将在
ApplicationViewModel
中定义,而不是在
LoginViewModel
中定义(此
ApplicationViewModel
将是
窗口的
DataContext
)。因此,假设您可能在
LoginViewModel
中的公共方法中定义了验证,您的
应用程序viewmodel
上的命令只调用该方法,如果它通过了您那里的任何验证,则切换到
LoggedInViewModel
。我将尝试让类似的方法工作。我看到的唯一问题是,如果我从LoginViewModel中删除LoginCommand,那么我将无法传递我用于实际登录的用户名、密码等信息;
public abstract class ObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this,
            new PropertyChangedEventArgs(propertyName));
    }
}
public class RelayCommand : ICommand
{
    private readonly Action<object> execute;
    private readonly Predicate<object> canExecute;

    public RelayCommand(Action<object> execute)
        : this(execute, null)
    { }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        this.execute = execute;
        this.canExecute = canExecute;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return canExecute == null ? true : canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        execute(parameter);
    }
}
    <DataTemplate DataType="{x:Type ViewModels:LoginViewModel}">
        <Views:LoginView />
    </DataTemplate>
    <DataTemplate DataType="{x:Type ViewModels:LoggedInViewModel}">
        <Views:LoggedInView />
    </DataTemplate>