我正在構建一個 WPF 應用程式,它有一個視窗,一個帶有多個視圖模型的選項卡式界面(2 個視圖模型,第一個選項卡是 ViewModel1,其余的將始終是 ViewModel2)這些是由用戶控制元件使用資料觸發器加載的。
最后還有一個其他視窗,例如對話框/設定等。
專案結構
我的專案分為幾個層,如下所述:
- 核心層(基本代碼層/介面很少的 3rd 方庫)
- Repo Layer(Repository Layer with EF,基本的CRUD功能,沒有業務邏輯)
- 服務層(使用 repos 的各個服務層,也包含業務邏輯)
- WPF 層(帶有視圖模型的主 UI 層)
我遇到的問題之一是在 WPF/MVVM 設定中使用 DI,我在網上看到了很多資源,其中很多是 ASP.NET Core DI,它與我關注的內容無關。我發現的問題是,在 WPF 中似乎有一個應用程式啟動點 (MainWindow),當使用 DI 時,所有內容都被注入到建構式中,然后將其進一步傳遞給其他對話框/模型,這看起來非常麻煩(或我做錯了)。例如,我目前在使用 Microsoft DI 的 App 代碼背后有類似的東西。
(對話框用于創建對話框/打開視窗等,訊息用于訊息框)
services.AddSingleton<IRepo1, Repo1>();
services.AddSingleton<IRepo1Service, Repo1Service>();
services.AddSingleton<IDialog, Dialog>();
services.AddSingleton<IMessaging, Messaging>();
要在視圖模型中使用這些,它們需要像這樣被注入到 MainWindow 建構式中
MainWindow(repo1service, dialog, messaging)
然后將這些傳遞給視圖模型
MainWindowViewModel(repo1service,dialog,messaging)
這個程序似乎我在建構式中做了很多作業并從單個應用程式點向下遍歷,其中一些視圖模型甚至可能不使用對話框或訊息傳遞,而只使用資料庫背景關系
MainWindowViewModel 具有選項卡式控制元件,然后在添加另一個選項卡時呼叫相應的視圖模型,因此例如我需要執行以下操作:
Tabs.Add(new ViewModel1(repo1service,dialog,messaging))
當用戶單擊新選項卡時。
我意識到我可以使用 DI 并像單例一樣添加視圖模型:
services.AddSingleton<ViewModel1>();
但是,如果僅在單擊按鈕后才呼叫此視圖模型,我仍然需要將引數傳遞給建構式。
如何避免將許多引數傳遞給視圖模型,DI 如何為我解決這個問題?我可以將 IServiceCollection 傳遞給模型并根據需要進行檢索還是這是一種不好的方法?
我已經閱讀了這些資源,但我仍然不確定如何解決我的問題。
https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-usage
如何處理 WPF/MVVM 應用程式中的依賴注入
依賴注入和建構式的瘋狂修改
如何避免依賴注入建構式的瘋狂?
uj5u.com熱心網友回復:
使用依賴注入時如何連接 WPF 應用程式的簡單示例。它還展示了如何注冊工廠(在本例中為委托)以啟用動態實體創建。這個想法是讓容器為你創建任何實體。DI 允許您避免使用new關鍵字,因此免除了您關注建構式及其依賴項的責任。
WPF 中推薦的顯示視圖的一般方法是視圖模型優先原則:基本上,將資料模型添加到視圖(例如通過將視圖模型系結到 aContentControl或ContentPresenter)并使用 aDataTemplate讓框架加載相關視圖。
應用程式.xaml.cs
private async void OnStartup(object sender, StartupEventArgs e)
{
var services = new ServiceCollection();
// Look how you can chain the service registrations:
services.AddSingleton<IMainViewModel, MainViewModel>()
.AddSingleton<IRepository, Repository>()
.AddSingleton<IViewModel1, ViewModel1>()
// Factory is used to create instances dynamically.
// Alternatively, instead of a delegate you can register a factory class.
.AddSingleton<Func<IViewModel1>>(serviceProvider => serviceProvider.GetService<IViewModel1>)
// Register a factory delegate taking an argument
.AddSingleton<Func<User, IViewModel2>>(serviceProvider => user =>
{
var repository = serviceProvider.GetService<IRepository>();
var dependency1 = serviceProvider.GetService<IDependency1>();
var dependency2 = serviceProvider.GetService<IDependency2>();
return new ViewModel2(
repository, dependency1, dependency2, user);
// Alternativelyly set property instead of using constructor
var viewModel2 = serviceProvider.GetService<IViewModel2>();
viewModel2.User = user;
return viewModel2;
})
// Alternatively register an abstract factory
.AddSingleton<IViewModel2Factory, ViewModel2Factory>()
.AddSingleton<IDependency1, Dependency1>()
.AddSingleton<IDependency2, Dependency2>()
.AddSingleton<IViewModel2, ViewModel2>()
.AddSingleton<IRepository, Repository>()
.AddSingleton<MainView>();
ServiceProvider container = services.BuildServiceProvider();
// Let the container compose the dependency graph
// and export the MainView to start the GUI
var mainWindow = container.GetService<MainView>();
// Launch the main view
mainWindow.Show();
}
ViewModel2Factory.cs
class ViewModel2Factory : IViewModel2Factory
{
private IRepository Repository { get; }
private Func<IRepository> RepositoryFactory { get; }
private Func<IDependency1> Dependency1Factory { get; }
private Func<IDependency2> Dependency2Factory { get; }
public (
IRepository repository,
IDependency1 dependency1,
IDependency2 dependency2)
{
// TDODO::Initialize properties
}
public IViewModel2 Create(User user)
{
var repository = this.RepositoryFactory.Invoke();
var dependency1 = this.Dependency1Factory.Invoke();
var dependency2 = this.Dependency2Factory.Invoke();
return new ViewModel2(repository, dependency1, dependency2, user);
}
}
主視圖模型.cs
class MainViewModel : IMainViewModel
{
public IViewModel2 ViewModel2 { get; }
private IViewModel1 ViewModel1 { get; }
private Func<IViewModel1> TabItemFactory { get; }
public MainViewModel(
IViewMode1 viewModel1,
Func<IViewModel1> viewModel1Factory,
IViewModel2 viewModel2)
{
this.ViewModel = viewModel2; // public read-only for data binding
this.ViewModel1 = viewModel1; // private read-only for internal use only
this.TabItemFactory = viewModel1Factory; // private read-only for internal use only
}
private void ExecuteAddTabCommand(object commandParameter)
{
// Uses a shared instance for every tab
this.Tabs.Add(this.ViewModel1);
// Create a fresh instance for every tab using a factory
IViewModel1 tabViewModel = this.TabItemFactory.Invoke();
this.Tabs.Add(tabViewModel);
}
}
主視窗.xaml.cs
partial class MainWindow : Window
{
public MainWindow(IMainViewModel mainViewModel)
{
InitializeComponent();
this.DataContext = mainViewModel;
}
}
主視窗.xaml
<Window>
<!-- Bind to a nested view model of the MainViewModel -->
<MyUserControl DataContext="{Binding ViewModel2}" />
</Window>
視圖模型1.cs
class ViewModel1 : IViewModel1
{
private IRepository Repository { get; }
private IViewModel2Factory ViewModel2Factory { get; }
public ViewModel1(IRepository repository, IViewModel2Factory viewModel2Factory)
{
this.Repository = repository; // private read-only for internal use only
this.ViewModel2Factory = viewModel2Factory;
}
public void CreateViewModel2()
{
IViewModel2 newTab = this.ViewModel2Factory.Create(this.User);
}
}
ViewModel2.cs
class ViewModel2 : IViewModel2
{
public ViewModel2(
IRepository repository,
IDependency1 dependency1,
IDependency2 dependency2,
User user)
{
...
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/399811.html
上一篇:更改DataContext以進行ControlTemplate處理
下一篇:如何傳遞物件來處理輸出事件
