通过 Scoped RegionManager 的区域将视图添加到 ItemsControl 的 ItemsSource
Adding Views to the ItemsSource of an ItemsControl via a Region of a Scoped RegionManager
我正在尝试通过区域填充 ComboBox
(ItemsControl
的衍生物)的 ItemsSource
。
查看
作用域 RegionManager
(在视图模型中找到)通过 prism:RegionManager.RegionManager="{Binding RegionManager}"
分配给视图。
MainWindow.xaml
<Window x:Class="Applications.Testing.Wpf.RegionCreationTester.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Applications.Testing.Wpf.RegionCreationTester"
xmlns:prism="http://www.codeplex.com/prism"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
prism:RegionManager.RegionManager="{Binding RegionManager}"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<ComboBox prism:RegionManager.RegionName="{x:Static local:RegionNames.itemsControlRegion}"/>
</Grid>
</Window>
MainWindow.xaml.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, IModelled<MainWindowViewModel>
{
public MainWindowViewModel ViewModel
{
get
{
return (MainWindowViewModel)DataContext;
}
set
{
DataContext = value;
}
}
public MainWindow()
{
InitializeComponent();
ViewModel.PopulateItemsControl();
}
}
}
ViewModel
视图模型通过 prism:ViewModelLocator.AutoWireViewModel="True"
分配给视图的 DataContext。
MainWindowViewModel.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
public class MainWindowViewModel
{
/// <summary>
/// Gets the <see cref="RegionManager"/> scoped to this control.
/// </summary>
/// <remarks>Exists so that child controls can register regions for their own child controls which are also child controls in this control.</remarks>
public RegionManager RegionManager { get; } = new RegionManager();
/// <summary>
/// Adds some child views to the <see cref="RegionNames.itemsControlRegion"/>.
/// </summary>
/// <remarks>Normally these views would be resolved using an IoC container but this have been omitted for brevity.</remarks>
public void PopulateItemsControl()
{
var region = RegionManager.Regions[RegionNames.itemsControlRegion];
region.Add(new TextBlock { Text = "Item #1" });
region.Add(new Button { Content = "Item #2" });
}
}
}
RegionNames.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
public static class RegionNames
{
public const string itemsControlRegion = "collectionRegion";
}
}
引导程序
App.xaml
<Application x:Class="Applications.Testing.Wpf.RegionCreationTester.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"/>
App.xaml.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
new RegionCreationTesterBootstrapper().Run();
}
}
}
RegionCreationTesterBootstrapper.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
public class RegionCreationTesterBootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
=> new MainWindow();
protected override void InitializeShell()
{
base.InitializeShell();
(Application.Current.MainWindow = (Window)Shell).Show();
}
}
}
一旦所有区域人口都发生并且应用程序即将 运行 我得到一个 Prism.Regions.UpdateRegionsException
包含一个 InnerException 消息 "Region with the given name is already registered: collectionRegion"
在 App
new RegionCreationTesterBootstrapper().Run()
个。在调用 MainWindow 的构造函数退出后,我的代码中的最后一行是 CreateShell
中的 new MainWindow()
断点。为什么我只尝试注册一次,却被告知该地区已经注册?我已经在 MainWindow 的构造函数中设置了断点,以确实确认它只被创建一次,即使它没有被创建,它所限定的 RegionManager
应该可以防止这个异常发生。我错过了什么?
更新
我刚刚注释掉了PopulateItemsControl
中的代码,发现即使只有一个视图被添加到该区域也会抛出异常,而且更奇怪的是,如果没有视图被添加到该区域但该区域被访问(如行中所做的那样:var region = RegionManager.Regions[RegionNames.itemsControlRegion];
)。因此,现在的问题是访问作用域 RegionManager 上的现有区域以进行视图注入,以便向其添加视图;我不确定为什么从 RegionManager 访问一个区域会改变它的状态,这似乎是 Prism 中的一个错误,或者可能与惰性枚举有关。
好吧,你做了两次,你只是没有意识到。当您在 ComboBox 上设置 RegionName
attached 属性 时,将附加一个事件处理程序来创建具有给定名称的区域(这是 RegionManager
的静态部分)。当您在 VM 中实例化的 RegionManager
实例尝试访问区域集合时,索引器首先调用 RegionManager
class 上的 static 方法,该方法引发事件。获得创建区域任务的全局 RegionManager
实例(当您使用 RegionName
附加 属性 时)尚未完成它的工作 - window 在您使用时尚未加载尝试使用您的实例访问该区域,处理程序尚未被删除并再次调用。如果您在 window 加载后调用了 PopulateItemsControl
方法(比如在 MainWindow 的 Loaded 事件处理程序中),您不会得到异常,但您的代码不会按预期工作。那是因为您的 RegionManager
实例没有“处理”您的 collectionRegion,全局 RegionManager
是。
正在注入 RegionManager 实例
如果您的 VM 中需要 RegionManager
实例,请使用构造函数注入。
public class MainWindowViewModel : BindableBase
{
private IRegionManager rm;
public MainWindowViewModel(IRegionManager manager)
{
this.rm = manager;
}
public void PopulateItemsControl()
{
var region = rm.Regions[RegionNames.itemsControlRegion];
region.Add(new TextBlock { Text = "Item #1" });
}
}
依赖注入容器(Unity 或您正在使用的任何东西)将在创建 VM 时解析 IRegionManager
实例(无论如何 PRISM 都会为您完成这项工作,您不会自己实例化它)。
区域范围
RegionManager
保留区域的集合,不允许具有相同名称的区域。因此,除非您的 window 将有多个 ComboBox,它们都有一个名为 collectionRegion
的区域,否则 RegionManager
(全局)就可以了。如果您的 collectionRegion
将拥有相同视图 class 的实例,它们都在其自身内定义了另一个区域,那么您需要区域范围 - RegionManager
具有这些视图自己范围的实例。在这种情况下,Add
方法可以为该视图创建 RegionManager
的本地实例:
IRegion collectionRegion = this.regionManager.Regions["collectionRegion"];
bool makeRegionManagerScope = true;
IRegionManager localRegionManager =
collectionRegion.Add(view, null, makeRegionManagerScope);
我正在尝试通过区域填充 ComboBox
(ItemsControl
的衍生物)的 ItemsSource
。
查看
作用域 RegionManager
(在视图模型中找到)通过 prism:RegionManager.RegionManager="{Binding RegionManager}"
分配给视图。
MainWindow.xaml
<Window x:Class="Applications.Testing.Wpf.RegionCreationTester.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Applications.Testing.Wpf.RegionCreationTester"
xmlns:prism="http://www.codeplex.com/prism"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
prism:RegionManager.RegionManager="{Binding RegionManager}"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<ComboBox prism:RegionManager.RegionName="{x:Static local:RegionNames.itemsControlRegion}"/>
</Grid>
</Window>
MainWindow.xaml.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, IModelled<MainWindowViewModel>
{
public MainWindowViewModel ViewModel
{
get
{
return (MainWindowViewModel)DataContext;
}
set
{
DataContext = value;
}
}
public MainWindow()
{
InitializeComponent();
ViewModel.PopulateItemsControl();
}
}
}
ViewModel
视图模型通过 prism:ViewModelLocator.AutoWireViewModel="True"
分配给视图的 DataContext。
MainWindowViewModel.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
public class MainWindowViewModel
{
/// <summary>
/// Gets the <see cref="RegionManager"/> scoped to this control.
/// </summary>
/// <remarks>Exists so that child controls can register regions for their own child controls which are also child controls in this control.</remarks>
public RegionManager RegionManager { get; } = new RegionManager();
/// <summary>
/// Adds some child views to the <see cref="RegionNames.itemsControlRegion"/>.
/// </summary>
/// <remarks>Normally these views would be resolved using an IoC container but this have been omitted for brevity.</remarks>
public void PopulateItemsControl()
{
var region = RegionManager.Regions[RegionNames.itemsControlRegion];
region.Add(new TextBlock { Text = "Item #1" });
region.Add(new Button { Content = "Item #2" });
}
}
}
RegionNames.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
public static class RegionNames
{
public const string itemsControlRegion = "collectionRegion";
}
}
引导程序
App.xaml
<Application x:Class="Applications.Testing.Wpf.RegionCreationTester.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"/>
App.xaml.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
new RegionCreationTesterBootstrapper().Run();
}
}
}
RegionCreationTesterBootstrapper.cs
namespace Applications.Testing.Wpf.RegionCreationTester
{
public class RegionCreationTesterBootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
=> new MainWindow();
protected override void InitializeShell()
{
base.InitializeShell();
(Application.Current.MainWindow = (Window)Shell).Show();
}
}
}
一旦所有区域人口都发生并且应用程序即将 运行 我得到一个 Prism.Regions.UpdateRegionsException
包含一个 InnerException 消息 "Region with the given name is already registered: collectionRegion"
在 App
new RegionCreationTesterBootstrapper().Run()
个。在调用 MainWindow 的构造函数退出后,我的代码中的最后一行是 CreateShell
中的 new MainWindow()
断点。为什么我只尝试注册一次,却被告知该地区已经注册?我已经在 MainWindow 的构造函数中设置了断点,以确实确认它只被创建一次,即使它没有被创建,它所限定的 RegionManager
应该可以防止这个异常发生。我错过了什么?
更新
我刚刚注释掉了PopulateItemsControl
中的代码,发现即使只有一个视图被添加到该区域也会抛出异常,而且更奇怪的是,如果没有视图被添加到该区域但该区域被访问(如行中所做的那样:var region = RegionManager.Regions[RegionNames.itemsControlRegion];
)。因此,现在的问题是访问作用域 RegionManager 上的现有区域以进行视图注入,以便向其添加视图;我不确定为什么从 RegionManager 访问一个区域会改变它的状态,这似乎是 Prism 中的一个错误,或者可能与惰性枚举有关。
好吧,你做了两次,你只是没有意识到。当您在 ComboBox 上设置 RegionName
attached 属性 时,将附加一个事件处理程序来创建具有给定名称的区域(这是 RegionManager
的静态部分)。当您在 VM 中实例化的 RegionManager
实例尝试访问区域集合时,索引器首先调用 RegionManager
class 上的 static 方法,该方法引发事件。获得创建区域任务的全局 RegionManager
实例(当您使用 RegionName
附加 属性 时)尚未完成它的工作 - window 在您使用时尚未加载尝试使用您的实例访问该区域,处理程序尚未被删除并再次调用。如果您在 window 加载后调用了 PopulateItemsControl
方法(比如在 MainWindow 的 Loaded 事件处理程序中),您不会得到异常,但您的代码不会按预期工作。那是因为您的 RegionManager
实例没有“处理”您的 collectionRegion,全局 RegionManager
是。
正在注入 RegionManager 实例
如果您的 VM 中需要 RegionManager
实例,请使用构造函数注入。
public class MainWindowViewModel : BindableBase
{
private IRegionManager rm;
public MainWindowViewModel(IRegionManager manager)
{
this.rm = manager;
}
public void PopulateItemsControl()
{
var region = rm.Regions[RegionNames.itemsControlRegion];
region.Add(new TextBlock { Text = "Item #1" });
}
}
依赖注入容器(Unity 或您正在使用的任何东西)将在创建 VM 时解析 IRegionManager
实例(无论如何 PRISM 都会为您完成这项工作,您不会自己实例化它)。
区域范围
RegionManager
保留区域的集合,不允许具有相同名称的区域。因此,除非您的 window 将有多个 ComboBox,它们都有一个名为 collectionRegion
的区域,否则 RegionManager
(全局)就可以了。如果您的 collectionRegion
将拥有相同视图 class 的实例,它们都在其自身内定义了另一个区域,那么您需要区域范围 - RegionManager
具有这些视图自己范围的实例。在这种情况下,Add
方法可以为该视图创建 RegionManager
的本地实例:
IRegion collectionRegion = this.regionManager.Regions["collectionRegion"];
bool makeRegionManagerScope = true;
IRegionManager localRegionManager =
collectionRegion.Add(view, null, makeRegionManagerScope);