0

我正在尝试通过一个区域填充ItemsSourcea ComboBox(的衍生物)。ItemsControl

看法

范围RegionManager(在视图模型上找到)通过 分配给视图prism:RegionManager.RegionManager="{Binding RegionManager}"

主窗口.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>

主窗口.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();
        }
    }
}

视图模型

视图模型通过 分配给视图的 DataContext prism:ViewModelLocator.AutoWireViewModel="True"

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" });
        }
    }
}

区域名称.cs

namespace Applications.Testing.Wpf.RegionCreationTester
{
    public static class RegionNames
    {
        public const string itemsControlRegion = "collectionRegion";
    }
}

引导程序

应用程序.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"/>

应用程序.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()我能够获得断点命中的代码中的最后一行是new MainWindow()CreateShell调用 MainWindow 的构造函数退出之后的 in 。为什么我只尝试注册一次时却被告知该区域已经注册?我在 MainWindow 的构造函数中设置了断点,以确实确认它只创建一次,即使不是,RegionManager它的作用域也应该防止发生此异常。我错过了什么?

更新

我刚刚注释掉了其中的代码PopulateItemsControl,发现即使只有一个视图被添加到该区域并且仍然陌生,如果没有向该区域添加视图但该区域被访问(如行中所做的那样var region = RegionManager.Regions[RegionNames.itemsControlRegion];:) . 因此,现在的问题与访问作用域 RegionManager 上的现有区域以进行视图注入有关,以便向其中添加视图;我不确定为什么从 RegionManager 访问一个区域会改变它的状态,这似乎是 Prism 中的一个错误,或者可能与惰性枚举有关。

4

1 回答 1

1

好吧,你做了两次,你只是不知道。当您RegionName在 ComboBox 上设置附加属性时,将附加一个将创建具有给定名称的区域的事件处理程序(这是 的静态部分RegionManager)。当RegionManager您在 VM 中实例化的实例尝试访问区域集合时,索引器首先调用引发事件的类的静态方法。获得创建区域任务的RegionManager全局实例(当您使用附加属性时)尚未完成它的工作 - 当您尝试使用实例访问区域时尚未加载窗口,处理程序尚未删除并且它被调用再次。如果你打电话给你的RegionManagerRegionNamePopulateItemsControl窗口加载后的方法(例如在 MainWindow 的 Loaded 事件处理程序中),您不会收到异常,但您的代码不会按预期工作。那是因为您的实例RegionManager没有“处理”您的 collectionRegion,全局RegionManager是。

注入 RegionManager 实例

如果您需要RegionManagerVM 中的实例,请使用构造函数注入。

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 或您正在使用的任何东西)将IRegionManager在创建 VM 时解析实例(无论如何,PRISM 正在为您完成这项工作,您不会自己实例化它)。

区域范围

RegionManager保留区域的集合,并且不允许具有相同名称的区域。因此,除非您的窗口将包含多个具有名为 的区域的组合框,否则collectionRegionRegionManager全局的)就可以了。如果您collectionRegion将拥有相同视图类的实例,它们都在其自身内定义了另一个区域,那么您需要区域范围 -RegionManager具有这些视图自己范围的实例。在这种情况下,该方法可以为该视图Add创建本地实例:RegionManager

IRegion collectionRegion = this.regionManager.Regions["collectionRegion"];
bool makeRegionManagerScope = true;
IRegionManager localRegionManager =
    collectionRegion.Add(view, null, makeRegionManagerScope);
于 2017-11-20T15:56:28.723 回答