如何使用具有多个页面和导航的 WPF 处理依赖注入?
How to handle dependency injection with WPF with multiple pages and navigation?
我一直在尝试在 WPF 中做以下事情:
- 一个 window,带有 登录页面 和 主页 。
- 成功验证用户后,window 应显示主页。
- 它应该通过使用本机 WPF 依赖注入框架来工作。
而且...
- 可能有第 3、4、5 页,这些页面中的每一个都应该能够相互调用。
- 也许这些页面中的每一个都可以包含可以相互调用的页面。
- 因此,如果可能,该解决方案应该能够处理嵌套页面和导航。
我有:
所以,在堆栈论坛中寻找解决方案后,我最终采用了这种组合方法。
从 App.xaml 开始,所有服务和视图模型都已初始化,主要 window 通过注入接收其视图模型:
private void ConfigureServices(ServiceCollection services)
{
services.AddSingleton<MainWindow>();
//ViewModels
services.AddSingleton<MainViewModel>();
services.AddSingleton<AuthViewModel>();
services.AddSingleton<HomeViewModel>();
}
private void OnStartup(object sender, StartupEventArgs e)
{
var mainWindow = serviceProvider.GetService<MainWindow>();
mainWindow.DataContext = serviceProvider.GetService<MainViewModel>();
mainWindow.Show();
}
然后,mainViewModel 通过注入接收所有其他视图模型并将它们存储在 属性。
public class MainViewModel
{
public IPageViewModel SelectedPage {get; set; } //PropertyChanged() removed for brevity.
public ObservableCollection<IPageViewModel> Pages {get; set;}
public MainViewModel(AuthViewModel authViewModel, HomeViewModel homeViewModel)
{
this.Pages = new ObservableCollection<IPageViewModel>() { authViewModel, homeViewModel};
this.SelectedPage = this.Pages.First();
}
}
所有页面视图模型都继承自此接口,因此可以按名称从集合中检索它们,然后在需要时添加为 SelectedPage。
public interface IPageViewModel : INotifyPropertyChanged
{
public string PageTitle { get; set; }
}
window 有一个内容控件,其中 属性 内容绑定到 SelectedPage,因此它已更新。
<Window>
<ContentControl Content="{Binding SelectedPage}" />
</Window>
并且它知道通过这些数据模板为每个视图模型使用哪个视图。
<Application.Resources>
<DataTemplate DataType="{x:Type vm:AuthViewModel}">
<views:AuthView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:HomeViewModel}">
<views:HomeView />
</DataTemplate>
</Application.Resources>
但是后来...我注意到这行不通,我只能从 mainViewModel 中调用 SelectedPage 上的更改。
public class AuthViewModel : BaseViewModel
{
public AuthViewModel() { }
public void AttemptLogin() {
// how
SelectedPage = Pages[1];
}
}
问题
我也许可以在所有子模型中注入 mainviewmodel,但这看起来不太好,事实上从一开始很多事情都是一团糟。
例如,我必须:
- 为我创建的每个视图模型添加一个服务视图模型到 app.xaml。
- 将它们中的每一个都添加为看起来很难看的主要window视图模型的参数。
我可能做错了,我需要帮助。
有很多可能的解决方案。简单的就是介绍一个事件
我还建议将 select 视图模型的责任移动并限制到 MainViewModel
。其他页面模型不应该知道谁 select 是谁的流程。否则这会增加一个太紧的耦合,这在这一点上是可以避免的。
public class MainViewModel
{
public IPageViewModel SelectedPage { get; set; }
private Dictionary<string, IPageViewModel> Pages { get; }
public MainViewModel(AuthViewModel authViewModel, HomeViewModel homeViewModel)
{
authViewModel.AuthenticationPassed += OnAuthenticationSuccessfull;
this.Pages = new Dictionary<string, IPageViewModel>()
{
{ nameof(AuthViewModel), authViewModel },
{ nameof(HomeViewModel), homeViewModel }
};
this.SelectedPage = this.Pages[nameof(AuthViewModel)];
}
public OnAuthenticationSuccessfull(object sender, EventArgs e)
{
(sender as AuthViewModel).AuthenticationPassed -= OnAuthenticationSuccessfull;
this.SelectedPage = this.Pages[nameof(HomeViewModel)];
}
}
class AuthViewModel
{
public event EventHandler AuthenticationPassed { get; }
...
}
我一直在尝试在 WPF 中做以下事情:
- 一个 window,带有 登录页面 和 主页 。
- 成功验证用户后,window 应显示主页。
- 它应该通过使用本机 WPF 依赖注入框架来工作。
而且...
- 可能有第 3、4、5 页,这些页面中的每一个都应该能够相互调用。
- 也许这些页面中的每一个都可以包含可以相互调用的页面。
- 因此,如果可能,该解决方案应该能够处理嵌套页面和导航。
我有:
所以,在堆栈论坛中寻找解决方案后,我最终采用了这种组合方法。
从 App.xaml 开始,所有服务和视图模型都已初始化,主要 window 通过注入接收其视图模型:
private void ConfigureServices(ServiceCollection services)
{
services.AddSingleton<MainWindow>();
//ViewModels
services.AddSingleton<MainViewModel>();
services.AddSingleton<AuthViewModel>();
services.AddSingleton<HomeViewModel>();
}
private void OnStartup(object sender, StartupEventArgs e)
{
var mainWindow = serviceProvider.GetService<MainWindow>();
mainWindow.DataContext = serviceProvider.GetService<MainViewModel>();
mainWindow.Show();
}
然后,mainViewModel 通过注入接收所有其他视图模型并将它们存储在 属性。
public class MainViewModel
{
public IPageViewModel SelectedPage {get; set; } //PropertyChanged() removed for brevity.
public ObservableCollection<IPageViewModel> Pages {get; set;}
public MainViewModel(AuthViewModel authViewModel, HomeViewModel homeViewModel)
{
this.Pages = new ObservableCollection<IPageViewModel>() { authViewModel, homeViewModel};
this.SelectedPage = this.Pages.First();
}
}
所有页面视图模型都继承自此接口,因此可以按名称从集合中检索它们,然后在需要时添加为 SelectedPage。
public interface IPageViewModel : INotifyPropertyChanged
{
public string PageTitle { get; set; }
}
window 有一个内容控件,其中 属性 内容绑定到 SelectedPage,因此它已更新。
<Window>
<ContentControl Content="{Binding SelectedPage}" />
</Window>
并且它知道通过这些数据模板为每个视图模型使用哪个视图。
<Application.Resources>
<DataTemplate DataType="{x:Type vm:AuthViewModel}">
<views:AuthView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:HomeViewModel}">
<views:HomeView />
</DataTemplate>
</Application.Resources>
但是后来...我注意到这行不通,我只能从 mainViewModel 中调用 SelectedPage 上的更改。
public class AuthViewModel : BaseViewModel
{
public AuthViewModel() { }
public void AttemptLogin() {
// how
SelectedPage = Pages[1];
}
}
问题
我也许可以在所有子模型中注入 mainviewmodel,但这看起来不太好,事实上从一开始很多事情都是一团糟。
例如,我必须:
- 为我创建的每个视图模型添加一个服务视图模型到 app.xaml。
- 将它们中的每一个都添加为看起来很难看的主要window视图模型的参数。
我可能做错了,我需要帮助。
有很多可能的解决方案。简单的就是介绍一个事件
我还建议将 select 视图模型的责任移动并限制到 MainViewModel
。其他页面模型不应该知道谁 select 是谁的流程。否则这会增加一个太紧的耦合,这在这一点上是可以避免的。
public class MainViewModel
{
public IPageViewModel SelectedPage { get; set; }
private Dictionary<string, IPageViewModel> Pages { get; }
public MainViewModel(AuthViewModel authViewModel, HomeViewModel homeViewModel)
{
authViewModel.AuthenticationPassed += OnAuthenticationSuccessfull;
this.Pages = new Dictionary<string, IPageViewModel>()
{
{ nameof(AuthViewModel), authViewModel },
{ nameof(HomeViewModel), homeViewModel }
};
this.SelectedPage = this.Pages[nameof(AuthViewModel)];
}
public OnAuthenticationSuccessfull(object sender, EventArgs e)
{
(sender as AuthViewModel).AuthenticationPassed -= OnAuthenticationSuccessfull;
this.SelectedPage = this.Pages[nameof(HomeViewModel)];
}
}
class AuthViewModel
{
public event EventHandler AuthenticationPassed { get; }
...
}