当我在WPF MVVM中使用IoC容器来控制窗口时,打开窗口可以工作,但关闭窗口不工作

ghg1uchk  于 2023-06-30  发布在  其他
关注(0)|答案(4)|浏览(96)

我想创建一个界面来控制所有窗口。我用Microsoft.Extensions.DependencyInjection
接口IWindowManager和类WindowManager为:

public interface IWindowManager
    {
        void OpenWindow<TWindow>() where TWindow : Window;
        void CloseWindow<TWindow>() where TWindow : Window;
    }
public class WindowManager : IWindowManager
    {
        private readonly IServiceProvider _serviceProvider;

        public WindowManager(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        public void OpenWindow<TWindow>() where TWindow : Window
        {
            var window = _serviceProvider.GetRequiredService<TWindow>();
            window.Show();
        }

        public void CloseWindow<TWindow>() where TWindow : Window
        {
            var window = _serviceProvider.GetRequiredService<TWindow>();
            window.Close();
        }
    }

在App.xaml.cs中,我创建了IoC容器,它与Microsoft Commuinty文档https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/ioc的代码相同

public partial class App : Application
    {
        public App() 
        {
            Services = ConfigureServices();
        }

        public new static App Current => (App)Application.Current;

        public IServiceProvider Services { get; }

        private static IServiceProvider ConfigureServices()
        {
            var services = new ServiceCollection();
            services.AddSingleton<IWindowManager, WindowManager>();

            services.AddTransient<MainWindowViewModel>();
            services.AddTransient<MainWindow>(
                sp => new MainWindow
                { DataContext = sp.GetService<MainWindowViewModel>() });

            services.AddTransient<TaskWindowViewModel>();
            services.AddTransient<TaskWindow>(
                sp => new TaskWindow
                { DataContext = sp.GetService<TaskWindowViewModel>() });

            return services.BuildServiceProvider();
        }

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            var MW = Services.GetService<MainWindow>();
            MW!.Show();
        }
    }

在MainWindowViewModel.cs中,我通过CommunityToolkit.Mvvm创建了两个RelayCommand方法,并与Xaml中的button绑定:

public partial class MainWindowViewModel : ObservableObject
    {
        private readonly IWindowManager _windowManager;

        #region Windows Manager
        [RelayCommand]
        private void OpenTaskWindow()
        {
            _windowManager.OpenWindow<TaskWindow>();
        }

        [RelayCommand]
        private void CloseMainWindow()
        {
            _windowManager.CloseWindow<MainWindow>();
        }
        #endregion

        public MainWindowViewModel( IWindowManager windowManager)
        {
            _windowManager = windowManager;
        }
    }

我尝试了OpenTaskWindow的方法,但CloseMainWindow不起作用。我不知道怎么了。请帮帮我,谢谢。

qoefvg9y

qoefvg9y1#

当你想用WindowManager打开一个窗口时
1.您从DI-Container

var window = _serviceProvider.GetRequiredService<TWindow>();

获取一个新窗口示例
1.您将显示新窗口示例

window.Show();

当你想用WindowManager关闭一个窗口时
1.从DI-Container

var window = _serviceProvider.GetRequiredService<TWindow>();

中获取一个新窗口示例
1.关闭新窗口示例

window.Close();

这就是错的地方:你总是创建一个新的示例-因为窗口注册为transient-当你想关闭一个已经显示的窗口时关闭它。

kgsdhlau

kgsdhlau2#

我解决了。添加一个字典viewModelWindowMapping来保存windows和viewModels的类型。

private Dictionary<object, Window> windowList = new();

    private Dictionary<Type, Type> viewModelWindowMapping = new()
    {
        { typeof(MainWindowViewModel),typeof(MainWindow)},
        { typeof(TaskWindowViewModel),typeof(TaskWindow)}
    };

    public WindowManager(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void OpenWindow(object viewModel)
    {
        if (viewModel == null)
        {
            throw new ArgumentNullException(nameof(viewModel));
        }

        if (windowList.ContainsKey(viewModel))
        {
            return;
        }

        Window window = GetWindowForViewModel(viewModel);

        windowList[viewModel] = window;

        window.Show();
    }

    public void CloseWindow(object viewModel)
    {
        if (viewModel == null)
        {
            throw new ArgumentNullException(nameof(viewModel));
        }

        if (!windowList.ContainsKey(viewModel))
        {
            return;
        }

        Window window = windowList[viewModel];

        window.Close();

        windowList.Remove(viewModel);
    }

    private Window GetWindowForViewModel(object viewModel)
    {
        var windowType = GetWindowTypeForViewModel(viewModel.GetType());
        return (Window)_serviceProvider.GetRequiredService(windowType);
    }

    private Type GetWindowTypeForViewModel(Type viewModelType)
    {
        if (viewModelWindowMapping.TryGetValue(viewModelType, out var windowType))
        {
            return windowType;
        }
        throw new Exception($"No window type is mapped for the view model type {viewModelType.FullName}");
    }

就像这样,当我想添加另一个窗口。我只需要在viewModelWindowMapping中添加一个Map

5jvtdoz2

5jvtdoz23#

应关闭以前打开的窗口示例。
例如,您可以在WindowManager类中处理此问题,方法是跟踪集合中当前打开的窗口,例如:

public class WindowManager : IWindowManager
{
    private readonly IServiceProvider _serviceProvider;
    private readonly List<Window> _openWindows = List<Window>();

    public WindowManager(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void OpenWindow<TWindow>() where TWindow : Window
    {
        var window = _serviceProvider.GetService<TWindow>();
        if (window != null)
        {
            _openWindows.Add(window);
            window.Show();
        }
    }

    public void CloseWindow<TWindow>() where TWindow : Window
    {
        var window = _openWindows.OfType<TWindow>().FirstOrDefault();
        if (window != null)
        {
            window.Close();
            _openWindows.Remove(window);
        }
    }
}
ebdffaop

ebdffaop4#

我想重写MainWindow类中的CloseWindow方法。

public partial class MainWindow : Window, IWindowManager
{
    public MainWindow()
    {
        InitializeComponent();
    }

    public void CloseWindow() => Close();
}

并且按钮是在MainWindowViewModel类中点击的。

[RelayCommand]
    private void CloseMainWindow(IWindowManager windowManager)
    {
        windowManager.CloseWindow();
    }

它可以工作,但不是我想要的。这意味着我将覆盖每个视图中的CloseWindow方法。我的意图是通过一个界面控制所有窗口的打开或关闭。

相关问题