ОБНОВЛЕНО (17.12.2009): Теперь отражает последний прогресс, который я сделал.
Это первое приложение, которое я и коллега разрабатываем с использованием Prism и MVVM в Silverlight 3.0. Я работаю над оболочкой / фреймворком для проекта, который будет иметь список «плагинов», которые можно добавить в «рабочее пространство».
Плагины регистрируются в классе WorkSpaceManager во время их определенных методов PRISM IModule.Initialize (), например:
workspace.RegisterPlugin(new PluginInfo() { Name = "MyPlugin", ViewType = typeof(MyPluginView), SettingsViewType = null });
Метод RegisterPlugin () просто добавляет объект PluginInfo в словарь с ключом в свойстве «Имя». Затем, когда я хочу добавить плагин в рабочую область, я делаю следующее:
workspace.AddPluginToWorkspace("MyPlugin");
Метод AddPluginToWorkspace класса WorkspaceManager выглядит следующим образом:
public void AddPluginToWorkspace(string pluginName)
{
if (AvailablePlugins.ContainsKey(pluginName))
{
PluginInfo pi = AvailablePlugins[pluginName];
WorkspacePlugin wsp = new WorkspacePlugin();
// Create the View
wsp.View = (Control)this.unityContainer.Resolve(pi.ViewType);
wsp.Name = pi.Name;
// Wire up the CloseCommand to WorkspaceManager's PluginClosing handler
wsp.CloseCommand = new DelegateCommand<WorkspacePlugin>(this.PluginClosing);
// Add the plugin to the active plugins (modules) collection
this.modules.Add(wsp);
// FIX: This should notify anyone listening that the ActivePlugins have changed. When enabled, this causes the same error that will be mentioned further on when attempting to close a plugin.
//this.eventAggregator.GetEvent<ActivePluginsChanged>().Publish(wsp);
}
}
Рабочая область ViewModel просто предоставляет коллекцию модулей службы WorkspaceManager, которая представляет собой текстовый текст представления рабочей области, как показано здесь:
<Grid x:Name="LayoutRoot"
Background="White">
<ListBox x:Name="ModuleListBox"
Grid.Row="1"
rgn:RegionManager.RegionName="Workspace"
Background="Yellow"
ItemsSource="{Binding Plugins}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Template>
<ControlTemplate>
<Grid x:Name="ListBoxGrid">
<ItemsPresenter></ItemsPresenter>
</Grid>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black"
BorderThickness="2"
Margin="0"
Padding="0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="5"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width=".05*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Content="X"
HorizontalAlignment="Right"
Grid.Column="1"
cmd:Click.Command="{Binding CloseCommand}"
cmd:Click.CommandParameter="{Binding}"></Button>
</Grid>
<Border BorderBrush="Black"
BorderThickness="2"
Margin="0"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Grid.Row="1">
<tk:Viewbox Stretch="Uniform"
StretchDirection="Both">
<ContentControl Content="{Binding View}"></ContentControl>
</tk:Viewbox>
</Border>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Обратите внимание на элемент управления Content, который привязан к свойству «View» WorkspacePlugin, и кнопку, у которой Click.Command привязан к «CloseCommand». Это то место, где я застрял изначально, но для по большей части это работает. Представление плагина загружается внутри других элементов управления, и я все еще могу связать команду закрытия (и другие команды, которые будут добавлены позже) с базовой моделью.
Проблема теперь в том, что всякий раз, когда я нажимаю кнопку закрытия и WorkspacePlugin удаляется из коллекции модулей, в ViewModel запускается событие изменения свойства, чтобы сообщить списку об обновлении. Я получаю следующее ошибка (это также происходит, если я раскомментирую строку под комментарием «FIX» выше:
System.ArgumentException: значение не попадает в ожидаемый диапазон.
в MS.Internal.XcpImports.CheckHResult (UInt32 ч)
в MS.Internal.XcpImports.SetValue (INativeCoreTypeWrapper obj, свойство DependencyProperty, DependencyObject doh)
в MS.Internal.XcpImports.SetValue (INativeCoreTypeWrapper doh, свойство DependencyProperty, Object obj)
в System.Windows.DependencyObject.SetObjectValueToCore (DependencyProperty dp, значение объекта)
в System.Windows.DependencyObject.RefreshExpression (DependencyProperty dp)
в System.Windows.Data.BindingExpression.RefreshExpression ()
в System.Windows.Data.BindingExpression.SendDataToTarget ()
в System.Windows.Data.BindingExpression.SourceAquired ()
в System.Windows.Data.BindingExpression.DataContextChanged (Object o, DataContextChangedEventArgs e)
в System.Windows.FrameworkElement.OnDataContextChanged (DataContextChangedEventArgs e)
в System.Windows.FrameworkElement.OnTreeParentUpdated (DependencyObject newParent, логический bIsNewParentAlive)
в System.Windows.DependencyObject.UpdateTreeParent (IManagedPeer oldParent, IManagedPeer newParent, логический bIsNewParentAlive, логический keepReferenceToParent)
в MS.Internal.FrameworkCallbacks.ManagedPeerTreeUpdate (IntPtr oldParentElement, IntPtr parentElement, IntPtr childElement, Байт bIsParentAlive, Байт bKeepReferenceToParent)
Из того, что я нашел, просматривая онлайн, это обычно означает, что визуальный элемент, который уже был добавлен в визуальное дерево, пытается быть добавлен снова. Этот вид делает, поскольку, если у меня есть только 1 плагин, отображаемый и закрывающий его, он исчезает и ошибки нет. Я вполне уверен, что ошибка связана с тем, что свойство WorkspacePlugin.View является визуальным элементом управления, а обновление привязки пытается повторно добавить его в визуальное дерево.
Как я могу обойти это или достичь желаемого результата без сообщения об ошибке?