Добавление представлений в ItemsSource элемента ItemsControl через область области управления областью
Я пытаюсь заселить ItemsSource
из ComboBox
(производная от ItemsControl
через регион.
Посмотреть
Сфера действия 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
Модель представления назначается 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" });
}
}
}
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()
, Последняя строка в моем коде, для которой я могу получить точку останова, это new MainWindow()
в CreateShell
после завершения вызова конструктора для MainWindow. Почему мне говорят, что регион уже зарегистрирован, когда я пытаюсь зарегистрировать его только один раз? Я установил точки останова в конструкторе MainWindow, чтобы действительно подтвердить, что он создается только один раз, и даже если это не так, RegionManager
к которой он относится, должно предотвратить возникновение этого исключения. Что я пропустил?
ОБНОВИТЬ
Я только что закомментировал код в PopulateItemsControl
и обнаружил, что исключение выдается, даже если в регион добавлен только один вид, и еще более странный, если в регион не добавлено ни одного вида, но к региону обращаются (как это сделано в строке: var region = RegionManager.Regions[RegionNames.itemsControlRegion];
). Таким образом, теперь проблема заключается в доступе к существующему региону в области RegionManager для представления инъекций, чтобы добавить в него представления; Я не уверен, почему доступ к региону из RegionManager изменил бы его состояние, это похоже на ошибку в Prism или, возможно, что-то связанное с ленивым перечислением.
1 ответ
Ну, вы делаете это дважды, вы просто не знаете. Когда вы установите RegionName
вложенное свойство на вашем ComboBox, прикреплен обработчик событий, который создаст регион с заданным именем (это статическая часть RegionManager
). Когда экземпляр RegionManager
созданный вами в вашей виртуальной машине пытается получить доступ к коллекции регионов, индексатор сначала вызывает статический метод RegionManager
класс, который поднимает событие. Глобальный RegionManager
экземпляр, который получил задачу создания региона (когда вы использовали RegionName
прикрепленное свойство) не завершило свою работу - окно не было загружено при попытке доступа к региону с помощью вашего экземпляра, обработчик не был удален и он вызывается снова. Если вы позвонили PopulateItemsControl
Метод после загрузки окна (скажем, в обработчике события Loaded в MainWindow), вы не получите исключение, но ваш код не будет работать так, как вы ожидаете. Это потому, что ваш экземпляр RegionManager
не "обрабатывает" вашу коллекцию Регион, глобальный RegionManager
является.
Внедрение экземпляра RegionManager
Если вам нужно 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 или что вы используете) будет разрешен IRegionManager
Например, при создании виртуальной машины (PRISM выполняет эту работу за вас, вы сами ее не создаете).
Области применения региона
RegionManager
хранит коллекцию регионов и не разрешает регионы с одинаковыми именами. Итак, если ваше окно не будет иметь несколько из тех ComboBox, которые все имеют регион с именем collectionRegion
, RegionManager
(глобальный) в порядке. Если твой collectionRegion
будет иметь экземпляры одного и того же класса представления, которые все определяют другой регион внутри себя, тогда вам нужны области действия - RegionManager
экземпляры с их собственной областью для этих представлений. В этом случае Add
Метод может создать локальный экземпляр RegionManager
для этого взгляда:
IRegion collectionRegion = this.regionManager.Regions["collectionRegion"];
bool makeRegionManagerScope = true;
IRegionManager localRegionManager =
collectionRegion.Add(view, null, makeRegionManagerScope);