更新(2009年12月17日):现在反映了我所取得的最新进展。
这是我和他的同事在Silverlight 3.0中使用Prism和MVVM开发的第一个应用程序。我正在为项目开发一个shell /框架,它将有一个可以添加到“工作区”的“插件”列表。
插件在其特定的PRISM IModule.Initialize()方法中注册了WorkSpaceManager类,如下所示:
workspace.RegisterPlugin(new PluginInfo() { Name = "MyPlugin", ViewType = typeof(MyPluginView), SettingsViewType = null });
RegisterPlugin()方法只是将PluginInfo对象添加到键入“Name”属性的字典中。然后,当我想在工作区中添加插件时,我会执行以下操作:
workspace.AddPluginToWorkspace("MyPlugin");
WorkspaceManager类的AddPluginToWorkspace方法如下所示:
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);
}
}
Workspace ViewModel只是公开了WorkspaceManager服务的模块集合,它是Workspace View的datacontext,如下所示:
<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>
请注意绑定到WorkspacePlugin的“View”属性的Content控件以及将Click.Command绑定到“CloseCommand”的Button。这是我最初被困的地方但是在大多数情况下,这是有效的。插件的视图被加载到其他控件中,我仍然可以将close命令(以及稍后要添加的其他命令)绑定到底层模型。
现在的问题是每当我点击关闭按钮并从模块集合中删除WorkspacePlugin时,会在ViewModel上触发属性更改事件以让列表框知道更新我得到了跟随错误(如果我取消注释上面“FIX”注释下面的行,也会发生这种情况:
System.ArgumentException:值不在预期范围内。 在MS.Internal.XcpImports.CheckHResult(UInt32 hr) 在MS.Internal.XcpImports.SetValue(INativeCoreTypeWrapper obj,DependencyProperty属性,DependencyObject doh) 在MS.Internal.XcpImports.SetValue(INativeCoreTypeWrapper doh,DependencyProperty属性,Object obj) 在System.Windows.DependencyObject.SetObjectValueToCore(DependencyProperty dp,Object value) 在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,Boolean bIsNewParentAlive) 在System.Windows.DependencyObject.UpdateTreeParent(IManagedPeer oldParent,IManagedPeer newParent,Boolean bIsNewParentAlive,Boolean keepReferenceToParent) 在MS.Internal.FrameworkCallbacks.ManagedPeerTreeUpdate(IntPtr oldParentElement,IntPtr parentElement,IntPtr childElement,Byte bIsParentAlive,Byte bKeepReferenceToParent)
通过在线查看我收集的内容,这通常意味着已经添加到可视树中的可视元素正在尝试再次添加。这种情况因为如果我只显示1个插件并关闭它,它就会消失并且没有错误。我很确定该错误是由于WorkspacePlugin.View属性是一个可视控件而绑定更新正在尝试将其重新添加到可视树中。
如何在没有错误消息的情况下解决此问题或获得所需结果?
答案 0 :(得分:0)
您可能不应该同时使用Prism Region以及通过ItemsSource将视图绑定到ListView。一般人们选择其中一个。我想你可能会看到一些奇怪的行为,因为你有两个。
我建议您的模块在其初始化方法中提供“插件”。
public MyModule : IModule
{
IRegionManager _mgr;
public MyModule(IRegionManager mgr)
{
_mgr = mgr;
}
public void Initialize()
{
_mgr.RegisterViewWithRegion("Workspace", typeof(MyPlugin));
}
}
你应该可以在此之后的第二天打电话。您不应该从shell收集并提供插件集合到您想要显示它们的区域...您应该让您的模块自己将它们贡献给该区域。这将使您能够保持一定程度的抽象,让您的模块更加自主。
祝你好运。答案 1 :(得分:0)
我最终得到了以下工作:
我创建了一个WorkspaceItemView视图和ViewModel,它看起来大致如下:
<UserControl>
<Grid x:Name="ResizeGrid"
MouseEnter="Plugin_MouseEnter"
MouseLeave="Plugin_MouseLeave">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*" />
<RowDefinition Height="5" />
</Grid.RowDefinitions>
<Border x:Name="border"
BorderBrush="Black"
BorderThickness="2"
Padding="0"
Margin="-1,-1,-1,-1">
</Border>
<Grid x:Name="grid"
Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width=".05*" />
</Grid.ColumnDefinitions>
<Thumb HorizontalAlignment="Stretch"
Background="Green"
DragDelta="Thumb_DragDelta"
/>
<Button Content="X"
HorizontalAlignment="Right"
Grid.Column="1"
cmd:Click.Command="{Binding CloseCommand}"
cmd:Click.CommandParameter="{Binding PluginID}" />
</Grid>
<Border BorderBrush="Black"
BorderThickness="2"
Margin="0"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Grid.Row="1">
<tk:Viewbox Stretch="Uniform"
StretchDirection="Both">
<ContentControl rgn:RegionManager.RegionName="PluginViewRegion" />
</tk:Viewbox>
</Border>
<Thumb x:Name="SizeGrip"
Grid.Row="1"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Width="10"
Height="10"
Margin="0,0,-7,-7"
Style="{StaticResource SizeGrip}"
DragDelta="SizeGrip_DragDelta"
DragStarted="SizeGrip_DragStarted"
DragCompleted="SizeGrip_DragCompleted" />
</Grid>
</UserControl>
public class WorkspaceItemViewModel : INotifyPropertyChanged
{
private IWorkspaceManager workspaceManager;
private IRegionManager regionManager;
private Guid pluginID;
public WorkspaceItemViewModel(IWorkspaceManager workspaceManager, IRegionManager regionManager)
{
this.workspaceManager = workspaceManager;
this.regionManager = regionManager;
}
public DelegateCommand<object> CloseCommand
{
get
{
return workspaceManager.CloseCommand;
}
}
public DelegateCommand<object> SelectCommand
{
get
{
return workspaceManager.SelectCommand;
}
}
public object CloseCommandParameter
{
get
{
return this;
}
}
public Guid PluginID
{
get
{
return this.pluginID;
}
set
{
this.pluginID = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("PluginID"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
将插件添加到工作区的WorkspaceManager代码如下所示:
public void AddPluginToWorkspace(string pluginName)
{
PluginInfo pi = AvailablePlugins[pluginName];
WorkspacePlugin wsp = new WorkspacePlugin();
wsp.Name = pi.Name;
wsp.CloseCommand = new DelegateCommand<object>(this.PluginClosing);
wsp.SelectCommand = new DelegateCommand<object>(this.PluginSelected);
wsp.id = System.Guid.NewGuid();
this.modules.Add(wsp.id, wsp);
var view = this.unityContainer.Resolve(pluginWindowType);
if (view is IWorkspacePlugin)
{
var iwsp = view as IWorkspacePlugin;
if (iwsp != null)
{
iwsp.PluginID = wsp.id;
}
}
else
{
throw new ArgumentException("Plugin view containers must implement IWorkspacePlugin.");
}
var workspaceRegion = regionManager.Regions["Workspace"];
var pluginRegion = workspaceRegion.Add(view, wsp.id.ToString(), true);
this.unityContainer.RegisterInstance<IRegionManager>(wsp.id.ToString(), pluginRegion);
pluginRegion.Regions["PluginViewRegion"].Context = view;
pluginRegion.Regions["PluginViewRegion"].Add(this.unityContainer.Resolve(pi.ViewType));
this.eventAggregator.GetEvent<ActivePluginsChanged>().Publish(wsp);
这实质上创建了一个范围区域,将WorkspaceItemView添加到工作区区域,然后解析并将实际插件的视图添加到新添加的WorkspaceItemView的PluginViewRegion中。我有一些清理工作要做,但我认为它运作良好。
感谢您的所有帮助。