You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Prism框架中关闭承载Region的View后,Region与控件的关联是否丢失?

问题描述

我正在为一款第三方应用开发模块,构建模块主窗口时创建了菜单和内容Region。通过代码创建Content Region后首次打开窗口可正常工作,但关闭窗口再重新打开时,Region与承载控件的关联似乎消失——视图功能正常但无法显示。删除并重新创建Region后恢复正常,希望得到问题原因和解决办法。

相关代码

MyModule 模块代码

public class MyModule : IModule
{
    public static string Name = "Test";
    private readonly IUnityContainer unityContainer;
    private readonly IAppNavigator appNavigator;
    private readonly IShowMessage showMessage;
    private readonly IShellNavigator shellNavigator;

    public MyModule(IUnityContainer unityContainer
        , IShellNavigator shellNavigator
        , IAppNavigator appNavigator
        , IShowMessage showMessage)
    {
        this.unityContainer = unityContainer;
        this.shellNavigator = shellNavigator;
        this.appNavigator = appNavigator;
        this.showMessage = showMessage;
    }

    private string menuHeader = "MyModul";
    private string menuDictionaries = "Słowniki";
    private IRegionManager regionManager;

    public void OnInitialized(IContainerProvider containerProvider)
    { 
        this.regionManager = containerProvider.Resolve<IRegionManager>();
        RegionAdapterMappings regionAdapterMappings = containerProvider.Resolve<RegionAdapterMappings>();
        regionAdapterMappings.RegisterMapping(typeof(AccordionControl), containerProvider.Resolve<DxAccordionControlRegionAdapter>());

        regionManager.RegisterViewWithRegion(RegionNames.AJ_AccordionRegion, typeof(TestModuleMenuView));
        regionManager.RegisterViewWithRegion(RegionNames.AJ_ContentRegion, () => containerProvider.Resolve<ViewA>());
        regionManager.RegisterViewWithRegion(RegionNames.AJ_ContentRegion, typeof(ViewB));

        initializeNavigation();
    }

    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterSingleton<IApplicationCommands, Commands.ApplicationCommands>();
        containerRegistry.RegisterForNavigation<ViewA, ViewAViewModel>();
        containerRegistry.RegisterForNavigation<ViewB, ViewBViewModel>();
    }

    private void initializeNavigation()
    {
        shellNavigator.AddNavigationItem(nameof(menuDictionaries), menuHeader, menuDictionaries, () => ShowDictionaryViewExecuted(), () => CanExecuteDictionaryView(), () => _loggedOn, 1202);
    }

    private bool CanExecuteDictionaryView()
    {
        return _loggedOn;
    }

    private void ShowDictionaryViewExecuted()
    {
        try
        {
            appNavigator.ShowViewAsync(() => unityContainer.Resolve<DictionariesCardIndexView>(), new OpenViewParams()
            {
                ViewType = typeof(DictionariesCardIndexView).ToString(),
                OpenFullScreen = true,
                OpenMaximized = false,
                OpenNewIfExists = false,
                OpenUsingLastFrameParams = true,
                ProgressDelay = 1,
                ShowProgressControl = true,
                Title = menuDictionaries
            });

        }
        catch (Exception ex)
        {
            showMessage.ShowError(ex.Message);
        }
    }
}

DictionariesCardIndexView XAML代码

<UserControl x:Class="Symfonia.PL.PS.RI.HM.Test.Views.DictionariesCardIndexView"
             d:DesignHeight="450" d:DesignWidth="800"
             mc:Ignorable="d">
    <DockPanel>
        <Border Margin="5" DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="1">
            <dxa:AccordionControl Name="AccordionControl"
                                  Width="Auto"
                                  prism:RegionManager.RegionName="{x:Static regions:RegionNames.AJ_AccordionRegion}"
                                  AutoExpandAllItems="True"
                                  ChildrenPath="Items"
                                  CollapsedItemDisplayMode="Content"
                                  CompactNavigation="False"
                                  HeaderContent="{Binding SelectedRootItem.Header, RelativeSource={RelativeSource Self}}"
                                  IsCollapseButtonVisible="False"
                                  SelectedItemChanged="AccordionControl_OnSelectedItemChanged"
                                  SelectedRootItemChanged="AccordionControl_OnSelectedRootItemChanged"
                                  RootItemDisplayMode="Group"
                                  ShowHeader="False"
                                  SelectionUnit="SubItem" 
                                  ViewMode="Accordion"
                                  RootItemExpandButtonPosition="None"
                                  ExpandItemOnHeaderClick="False"
                                   >
            </dxa:AccordionControl>
        </Border>
        <ContentControl x:Name="ContentControl_AJ"/>
    </DockPanel>
