有 2 个 ViewModel 和 AuthViewModel 和 LoginViewModel。LoginView 有一个 Frame,它的内容是一个 LoginControl,它有一个 AuthViewModel 上下文。在这个 LoginControl 中有一个按钮,当单击时,LoginCompleted 事件应该会触发,并且该事件在 App.xaml.cs 中进行处理。
AuthViewModel.cs
public event Action LoginCompleted;
public ICommand AuthCommand => new RelayCommand(o => AuthMethod());
public void AuthMethod()
{
LoginCompleted?.Invoke();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
应用程序.xaml.cs
private LoginWindow Window { get; set; }
private MainWindow CustomWindow { get; set; }
public MainLoginVIewModel MainViewModel { get; set; }
public LoginViewModel LoginViewModel { get; set; }
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
LoginViewModel = new LoginViewModel();
MainViewModel = new MainLoginVIewModel(LoginViewModel);
LoginViewModel.OnAuthorize += LoginViewModelOnOnAuthorize;
Window = new LoginWindow { DataContext = MainViewModel };
Window.Show();
}
private void LoginViewModelOnOnAuthorize(object sender, LoginEventArgs e)
{
if (e.IsAuthorized)
{
CustomWindow = new MainWindow { DataContext = new ContentViewModel(e.User) };
CustomWindow.Show();
Window.Close();
}
}
也就是说,通过单击 LoginControl 中的按钮,App.xaml.cs 中的事件应该会触发。如果我做错了什么,请告诉我如何更正确地实现它。
如果你需要更多,我会扔掉代码
注册控制.xaml
<Grid>
<TextBox Text="{Binding User}" Width="100" Height="100"></TextBox>
</Grid>
RegisterViewModel.cs
class RegisterViewModel : VM
{
private string user;
public string User
{
get => user;
set => Set(ref user, value);
}
}
主授权窗口的ViewModel,我称之为MainLoginViewModel
RegisterViewModel regVM;
public MainLoginVIewModel(LoginViewModel loginVM)
{
TestCommand = new RelayCommand(Test);
regVM = new RegisterViewModel();
CurrentContent = loginVM;
}
private VM currentContent;
public VM CurrentContent
{
get => currentContent;
set => Set(ref currentContent, value);
}
public ICommand TestCommand { get; }
private void Test()
{
CurrentContent = regVM;
}
如果你这么快看你的描述,那么你这里至少有3个问题:
x:Name="textBox1"
并且您想以这种方式为其设置文本:textBox1.Text = "..."
那么您直接使用此元素,根据 MVVM 规则,这是非常糟糕的!为此目的,有绑定(您指定<TextBox Text="{Binding MyTextProperty}">
)。尝试从控件中删除所有名称,甚至完全删除 View 部分。根据 MVVM 模式,即使没有视图,您的应用程序也应该可以工作。Frame
,这对于在窗口中显示内容来说不是一个很好的控件,它不适合 MVVM 模式。尝试使用此选项,它会变得更容易!让我们试着用我上面写的所有东西的实现来做一个简单的例子。让我们通过事件和内容更改来模拟授权:
我将从一个空项目开始这一切,你自己看看......
因此,首先,我们需要两个辅助类:
VM
- 它实现INotifyPropertyChanged
了,我们也将使用它来设置当前的内容。每个人都有不同的实现,我个人采取这个选项:RelayCommand
在 MVVM 中,不习惯使用控制事件(Click 等),它们被替换为绑定,命令 (ICommand
) 用于将逻辑附加到按钮。RelayCommand
分别是这个接口的一个实现。再说一次,每个人都不一样,我个人会选择这个选项:好的,准备工作已经完成。现在让我们为它们制作两个页面和一个虚拟机。
授权页面。在它上面,我们将询问用户的登录名和密码,并且在它的 VM 中,我们将实现一个在进行授权尝试时将被调用的命令。它本身
LoginViewModel
将从类继承VM
(因此我们可以在更改内容时应用它,也可以使用 INPC)。LoginViewModel
:LoginEventArgs
是事件数据 (EventArgs
),一个简单的类,其中包含要在此事件中传递的数据的结构:LoginView
- 这是我们授权的视图,只有两个字段和一个按钮,我们将所有内容绑定到来自LoginViewModel
. View 本身是一个用户控件 (UserControl
):包含我们将在授权后显示的内容的页面。我们完全通过类比来做所有事情
LoginView
:ContentViewModel
- 最简单的 ViewModel,例如,我将只包含一个属性。ViewModel 必须继承自 classVM
,否则我们将无法将其设置为内容:ContentView
- 查看我们的主要内容。是UserControl
,只包含绑定TextBlock
:现在让我们在应用程序的主窗口 (
MainWindow
) 上工作。对他来说,我们还将创建自己的 ViewModel,我们将给MainViewModel
它命名并将所有这些组合在其中:您可以看到我们创建了 Authorization ViewModel 属性,在构造函数中对其进行了初始化,订阅了事件,例如,将其设置为我们窗口的主要内容。例如,在事件处理程序中,我形成了一条分配给 created 属性的消息。
现在本身
MainWindow
:在帮助下
DataTemplate
,我们将每个 ViewModel 与它的 View 关联起来。我们还显示一条消息(通常的TextBlock
,绑定到属性Message
),以及页面的内容(我们将Frame
其替换为 ),我们将其设置为ContentPresenter
。我们还有待问
DataContext
,我们将通过重写来做到这一点App
:App.xaml
清理StartupUri="MainWindow.xaml"
。在
App.xaml.cs
重新定义OnStartup
:就是这样,让我们开始欣赏所做的工作:
所以,很简单,我们摆脱了可怕的
Frame
,使用一个事件并在一个窗口中显示不同的内容。请注意,我们从未访问过任何 View 元素,我们可以完全删除它们,我们的逻辑将过上自己的生活。这种分层就是 MVVM !最重要的是了解它的工作原理,然后一切都会像发条一样!祝你好运!如果我们有不同的窗口,那么最简单的选择是执行以下操作:
让我们重写 MainVewModel,或者更确切地说是它的构造函数,让它接受 LoginViewModel:
让我们重写App。这里我们需要初始化LoginVM,MainVM,订阅事件,在事件中做开/关逻辑,设置窗口属性:
就是这样,现在在启动时,会打开带有授权内容的MainWindow,如果授权成功,则MainWindow将关闭,然后会出现CustomWindow(我们的另一个窗口)。