C# WPF MVVM从icommand执行更改父窗口视图模型
我目前正在掌握C#WPF MVVM模式,遇到了一个相当大的障碍 我试图做的是触发一个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
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命令呢@tLoginCommand
将在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>