c# - 如何使用具有多个页面和导航的 WPF 处理依赖注入(inject)?

标签 c# wpf mvvm dependency-injection .net-5

我一直在尝试在 WPF 中执行以下操作:

  • 带有登录页面主页的窗口。
  • 成功验证用户身份后,窗口应显示主页。
  • 它应该通过使用 native WPF 依赖项注入(inject)框架来工作。

但也...

  • 可能有页面 3、4、5,并且每个页面都应该能够相互调用。
  • 也许每个页面中都可以包含可以互相调用的页面。
  • 因此,如果可能的话,该解决方案应该能够使用嵌套页面和导航。

我有什么:

因此,在堆栈论坛中寻找解决方案后,我最终采用了这种组合方法。

App.xaml开始,所有服务和 View 模型都被初始化,主窗口通过注入(inject)接收其 View 模型:

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 通过注入(inject)接收所有其他 View 模型并将它们存储在属性中。

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

所有页面 View 模型都继承自此接口(interface),因此可以按名称从集合中检索它们,然后在需要时将其添加为 SelectedPage。

public interface IPageViewModel : INotifyPropertyChanged
{
    public string PageTitle { get; set; }
}

窗口有一个内容控件,其属性内容绑定(bind)到 SelectedPage,以便更新。

<Window>
   <ContentControl Content="{Binding SelectedPage}" />
</Window>

并且它通过这些数据模板知道每个 View 模型要使用哪个 View 。

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

问题

我也许可以在所有子模型中注入(inject) mainviewmodel,但这看起来不太好,事实上从一开始很多事情就有点困惑。

例如,我必须:

  • 为我创建的每个 View 模型添加一个服务 View 模型到 app.xaml。
  • 将它们中的每一个添加为主窗口 View 模型的参数,这看起来很难看。

我可能做错了,我需要帮助。

最佳答案

有很多可能的解决方案。一个简单的方法就是引入一个事件。

我还建议将选择 View 模型的责任转移并限制到 MainViewModel。其他页面模型不应该知道像谁选择谁这样的流程。否则,这会增加过于紧密的耦合,这在此时是可以避免的。

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

  ...
}

关于c# - 如何使用具有多个页面和导航的 WPF 处理依赖注入(inject)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66578575/

相关文章:

WPF DataGrid 使用 MVVM 添加、更新和删除

c# - 调试器尝试使用错误的程序集名称启动程序集

c# - 在用户控件内单击按钮更改 View

android - 具有事件驱动的 Firebase 数据库的数据访问服务/层

c# - Rx.NET 在所有回调之后再次直接调用函数

c# - 如何在WPF中使用Flyout?

c# - WPF 媒体元素,带有 https 的源失败

c# - 如何定位图像中的对象?

c# - C#/VBA COM Interop 中的错误处理

c# - 如何将依赖项注入(inject) Caliburn.Micro 中的 View 模型?