</UserControl>

DictionariesCardIndexView 后台代码

using DevExpress.Xpf.Accordion;
using Prism.Regions;
using Symfonia.PL.PS.RI.HM.Test.Interfaces;
using Symfonia.PL.PS.RI.HM.Test.Navigations;
using Symfonia.PL.PS.RI.HM.Test.ViewModels;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace Symfonia.PL.PS.RI.HM.Test.Views
{
    public partial class DictionariesCardIndexView : UserControl
    {
        private readonly IRegionManager _regionManager;
        private readonly IApplicationCommands _applicationCommands;
        public DictionariesCardIndexView(DictionariesCardIndexViewModel dictionariesCardIndexViewModel, IApplicationCommands applicationCommands,
            IRegionManager regionManager)
        {
            InitializeComponent();
            _applicationCommands = applicationCommands;
            _regionManager = regionManager;
            SetRegionManager(regionManager, this.ContentControl_AJ, Regions.RegionNames.AJ_ContentRegion);
            DataContext = dictionariesCardIndexViewModel;
        }

        void SetRegionManager(IRegionManager regionManager, DependencyObject regionTarget, string regionName)
        {
            //if (regionManager.Regions.ContainsRegionWithName(regionName))
            //{
            //    IRegion region = regionManager.Regions[regionName];
            //    regionManager.Regions.Remove(regionName);
            //}
            RegionManager.SetRegionName(regionTarget, regionName);
            RegionManager.SetRegionManager(regionTarget, regionManager);
        }
    }
}
问题原因与解决方案

核心原因

Prism的RegionManager是单例对象,首次创建AJ_ContentRegion并关联到ContentControl后,关闭窗口时该ContentControl会被销毁,但RegionManager中仍保留着这个Region的注册信息。再次打开窗口时,尝试将新的ContentControl关联到同名Region,Prism不会自动将新控件与已存在的Region重新绑定,导致视图无法渲染到新控件上。

直接解决方案

修改SetRegionManager方法,在绑定新控件前清理旧Region的注册信息:

void SetRegionManager(IRegionManager regionManager, DependencyObject regionTarget, string regionName)
{
    if (regionManager.Regions.ContainsRegionWithName(regionName))
    {
        var existingRegion = regionManager.Regions[regionName];
        // 先移除旧Region中的所有视图(避免内存泄漏)
        foreach (var view in existingRegion.Views.ToList())
        {
            existingRegion.Remove(view);
        }
        // 从RegionManager中移除旧Region
        regionManager.Regions.Remove(regionName);
    }
    // 将新控件绑定到Region
    RegionManager.SetRegionName(regionTarget, regionName);
    RegionManager.SetRegionManager(regionTarget, regionManager);
}

优化建议

  1. 使用Scoped Region(作用域Region):如果窗口支持多实例,建议为每个窗口创建独立的RegionManager,避免全局Region冲突。示例:
var scopedRegionManager = _regionManager.CreateRegionManager();
SetRegionManager(scopedRegionManager, this.ContentControl_AJ, Regions.RegionNames.AJ_ContentRegion);
// 窗口销毁时清理作用域RegionManager
this.Unloaded += (s, e) => scopedRegionManager.Regions.Clear();
  1. XAML直接绑定Region:如果窗口是单实例场景,可直接在ContentControl的XAML中设置prism:RegionManager.RegionName="{x:Static regions:RegionNames.AJ_ContentRegion}",无需通过代码绑定,Prism会自动处理控件与Region的生命周期关联。
  2. 调整视图注册时机:RegisterViewWithRegion建议放在模块的RegisterTypes方法中,或者在窗口每次打开时重新注册,确保视图与Region的关联逻辑正确触发。

内容的提问来源于stack exchange,提问作者Adrian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 00:07:08