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); }
优化建议
- 使用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();
- XAML直接绑定Region:如果窗口是单实例场景,可直接在ContentControl的XAML中设置
prism:RegionManager.RegionName="{x:Static regions:RegionNames.AJ_ContentRegion}",无需通过代码绑定,Prism会自动处理控件与Region的生命周期关联。 - 调整视图注册时机:
RegisterViewWithRegion建议放在模块的RegisterTypes方法中,或者在窗口每次打开时重新注册,确保视图与Region的关联逻辑正确触发。
内容的提问来源于stack exchange,提问作者Adrian
相关产品推荐
相关产品推荐

