WPF MVVM从icommand执行更改父窗口viewmodel

时间:2016-06-04 00:14:35

标签: c# wpf mvvm icommand

我目前正处于掌握C#WPF MVVM模式的过程中,偶然发现了一个很大的障碍...

我试图触发LoginCommand,成功执行后我将更改父窗口的viewmodel。唯一的问题是我无法想到改变父窗口的viewmodel而不破坏MVVM设计模式的方法,因为我无法访问父窗口{{1}在窗口中设置其活动ContentControl的路径。

以下是该方案:

在我们的UserControlViewModel中,我们有两个DataTemplates:
App.xaml

在我们的<DataTemplate DataType="{x:Type ViewModels:LoginViewModel}"> <Views:LoginView /> </DataTemplate> <DataTemplate DataType="{x:Type ViewModels:LoggedInViewModel}"> <Views:LoggedView /> </DataTemplate>中,我们有:
MainWindow
后面的<ContentControl Content="{Binding ViewModel}" />代码会设置MainWindow

在我们的ViewModel = LoginViewModel中,我们有:
LoginViewModel

现在要钱...... <Button Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=pwPasswordBoxControlInXaml}" />
LoginCommand

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

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

  • 使MainWindow具有我可以访问的静态实例单例,然后从命令中更改public void Execute(object parameter) { // Do some validation // Async login task stuff // ... // Logged in... change the MainWindow's ViewModel to the LoggedInViewModel }属性。
  • 尝试在MainWindow中实现某种形式的路由命令侦听器,然后让命令触发由父窗口处理的路由命令事件。

2 个答案:

答案 0 :(得分:3)

我做了一个快速演示,展示了一种做法。我保持尽可能简单,以提出一般的想法。有很多不同的方法可以完成相同的事情(例如,您可以在MainWindowViewModel内部对LoginViewModel进行引用,处理其中的所有内容,然后在MainWindowViewModel上调用方法来触发工作区更改,或者您可以使用事件/消息等。)

但绝对有Navigation with MVVM的读物。这是一个非常好的介绍,当我开始使用它时,我发现它非常有用。

要取消这一点的关键是要有一个外部MainWindowViewModelApplicationViewModel来处理导航,保存对工作空间的引用等等。然后选择如何与此进行交互给你。

在下面的代码中,我忽略了定义WindowUserControl等的混乱,以缩短时间。

窗口:

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

MainWindowViewModel(应将其设置为DataContext的{​​{1}}):

Window

LoginView:

在此示例中,我将public class MainWindowViewModel : ObservableObject { LoginViewModel loginViewModel = new LoginViewModel(); LoggedInViewModel loggedInViewModel = new LoggedInViewModel(); public MainWindowViewModel() { CurrentWorkspace = loginViewModel; LoginCommand = new RelayCommand((p) => DoLogin()); } private WorkspaceViewModel currentWorkspace; public WorkspaceViewModel CurrentWorkspace { get { return currentWorkspace; } set { if (currentWorkspace != value) { currentWorkspace = value; OnPropertyChanged(); } } } public ICommand LoginCommand { get; set; } public void DoLogin() { bool isValidated = loginViewModel.Validate(); if (isValidated) { CurrentWorkspace = loggedInViewModel; } } } 上的Button绑定到LoginView LoginCommand上的Window(即DataContext

MainWindowViewModel

LoginViewModel:

<StackPanel Orientation="Vertical">
    <TextBox Text="{Binding UserName}"/>
    <Button Content="Login" Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.LoginCommand}"/>
</StackPanel>

LoggedInView:

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;
        }
    }
}

LoggedInViewModel:

<StackPanel Orientation="Vertical">
    <TextBox Text="{Binding RestrictedData}"/>
</StackPanel>

WorkspaceViewModel:

public class LoggedInViewModel : WorkspaceViewModel
{
    private string restrictedData = "Some restricted data";
    public string RestrictedData
    {
        get { return restrictedData; }
        set
        {
            if (restrictedData != value)
            {
                restrictedData = value;
                OnPropertyChanged();
            }
        }
    }
}

然后你可能已经实现了一些其他类(或替代方案)。

ObservableObject:

public abstract class WorkspaceViewModel : ObservableObject
{
}

RelayCommand:

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

的App.xaml:

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);
    }
}

答案 1 :(得分:0)

<ContentControl Content="{Binding ViewModel}">
  <ContentControl.Resources>
     <DataTemplate DataType="{x:Type vm:LoginViewModelClass}">
        <!-- some LoginView -->
     </DataTemplate>

     <DataTemplate DataType="{x:Type vm:LoggedInViewModelClass}">
        <!-- some LoggedInView -->
     </DataTemplate>
  </ContentControl.Resources>
</ContentControl>