c# - 使用 PRISM 与 WPF 应用程序中的区域对话

标签 c# wpf mvvm dialog prism

我正在使用 PRISM/Unity 和 MVVM 方法开发 WPF 应用程序。到目前为止一切都很好,但我想我有一个概念上的问题。

因为我需要模态对话框,所以我实现了一个对话框服务,它被注入(inject)到 View 模型中,可以用来打开一个带有相应 View 的模态对话框。此对话框需要可导航( = 包含一个区域),以便我能够在对话框内切换 View 。为此,该服务创建一个窗口实例并导航到 DialogRegion 中请求的 View 。这也有效。

我现在的问题是,有时我需要打开第二个模态对话框,该对话框也需要可导航。如果我使用我的对话服务,我会得到对话窗口的一个新实例并抛出异常,因为该区域已经在区域管理器中注册(它在对话窗口的代码后面完成)。这是有道理的,因为 regionmanager 将无法区分区域,但会阻止我打开第二个可导航对话框。

什么是更好的方法?我只能考虑为该区域创建一个具有不同名称的第二个对话窗口,但这似乎是一个非常丑陋的解决方案......


编辑 1: 显示对话框的代码:

dialogService.ShowDialog<MyViewModel>();

对话框服务中显示对话框的代码:

public void ShowDialog<TDialogViewModel>() where TDialogViewModel : class
{
    IModalDialog dialog = ServiceLocator.Current.TryResolve<IModalDialog>();
    dialog.Owner = Application.Current.MainWindow;
    regionManager.RequestNavigate(typeof(TDialogViewModel), Regions.DialogRegion);
    dialog.ShowDialog();
}

对话窗口中的代码:

public DialogWindow(IRegionManager regionManager) : this()
{
    RegionManager.SetRegionManager(this, regionManager);
    this.regionManager = regionManager;
}
private void Window_Unloaded(object sender, RoutedEventArgs e)
{
    regionManager.Regions.Remove(Regions.DialogRegion);
    regionManager.Regions.Remove(Regions.DialogStatusRegion);
}

View 中用于构造、导航和显示其他对话框的代码

public MyViewModel(IRegionManager regionManager, IModalDialogService dialogService) : this()
{
    this.regionManager = regionManager;
    this.dialogService = dialogService;
}
void NavigateToSecondView()
{
    regionManager.RequestNavigate<MyViewModel2>(Regions.DialogRegion);
}
void ShowDialog2()
{
    // This is where a second dialog window with MyViewModel3 should be shown
    dialogService.ShowDialog<MyViewModel3>();
}

编辑 2: 窗口的 XAML:

<Window.Resources>
    <DataTemplate DataType="{x:Type localModels:MyViewModel}">
        <localViewsProject:MyView/>
    </DataTemplate>
</Window.Resources>
<Grid>
    <ContentControl Grid.Row="0" x:Name="DialogContent" Margin="10,10,10,10" 
                    prismRegions:RegionManager.RegionName="{x:Static navi:Regions.DialogRegion}">
    </ContentControl>
</Grid>

编辑 3 - IModalDialog 的代码: 我在网络上的某个地方得到了 IModalDialog 的想法,但我不记得在哪里。这个想法是有一个由一些窗口实例实现的接口(interface),并允许与用户交互。我的对话只有一个内容区域(加上状态/进度通知区域)可以通过 Prism 填充。

这里有一些代码来解释这个想法:

public partial class DialogWindow : Window, IModalDialog
{
    internal IRegionManager regionManager;

    public DialogWindow()
    {
        InitializeComponent();
    }

    public DialogWindow(IRegionManager regionManager)
        : this()
    {
        RegionManager.SetRegionManager(this, regionManager);
        this.regionManager = regionManager;
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // Load Model, when window is loaded
        BaseViewModel model = this.DialogContent.Content as BaseViewModel;
        if (model != null)
        {
            model.LoadData();
        }
    }

    private void Window_Unloaded(object sender, RoutedEventArgs e)
    {
        regionManager.Regions.Remove(Regions.DialogRegion);
        regionManager.Regions.Remove(Regions.DialogStatusRegion);
    }
}

所以为了使用它,我有一个静态的 ModalDialogService,它接收一个模型和一些在对话框关闭后要完成的操作。像这样:

public void ShowDialog<TDialogViewModel>(Action<TDialogViewModel> initializeAction = null, Action<TDialogViewModel> actionAfterClose = null) where TDialogViewModel : class
{
    IModalDialog dialog = ServiceLocator.Current.TryResolve<IModalDialog>();
    if (actionAfterClose != null)
    {
        WeakEventManager<IModalDialog, EventArgs>.AddHandler(
                dialog,
                "Closed",
                (sender, e) => dialogView_Closed(sender, e, typeof(TDialogViewModel), Regions.DialogRegion, actionAfterClose));
        }

        dialog.Owner = Application.Current.MainWindow;
        regionManager.RequestNavigate(typeof(TDialogViewModel), Regions.DialogRegion);

        TDialogViewModel model = null;
        var region = regionManager.Regions[Regions.DialogRegion];

        foreach (var view in region.ActiveViews)
        {
            if (view is TDialogViewModel)
            {
                model = (TDialogViewModel)view;
                if (initializeAction != null)
                {
                    initializeAction(model);
                }
            }
        }

        // Attach an event listener to the closing event in order to prevent closing
        // if the form is not in a closeable state.
        if (typeof(IConfirmNavigationRequest).IsAssignableFrom(typeof(TDialogViewModel)))
        {
            WeakEventManager<IModalDialog, CancelEventArgs>.AddHandler(
                dialog,
                "Closing",
                (sender, e) => dialogView_Closing(sender, e, Regions.DialogRegion));
        }

        // This event can be published by a viewmodel in order to close the dialog
        eventAggregator.GetEvent<DialogCloseRequestedEvent>().Subscribe((containingDialog) =>
        {
            if (containingDialog == model)
            {
                dialog.Close();
            }
        });

        dialog.ShowDialog();
    }
}

最佳答案

而不是用 RegionManager 重新注册一个 Region。您可以重用已注册的初始 Region 并创建它的多个实例。这些被称为 Scoped Regions (在创建区域的多个实例部分下)。例如:

// You may need to keep track of these scopes
IRegionManager scopedRegionManager = _regionManager.Regions["DialogRegion"].Add(view, viewName, true);
scopedRegionManager.Navigate(viewName);

编辑

我相信这就像将 DialogWindow 的构造函数更改为这样简单:

public DialogWindow(IRegionManager regionManager) : this()
{
    var scopedRegionManager = regionManager.Regions[Regions.DialogRegion].Add(this, null, true);
    this.regionManager = scopedRegionManager;
}

关于c# - 使用 PRISM 与 WPF 应用程序中的区域对话,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23076316/

相关文章:

c# - 使用 Dapper 将列表插入临时表

wpf - 如何绑定(bind)到 IronPython 中的 ListBox?

c# - 为什么绑定(bind)到转换完全有效?

wpf - 使用 MVVM-Light 的消息传递问题

unit-testing - 为什么我的 ViewModel 不应该依赖于服务的具体实现?

angularjs - AngularJS 中的模型 View ViewModel

c# - ValidationSummary 不显示错误

c# - MySql 每天运行sql方法

c# - 有趣的是,这可能是一个堆栈溢出问题

c# - 非用户界面 "The calling thread cannot access this object because a different thread owns it"