diff --git a/src/Maui/Prism.Maui/Navigation/Regions/RegionViewRegistry.cs b/src/Maui/Prism.Maui/Navigation/Regions/RegionViewRegistry.cs index c3a04bf33..61a27c527 100644 --- a/src/Maui/Prism.Maui/Navigation/Regions/RegionViewRegistry.cs +++ b/src/Maui/Prism.Maui/Navigation/Regions/RegionViewRegistry.cs @@ -14,6 +14,8 @@ namespace Prism.Navigation.Regions; public class RegionViewRegistry : IRegionViewRegistry { private readonly ListDictionary> _registeredContent = new(); + private readonly Dictionary> _registeredViewTypes = new(); + private readonly Dictionary> _registeredTargetNames = new(); private readonly WeakDelegatesManager _contentRegisteredListeners = new(); /// @@ -49,6 +51,11 @@ public IEnumerable GetContents(string regionName, IContainerProvider con /// Content type to be registered for the . public void RegisterViewWithRegion(string regionName, Type viewType) { + if (!TryAddRegisteredViewType(regionName, viewType)) + { + return; + } + RegisterViewWithRegion(regionName, c => { var registry = c.Resolve(); @@ -67,6 +74,11 @@ public void RegisterViewWithRegion(string regionName, Type viewType) /// Content type to be registered for the . public void RegisterViewWithRegion(string regionName, string targetName) { + if (!TryAddRegisteredTargetName(regionName, targetName)) + { + return; + } + RegisterViewWithRegion(regionName, c => { var registry = c.Resolve(); @@ -81,10 +93,50 @@ public void RegisterViewWithRegion(string regionName, string targetName) /// Delegate used to retrieve the content associated with the . public void RegisterViewWithRegion(string regionName, Func getContentDelegate) { + if (IsContentDelegateRegistered(regionName, getContentDelegate)) + { + return; + } + _registeredContent.Add(regionName, getContentDelegate); OnContentRegistered(new ViewRegisteredEventArgs(regionName, getContentDelegate)); } + private bool TryAddRegisteredViewType(string regionName, Type viewType) + { + if (!_registeredViewTypes.TryGetValue(regionName, out HashSet viewTypes)) + { + viewTypes = new HashSet(); + _registeredViewTypes.Add(regionName, viewTypes); + } + + return viewTypes.Add(viewType); + } + + private bool TryAddRegisteredTargetName(string regionName, string targetName) + { + if (!_registeredTargetNames.TryGetValue(regionName, out HashSet targetNames)) + { + targetNames = new HashSet(); + _registeredTargetNames.Add(regionName, targetNames); + } + + return targetNames.Add(targetName); + } + + private bool IsContentDelegateRegistered(string regionName, Func getContentDelegate) + { + foreach (var existingDelegate in _registeredContent[regionName]) + { + if (existingDelegate == getContentDelegate) + { + return true; + } + } + + return false; + } + private void OnContentRegistered(ViewRegisteredEventArgs e) { try diff --git a/src/Wpf/Prism.Wpf/Navigation/Regions/RegionManager.cs b/src/Wpf/Prism.Wpf/Navigation/Regions/RegionManager.cs index d618967ba..1d2a542ec 100644 --- a/src/Wpf/Prism.Wpf/Navigation/Regions/RegionManager.cs +++ b/src/Wpf/Prism.Wpf/Navigation/Regions/RegionManager.cs @@ -111,6 +111,48 @@ private static void OnSetRegionNameCallback(DependencyObject element, Dependency if (!IsInDesignMode(element)) { CreateRegion(element); + RegisterDefaultViewWithRegion(element); + } + } + + private static void OnDefaultViewChanged(DependencyObject element, DependencyPropertyChangedEventArgs args) + { + if (!IsInDesignMode(element) && args.NewValue != null) + { + RegisterDefaultViewWithRegion(element, args.NewValue); + } + } + + private static void RegisterDefaultViewWithRegion(DependencyObject element) + { + var defaultView = GetDefaultView(element); + if (defaultView != null) + { + RegisterDefaultViewWithRegion(element, defaultView); + } + } + + private static void RegisterDefaultViewWithRegion(DependencyObject element, object defaultView) + { + var regionName = GetRegionName(element); + if (string.IsNullOrEmpty(regionName)) + { + return; + } + + var regionViewRegistry = ContainerLocator.Container.Resolve(); + + if (defaultView is string targetName) + { + regionViewRegistry.RegisterViewWithRegion(regionName, targetName); + } + else if (defaultView is Type viewType) + { + regionViewRegistry.RegisterViewWithRegion(regionName, viewType); + } + else + { + regionViewRegistry.RegisterViewWithRegion(regionName, _ => defaultView); } } @@ -167,6 +209,52 @@ public static void SetRegionManager(DependencyObject target, IRegionManager valu target.SetValue(RegionManagerProperty, value); } + /// + /// Identifies the DefaultView attached property. + /// + /// + /// Sets the default view to be displayed in a region when it is created. + /// This can be a view name, a type, or an instance of the view. + /// +#if !AVALONIA + public static readonly DependencyProperty DefaultViewProperty = + DependencyProperty.RegisterAttached("DefaultView", typeof(object), typeof(RegionManager), + new PropertyMetadata(defaultValue: null, propertyChangedCallback: OnDefaultViewChanged)); +#else + public static readonly AvaloniaProperty DefaultViewProperty = + AvaloniaProperty.RegisterAttached("DefaultView", typeof(RegionManager)); +#endif + + /// + /// Sets the attached property for the specified region target. + /// + /// The object that will host the default view. + /// + /// The default view to display in the region. This can be a view name, a type, or an instance of the view. + /// + public static void SetDefaultView(DependencyObject regionTarget, object viewNameTypeOrInstance) + { + if (regionTarget == null) + throw new ArgumentNullException(nameof(regionTarget)); + + regionTarget.SetValue(DefaultViewProperty, viewNameTypeOrInstance); + } + + /// + /// Gets the value of the attached property for the specified region target. + /// + /// The object that hosts the default view. + /// + /// The default view associated with the region. This can be a view name, a type, or an instance of the view. + /// + public static object GetDefaultView(DependencyObject regionTarget) + { + if (regionTarget == null) + throw new ArgumentNullException(nameof(regionTarget)); + + return regionTarget.GetValue(DefaultViewProperty); + } + /// /// Identifies the RegionContext attached property. /// @@ -272,6 +360,7 @@ static RegionManager() // TODO: Could this go into the default constructor? RegionNameProperty.Changed.Subscribe(args => OnSetRegionNameCallback(args?.Sender, args)); RegionContextProperty.Changed.Subscribe(args => OnRegionContextChanged(args?.Sender, args)); + DefaultViewProperty.Changed.Subscribe(args => OnDefaultViewChanged(args?.Sender, args)); } #endif diff --git a/src/Wpf/Prism.Wpf/Navigation/Regions/RegionViewRegistry.cs b/src/Wpf/Prism.Wpf/Navigation/Regions/RegionViewRegistry.cs index 404653ef6..b457e467a 100644 --- a/src/Wpf/Prism.Wpf/Navigation/Regions/RegionViewRegistry.cs +++ b/src/Wpf/Prism.Wpf/Navigation/Regions/RegionViewRegistry.cs @@ -13,6 +13,8 @@ public class RegionViewRegistry : IRegionViewRegistry { private readonly IContainerProvider _container; private readonly ListDictionary> _registeredContent = new ListDictionary>(); + private readonly Dictionary> _registeredViewTypes = new Dictionary>(); + private readonly Dictionary> _registeredTargetNames = new Dictionary>(); private readonly WeakDelegatesManager _contentRegisteredListeners = new WeakDelegatesManager(); /// @@ -57,6 +59,11 @@ public IEnumerable GetContents(string regionName, IContainerProvider con /// Content type to be registered for the . public void RegisterViewWithRegion(string regionName, Type viewType) { + if (!TryAddRegisteredViewType(regionName, viewType)) + { + return; + } + RegisterViewWithRegion(regionName, _ => CreateInstance(viewType)); } @@ -67,6 +74,11 @@ public void RegisterViewWithRegion(string regionName, Type viewType) /// Delegate used to retrieve the content associated with the . public void RegisterViewWithRegion(string regionName, Func getContentDelegate) { + if (IsContentDelegateRegistered(regionName, getContentDelegate)) + { + return; + } + _registeredContent.Add(regionName, getContentDelegate); OnContentRegistered(new ViewRegisteredEventArgs(regionName, getContentDelegate)); } @@ -79,8 +91,15 @@ public void RegisterViewWithRegion(string regionName, FuncThe name of the region to associate the view with. /// The type of the view to register with the /// The , for adding several views easily - public void RegisterViewWithRegion(string regionName, string targetName) => + public void RegisterViewWithRegion(string regionName, string targetName) + { + if (!TryAddRegisteredTargetName(regionName, targetName)) + { + return; + } + RegisterViewWithRegion(regionName, c => c.Resolve(targetName)); + } /// /// Creates an instance of a registered view . @@ -94,6 +113,41 @@ protected virtual object CreateInstance(Type type) return view; } + private bool TryAddRegisteredViewType(string regionName, Type viewType) + { + if (!_registeredViewTypes.TryGetValue(regionName, out HashSet viewTypes)) + { + viewTypes = new HashSet(); + _registeredViewTypes.Add(regionName, viewTypes); + } + + return viewTypes.Add(viewType); + } + + private bool TryAddRegisteredTargetName(string regionName, string targetName) + { + if (!_registeredTargetNames.TryGetValue(regionName, out HashSet targetNames)) + { + targetNames = new HashSet(); + _registeredTargetNames.Add(regionName, targetNames); + } + + return targetNames.Add(targetName); + } + + private bool IsContentDelegateRegistered(string regionName, Func getContentDelegate) + { + foreach (Func existingDelegate in _registeredContent[regionName]) + { + if (existingDelegate == getContentDelegate) + { + return true; + } + } + + return false; + } + private void OnContentRegistered(ViewRegisteredEventArgs e) { try diff --git a/tests/Avalonia/Prism.Avalonia.Tests/Regions/Behaviors/AutoPopulateRegionBehaviorFixture.cs b/tests/Avalonia/Prism.Avalonia.Tests/Regions/Behaviors/AutoPopulateRegionBehaviorFixture.cs index 8693d7749..b07a93d9f 100644 --- a/tests/Avalonia/Prism.Avalonia.Tests/Regions/Behaviors/AutoPopulateRegionBehaviorFixture.cs +++ b/tests/Avalonia/Prism.Avalonia.Tests/Regions/Behaviors/AutoPopulateRegionBehaviorFixture.cs @@ -11,6 +11,10 @@ namespace Prism.Avalonia.Tests.Regions.Behaviors { public class AutoPopulateRegionBehaviorFixture { + private class MockContentObject + { + } + [Fact] public void ShouldGetViewsFromRegistryOnAttach() { @@ -64,6 +68,27 @@ public void NullRegionThrows() } + [Fact] + public void WhenSameViewTypeRegisteredTwice_RegionContainsSingleView() + { + var containerMock = new Mock(); + ContainerLocator.SetContainerExtension(containerMock.Object); + containerMock.Setup(c => c.Resolve(typeof(MockContentObject))).Returns(new MockContentObject()); + var registry = new RegionViewRegistry(containerMock.Object); + registry.RegisterViewWithRegion("MyRegion", typeof(MockContentObject)); + registry.RegisterViewWithRegion("MyRegion", typeof(MockContentObject)); + + var region = new Region { Name = "MyRegion" }; + var behavior = new AutoPopulateRegionBehavior(registry) + { + Region = region + }; + + behavior.Attach(); + + Assert.Single(region.Views); + } + [Fact] public void CanAttachBeforeSettingName() { diff --git a/tests/Avalonia/Prism.Avalonia.Tests/Regions/RegionManagerFixture.cs b/tests/Avalonia/Prism.Avalonia.Tests/Regions/RegionManagerFixture.cs index 255ecfc60..072e22842 100644 --- a/tests/Avalonia/Prism.Avalonia.Tests/Regions/RegionManagerFixture.cs +++ b/tests/Avalonia/Prism.Avalonia.Tests/Regions/RegionManagerFixture.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Threading.Tasks; +using Avalonia.Controls; using Moq; using Prism.Avalonia.Tests.Mocks; using Prism.Ioc; using Prism.Navigation.Regions; +using Prism.Navigation.Regions.Behaviors; using Xunit; namespace Prism.Avalonia.Tests.Regions @@ -448,6 +450,115 @@ public void CanAddRegionToRegionManager() Assert.Equal("region", region.Name); } + [Fact] + public void SettingDefaultViewType_RegistersViewWithRegion() + { + try + { + var mockRegionContentRegistry = new MockRegionContentRegistry(); + string regionName = null; + Type viewType = null; + mockRegionContentRegistry.RegisterContentWithViewType = (name, type) => + { + regionName = name; + viewType = type; + return null; + }; + + var containerMock = new Mock(); + containerMock.Setup(c => c.Resolve(typeof(IRegionViewRegistry))).Returns(mockRegionContentRegistry); + containerMock.Setup(c => c.Resolve(typeof(DelayedRegionCreationBehavior))) + .Returns(() => new DelayedRegionCreationBehavior(CreateRegionAdapterMappings())); + ContainerLocator.SetContainerExtension(containerMock.Object); + + var host = new ContentControl(); + RegionManager.SetRegionName(host, "MyRegion"); + RegionManager.SetDefaultView(host, typeof(object)); + + Assert.Equal("MyRegion", regionName); + Assert.Equal(typeof(object), viewType); + } + finally + { + ContainerLocator.ResetContainer(); + } + } + + [Fact] + public void SettingDefaultViewBeforeRegionName_RegistersWhenRegionNameIsSet() + { + try + { + var mockRegionContentRegistry = new MockRegionContentRegistry(); + string regionName = null; + Type viewType = null; + mockRegionContentRegistry.RegisterContentWithViewType = (name, type) => + { + regionName = name; + viewType = type; + return null; + }; + + var containerMock = new Mock(); + containerMock.Setup(c => c.Resolve(typeof(IRegionViewRegistry))).Returns(mockRegionContentRegistry); + containerMock.Setup(c => c.Resolve(typeof(DelayedRegionCreationBehavior))) + .Returns(() => new DelayedRegionCreationBehavior(CreateRegionAdapterMappings())); + ContainerLocator.SetContainerExtension(containerMock.Object); + + var host = new ContentControl(); + RegionManager.SetDefaultView(host, typeof(object)); + RegionManager.SetRegionName(host, "MyRegion"); + + Assert.Equal("MyRegion", regionName); + Assert.Equal(typeof(object), viewType); + } + finally + { + ContainerLocator.ResetContainer(); + } + } + + [Fact] + public void SettingDefaultViewTwiceWithDuplicateRegistry_RegionAutoPopulatesSingleView() + { + try + { + var containerMock = new Mock(); + ContainerLocator.SetContainerExtension(containerMock.Object); + containerMock.Setup(c => c.Resolve(typeof(MockContentObject))).Returns(new MockContentObject()); + containerMock.Setup(c => c.Resolve(typeof(DelayedRegionCreationBehavior))) + .Returns(() => new DelayedRegionCreationBehavior(CreateRegionAdapterMappings())); + var registry = new RegionViewRegistry(containerMock.Object); + containerMock.Setup(c => c.Resolve(typeof(IRegionViewRegistry))).Returns(registry); + + var host = new ContentControl(); + RegionManager.SetRegionName(host, "MyRegion"); + RegionManager.SetDefaultView(host, typeof(MockContentObject)); + RegionManager.SetDefaultView(host, typeof(MockContentObject)); + + var region = new Region { Name = "MyRegion" }; + var behavior = new AutoPopulateRegionBehavior(registry) { Region = region }; + behavior.Attach(); + + Assert.Single(region.Views); + } + finally + { + ContainerLocator.ResetContainer(); + } + } + + private class MockContentObject + { + } + + private static RegionAdapterMappings CreateRegionAdapterMappings() + { + var mappings = new RegionAdapterMappings(); + mappings.RegisterMapping(typeof(ContentControl), new ContentControlRegionAdapter(null)); + return mappings; + } + [Fact] public void ShouldThrowIfRegionNameArgumentIsDifferentToRegionNameProperty() { diff --git a/tests/Avalonia/Prism.Avalonia.Tests/Regions/RegionViewRegistryFixture.cs b/tests/Avalonia/Prism.Avalonia.Tests/Regions/RegionViewRegistryFixture.cs index 7db7ce8ce..cad9b8251 100644 --- a/tests/Avalonia/Prism.Avalonia.Tests/Regions/RegionViewRegistryFixture.cs +++ b/tests/Avalonia/Prism.Avalonia.Tests/Regions/RegionViewRegistryFixture.cs @@ -12,6 +12,39 @@ namespace Prism.Avalonia.Tests.Regions; public class RegionViewRegistryFixture { + [Fact] + public void RegisterViewWithRegion_DuplicateTypeRegistration_ReturnsSingleView() + { + var containerMock = new Mock(); + ContainerLocator.SetContainerExtension(containerMock.Object); + containerMock.Setup(c => c.Resolve(typeof(MockContentObject))).Returns(new MockContentObject()); + var registry = new RegionViewRegistry(containerMock.Object); + + registry.RegisterViewWithRegion("MyRegion", typeof(MockContentObject)); + registry.RegisterViewWithRegion("MyRegion", typeof(MockContentObject)); + + var result = registry.GetContents("MyRegion"); + + Assert.Single(result); + } + + [Fact] + public void RegisterViewWithRegion_DuplicateTargetNameRegistration_ReturnsSingleView() + { + var containerMock = new Mock(); + ContainerLocator.SetContainerExtension(containerMock.Object); + var content = new MockContentObject(); + containerMock.Setup(c => c.Resolve(typeof(object), "MyView")).Returns(content); + var registry = new RegionViewRegistry(containerMock.Object); + + registry.RegisterViewWithRegion("MyRegion", "MyView"); + registry.RegisterViewWithRegion("MyRegion", "MyView"); + + var result = registry.GetContents("MyRegion"); + + Assert.Single(result); + } + [Fact] public void CanRegisterContentAndRetrieveIt() { diff --git a/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Regions/RegionFixture.cs b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Regions/RegionFixture.cs index 2a61ad91e..8a2d315d1 100644 --- a/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Regions/RegionFixture.cs +++ b/tests/Maui/Prism.DryIoc.Maui.Tests/Fixtures/Regions/RegionFixture.cs @@ -32,6 +32,31 @@ public void ContentRegion_CreatedBy_RequestNavigate() Assert.IsType(page.ContentRegion.Content.BindingContext); } + [Fact] + public void FrameRegion_DuplicateRegisterViewWithRegion_ContainsSingleView() + { + var mauiApp = CreateBuilder(prism => + prism.RegisterTypes(container => + { + container.RegisterForNavigation(); + container.RegisterForRegionNavigation(); + }) + .OnInitialized(container => + { + var regionManager = container.Resolve(); + regionManager.RegisterViewWithRegion("FrameRegion", "MockRegionViewA"); + regionManager.RegisterViewWithRegion("FrameRegion", "MockRegionViewA"); + }) + .CreateWindow("MockContentRegionPage")) + .Build(); + var window = GetWindow(mauiApp); + + Assert.IsType(window.Page); + var page = window.Page as MockContentRegionPage; + Assert.NotNull(page.FrameRegion.Content); + Assert.IsType(page.FrameRegion.Content); + } + [Fact] public void FrameRegion_CreatedBy_RegisterViewWithRegion() { diff --git a/tests/Uno/Prism.Uno.WinUI.Tests/Regions/RegionManagerDefaultViewFixture.cs b/tests/Uno/Prism.Uno.WinUI.Tests/Regions/RegionManagerDefaultViewFixture.cs new file mode 100644 index 000000000..016193b8d --- /dev/null +++ b/tests/Uno/Prism.Uno.WinUI.Tests/Regions/RegionManagerDefaultViewFixture.cs @@ -0,0 +1,55 @@ +using Microsoft.UI.Xaml.Controls; +using Moq; +using Prism.Ioc; +using Prism.Navigation.Regions; +using Prism.Navigation.Regions.Behaviors; +using Xunit; + +namespace Prism.Uno.WinUI.Tests.Regions; + +public class RegionManagerDefaultViewFixture +{ + private class MockContentObject + { + } + + [Fact(Skip = "Requires a real Uno/WinUI runtime; unit tests run against ref assemblies.")] + public void SettingDefaultViewType_RegistersViewWithRegion() + { + var mockRegistry = new Mock(); + var containerMock = new Mock(); + containerMock.Setup(c => c.Resolve(typeof(IRegionViewRegistry))).Returns(mockRegistry.Object); + containerMock.Setup(c => c.Resolve(typeof(DelayedRegionCreationBehavior))) + .Returns(new DelayedRegionCreationBehavior(new RegionAdapterMappings())); + ContainerLocator.SetContainerExtension(containerMock.Object); + + var host = new ContentControl(); + RegionManager.SetRegionName(host, "MyRegion"); + RegionManager.SetDefaultView(host, typeof(MockContentObject)); + + mockRegistry.Verify(r => r.RegisterViewWithRegion("MyRegion", typeof(MockContentObject)), Times.Once); + } + + [Fact(Skip = "Requires a real Uno/WinUI runtime; unit tests run against ref assemblies.")] + public void SettingDefaultViewTwice_RegistersOnceDueToDuplicateGuard() + { + var containerMock = new Mock(); + ContainerLocator.SetContainerExtension(containerMock.Object); + containerMock.Setup(c => c.Resolve(typeof(MockContentObject))).Returns(new MockContentObject()); + containerMock.Setup(c => c.Resolve(typeof(DelayedRegionCreationBehavior))) + .Returns(new DelayedRegionCreationBehavior(new RegionAdapterMappings())); + var registry = new RegionViewRegistry(containerMock.Object); + containerMock.Setup(c => c.Resolve(typeof(IRegionViewRegistry))).Returns(registry); + + var host = new ContentControl(); + RegionManager.SetRegionName(host, "MyRegion"); + RegionManager.SetDefaultView(host, typeof(MockContentObject)); + RegionManager.SetDefaultView(host, typeof(MockContentObject)); + + var region = new Region { Name = "MyRegion" }; + var behavior = new AutoPopulateRegionBehavior(registry) { Region = region }; + behavior.Attach(); + + Assert.Single(region.Views); + } +} diff --git a/tests/Uno/Prism.Uno.WinUI.Tests/Regions/RegionViewRegistryFixture.cs b/tests/Uno/Prism.Uno.WinUI.Tests/Regions/RegionViewRegistryFixture.cs new file mode 100644 index 000000000..840a49470 --- /dev/null +++ b/tests/Uno/Prism.Uno.WinUI.Tests/Regions/RegionViewRegistryFixture.cs @@ -0,0 +1,48 @@ +using System; +using System.Linq; +using Moq; +using Prism.Ioc; +using Prism.Navigation.Regions; +using Xunit; + +namespace Prism.Uno.WinUI.Tests.Regions; + +public class RegionViewRegistryFixture +{ + private class MockContentObject + { + } + + [Fact] + public void RegisterViewWithRegion_DuplicateTypeRegistration_ReturnsSingleView() + { + var containerMock = new Mock(); + ContainerLocator.SetContainerExtension(containerMock.Object); + containerMock.Setup(c => c.Resolve(typeof(MockContentObject))).Returns(new MockContentObject()); + var registry = new RegionViewRegistry(containerMock.Object); + + registry.RegisterViewWithRegion("MyRegion", typeof(MockContentObject)); + registry.RegisterViewWithRegion("MyRegion", typeof(MockContentObject)); + + var result = registry.GetContents("MyRegion"); + + Assert.Single(result); + } + + [Fact] + public void RegisterViewWithRegion_DuplicateTargetNameRegistration_ReturnsSingleView() + { + var containerMock = new Mock(); + ContainerLocator.SetContainerExtension(containerMock.Object); + var content = new MockContentObject(); + containerMock.Setup(c => c.Resolve(typeof(object), "MyView")).Returns(content); + var registry = new RegionViewRegistry(containerMock.Object); + + registry.RegisterViewWithRegion("MyRegion", "MyView"); + registry.RegisterViewWithRegion("MyRegion", "MyView"); + + var result = registry.GetContents("MyRegion"); + + Assert.Single(result); + } +} diff --git a/tests/Wpf/Prism.Wpf.Tests/Regions/Behaviors/AutoPopulateRegionBehaviorFixture.cs b/tests/Wpf/Prism.Wpf.Tests/Regions/Behaviors/AutoPopulateRegionBehaviorFixture.cs index 4b8b3784d..82643234b 100644 --- a/tests/Wpf/Prism.Wpf.Tests/Regions/Behaviors/AutoPopulateRegionBehaviorFixture.cs +++ b/tests/Wpf/Prism.Wpf.Tests/Regions/Behaviors/AutoPopulateRegionBehaviorFixture.cs @@ -12,6 +12,10 @@ namespace Prism.Wpf.Tests.Regions.Behaviors public class AutoPopulateRegionBehaviorFixture { + private class MockContentObject + { + } + [Fact] public void ShouldGetViewsFromRegistryOnAttach() { @@ -65,6 +69,27 @@ public void NullRegionThrows() } + [Fact] + public void WhenSameViewTypeRegisteredTwice_RegionContainsSingleView() + { + var containerMock = new Mock(); + ContainerLocator.SetContainerExtension(containerMock.Object); + containerMock.Setup(c => c.Resolve(typeof(MockContentObject))).Returns(new MockContentObject()); + var registry = new RegionViewRegistry(containerMock.Object); + registry.RegisterViewWithRegion("MyRegion", typeof(MockContentObject)); + registry.RegisterViewWithRegion("MyRegion", typeof(MockContentObject)); + + var region = new Region { Name = "MyRegion" }; + var behavior = new AutoPopulateRegionBehavior(registry) + { + Region = region + }; + + behavior.Attach(); + + Assert.Single(region.Views); + } + [Fact] public void CanAttachBeforeSettingName() { diff --git a/tests/Wpf/Prism.Wpf.Tests/Regions/RegionManagerFixture.cs b/tests/Wpf/Prism.Wpf.Tests/Regions/RegionManagerFixture.cs index 32aacd63b..28f8b609c 100644 --- a/tests/Wpf/Prism.Wpf.Tests/Regions/RegionManagerFixture.cs +++ b/tests/Wpf/Prism.Wpf.Tests/Regions/RegionManagerFixture.cs @@ -5,6 +5,7 @@ using Moq; using Prism.Ioc; using Prism.Navigation.Regions; +using Prism.Navigation.Regions.Behaviors; using Prism.Wpf.Tests.Mocks; using Xunit; @@ -449,6 +450,108 @@ public void CanAddRegionToRegionManager() Assert.Equal("region", region.Name); } + [StaFact] + public void SettingDefaultViewType_RegistersViewWithRegion() + { + try + { + var mockRegionContentRegistry = new MockRegionContentRegistry(); + string regionName = null; + Type viewType = null; + mockRegionContentRegistry.RegisterContentWithViewType = (name, type) => + { + regionName = name; + viewType = type; + return null; + }; + + var containerMock = new Mock(); + containerMock.Setup(c => c.Resolve(typeof(IRegionViewRegistry))).Returns(mockRegionContentRegistry); + containerMock.Setup(c => c.Resolve(typeof(DelayedRegionCreationBehavior))) + .Returns(new DelayedRegionCreationBehavior(new RegionAdapterMappings())); + ContainerLocator.SetContainerExtension(containerMock.Object); + + var host = new System.Windows.Controls.ContentControl(); + RegionManager.SetRegionName(host, "MyRegion"); + RegionManager.SetDefaultView(host, typeof(object)); + + Assert.Equal("MyRegion", regionName); + Assert.Equal(typeof(object), viewType); + } + finally + { + ContainerLocator.ResetContainer(); + } + } + + [StaFact] + public void SettingDefaultViewBeforeRegionName_RegistersWhenRegionNameIsSet() + { + try + { + var mockRegionContentRegistry = new MockRegionContentRegistry(); + string regionName = null; + Type viewType = null; + mockRegionContentRegistry.RegisterContentWithViewType = (name, type) => + { + regionName = name; + viewType = type; + return null; + }; + + var containerMock = new Mock(); + containerMock.Setup(c => c.Resolve(typeof(IRegionViewRegistry))).Returns(mockRegionContentRegistry); + containerMock.Setup(c => c.Resolve(typeof(DelayedRegionCreationBehavior))) + .Returns(new DelayedRegionCreationBehavior(new RegionAdapterMappings())); + ContainerLocator.SetContainerExtension(containerMock.Object); + + var host = new System.Windows.Controls.ContentControl(); + RegionManager.SetDefaultView(host, typeof(object)); + RegionManager.SetRegionName(host, "MyRegion"); + + Assert.Equal("MyRegion", regionName); + Assert.Equal(typeof(object), viewType); + } + finally + { + ContainerLocator.ResetContainer(); + } + } + + [StaFact] + public void SettingDefaultViewTwiceWithDuplicateRegistry_RegionAutoPopulatesSingleView() + { + try + { + var containerMock = new Mock(); + ContainerLocator.SetContainerExtension(containerMock.Object); + containerMock.Setup(c => c.Resolve(typeof(MockContentObject))).Returns(new MockContentObject()); + containerMock.Setup(c => c.Resolve(typeof(DelayedRegionCreationBehavior))) + .Returns(new DelayedRegionCreationBehavior(new RegionAdapterMappings())); + var registry = new RegionViewRegistry(containerMock.Object); + containerMock.Setup(c => c.Resolve(typeof(IRegionViewRegistry))).Returns(registry); + + var host = new System.Windows.Controls.ContentControl(); + RegionManager.SetRegionName(host, "MyRegion"); + RegionManager.SetDefaultView(host, typeof(MockContentObject)); + RegionManager.SetDefaultView(host, typeof(MockContentObject)); + + var region = new Region { Name = "MyRegion" }; + var behavior = new AutoPopulateRegionBehavior(registry) { Region = region }; + behavior.Attach(); + + Assert.Single(region.Views); + } + finally + { + ContainerLocator.ResetContainer(); + } + } + + private class MockContentObject + { + } + [Fact] public void ShouldThrowIfRegionNameArgumentIsDifferentToRegionNameProperty() { diff --git a/tests/Wpf/Prism.Wpf.Tests/Regions/RegionViewRegistryFixture.cs b/tests/Wpf/Prism.Wpf.Tests/Regions/RegionViewRegistryFixture.cs index 106f8885f..64486baba 100644 --- a/tests/Wpf/Prism.Wpf.Tests/Regions/RegionViewRegistryFixture.cs +++ b/tests/Wpf/Prism.Wpf.Tests/Regions/RegionViewRegistryFixture.cs @@ -13,6 +13,39 @@ namespace Prism.Wpf.Tests.Regions public class RegionViewRegistryFixture { + [Fact] + public void RegisterViewWithRegion_DuplicateTypeRegistration_ReturnsSingleView() + { + var containerMock = new Mock(); + ContainerLocator.SetContainerExtension(containerMock.Object); + containerMock.Setup(c => c.Resolve(typeof(MockContentObject))).Returns(new MockContentObject()); + var registry = new RegionViewRegistry(containerMock.Object); + + registry.RegisterViewWithRegion("MyRegion", typeof(MockContentObject)); + registry.RegisterViewWithRegion("MyRegion", typeof(MockContentObject)); + + var result = registry.GetContents("MyRegion"); + + Assert.Single(result); + } + + [Fact] + public void RegisterViewWithRegion_DuplicateTargetNameRegistration_ReturnsSingleView() + { + var containerMock = new Mock(); + ContainerLocator.SetContainerExtension(containerMock.Object); + var content = new MockContentObject(); + containerMock.Setup(c => c.Resolve(typeof(object), "MyView")).Returns(content); + var registry = new RegionViewRegistry(containerMock.Object); + + registry.RegisterViewWithRegion("MyRegion", "MyView"); + registry.RegisterViewWithRegion("MyRegion", "MyView"); + + var result = registry.GetContents("MyRegion"); + + Assert.Single(result); + } + [Fact] public void CanRegisterContentAndRetrieveIt() {