我目前正处于掌握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?
到目前为止我尝试过的事情:
public void Execute(object parameter)
{
// Do some validation
// Async login task stuff
// ...
// Logged in... change the MainWindow's ViewModel to the LoggedInViewModel
}
属性。答案 0 :(得分:3)
我做了一个快速演示,展示了一种做法。我保持尽可能简单,以提出一般的想法。有很多不同的方法可以完成相同的事情(例如,您可以在MainWindowViewModel
内部对LoginViewModel
进行引用,处理其中的所有内容,然后在MainWindowViewModel
上调用方法来触发工作区更改,或者您可以使用事件/消息等。)
但绝对有Navigation with MVVM的读物。这是一个非常好的介绍,当我开始使用它时,我发现它非常有用。
要取消这一点的关键是要有一个外部MainWindowViewModel
或ApplicationViewModel
来处理导航,保存对工作空间的引用等等。然后选择如何与此进行交互给你。
在下面的代码中,我忽略了定义Window
,UserControl
等的混乱,以缩短时间。
窗口:
<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>