Vis*_*hal 2 c# wpf xaml prism mef
我有一个名为ModuleMenu的模块.在这个模块中,我有一个名为MenuView的UserControl和一个名为UserControlViewModel的Corresponding ViewModel.我还有一个名为Module的类.所有代码如下:
MenuView.xmal
<UserControl ..............>
<ListBox ItemsSource="{Binding MenuItems, Converter={StaticResource dummy}}" DisplayMemberPath="MenuItemName" SelectedItem="{Binding SelectedMenuItem}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Margin" Value="10,0" />
</Style>
</ListBox.Resources>
</ListBox>
</UserControl>
Run Code Online (Sandbox Code Playgroud)
MenuView.xaml.cs
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class MenuView : UserControl
{
public MenuView()
{
InitializeComponent();
}
}
Run Code Online (Sandbox Code Playgroud)
UserControlViewModel.cs
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class MenuViewModel : ViewModelBase
{
IServiceFactory _ServiceFactory;
[ImportingConstructor]
public MenuViewModel(IServiceFactory serviceFactory)
{
_ServiceFactory = serviceFactory;
}
protected override void OnViewLoaded()
{
_MenuItems = new ObservableCollection<MenuItem>();
WithClient<IMenuItemService>(_ServiceFactory.CreateClient<IMenuItemService>(), menuItemClient =>
{
MenuItem[] menuItems = menuItemClient.GetAllParentMenuItemsWithChildren();
if (menuItems != null)
{
foreach (MenuItem menuItem in menuItems)
{
_MenuItems.Add(menuItem);
}
_SelectedMenuItem = _MenuItems[2];
}
});
}
private ObservableCollection<MenuItem> _MenuItems;
public ObservableCollection<MenuItem> MenuItems
{
get
{
return _MenuItems;
}
set
{
if (_MenuItems != value)
{
_MenuItems = value;
OnPropertyChanged(() => MenuItems, false);
}
}
}
private MenuItem _SelectedMenuItem;
public MenuItem SelectedMenuItem
{
get
{
return _SelectedMenuItem;
}
set
{
if (_SelectedMenuItem != value)
{
_SelectedMenuItem = value;
OnPropertyChanged(() => SelectedMenuItem);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Module.cs
[ModuleExport(typeof(Module), InitializationMode=InitializationMode.WhenAvailable)]
public class Module : IModule
{
IRegionManager _regionManager;
[ImportingConstructor]
public Module(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void Initialize()
{
_regionManager.Regions[RegionNames.MenubarRegion].Add(ServiceLocator.Current.GetInstance<MenuView>());
}
}
Run Code Online (Sandbox Code Playgroud)
现在在我的主项目中,我有一个名为BootStrapper.cs的类,如下所示:
public class Bootstrapper : MefBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.GetExportedValue<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)Shell;
App.Current.MainWindow.Show();
}
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(ModuleMenu.Module).Assembly));
}
}
Run Code Online (Sandbox Code Playgroud)
在我的App.xaml中:
<Application ..............>
<Application.Resources>
<DataTemplate DataType="{x:Type modMenu:MenuViewModel}">
<modMenu:MenuView />
</DataTemplate>
</Application.Resources>
</Application>
Run Code Online (Sandbox Code Playgroud)
最后在App.xaml.cs中:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
}
Run Code Online (Sandbox Code Playgroud)
当我运行应用程序时,我按预期获得shell.它显示了我的MenuView,但未加载MenuView中的数据.我尝试使用虚拟转换器调试它,并显示viewModel永远不会被初始化.
那么,现在我的问题是如何初始化viewModel?
更新:
在尝试您的代码后,我得到如下异常:
尝试将视图添加到区域'MenubarRegion'时发生异常.
- The most likely causing exception was was:
'Microsoft.Practices.ServiceLocation.ActivationException: Activation
error occured while trying to get instance of type MenuView, key "" --->
Microsoft.Practices.ServiceLocation.ActivationException: Activation
error occured while trying to get instance of type MenuView, key ""
at Microsoft.Practices.Prism.MefExtensions.MefServiceLocatorAdapter
.DoGetInstance(Type serviceType, String key)
at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase
.GetInstance(Type serviceType, String key)
--- End of inner exception stack trace ---
at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase
.GetInstance(Type serviceType, String key)
at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase
.GetInstance(Type serviceType)
at Microsoft.Practices.Prism.Regions.RegionViewRegistry
.CreateInstance(Type type)
at Microsoft.Practices.Prism.Regions.RegionViewRegistry
.<>c__DisplayClass1.<RegisterViewWithRegion>b__0()
at Microsoft.Practices.Prism.Regions.Behaviors
.AutoPopulateRegionBehavior
.OnViewRegistered(Object sender, ViewRegisteredEventArgs e)'.
But also check the InnerExceptions for more detail or call
.GetRootException().
Run Code Online (Sandbox Code Playgroud)
当我看一下内部异常时,我收到以下错误消息:
{"Activation error occured while trying to get instance of type MenuView, key \"\""}
Run Code Online (Sandbox Code Playgroud)
UPDATE2:
这是ServiceFactory类型的Export:
[Export(typeof(IServiceFactory))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ServiceFactory : IServiceFactory
{
public T CreateClient<T>() where T : IServiceContract
{
return ObjectBase.Container.GetExportedValue<T>();
}
}
Run Code Online (Sandbox Code Playgroud)
您将DataTemplateview 定义为viewmodel的视图,但实际上并未使用它.
使用Prism有很多方法可以解决您的问题,请参阅此主题.
您可以DataContext在XAML中设置视图的属性:
<UserControl.DataContext>
<my:MyViewModel/>
</UserControl.DataContext>
Run Code Online (Sandbox Code Playgroud)
您可以在视图的构造函数中创建一个viewmodel:
public MyView()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
Run Code Online (Sandbox Code Playgroud)
更好的方法是通过依赖注入导入viewmodel:
[ImportingConstructor]
public MyView(MyViewModel viewModel)
{
InitializeComponent();
this.DataContext = viewModel;
}
Run Code Online (Sandbox Code Playgroud)
您可以使用Prism的viewmodel位置服务:
<MyView prism:ViewModelLocator.AutoWireViewModel="True"/>
Run Code Online (Sandbox Code Playgroud)
最后但并非最不重要:您也可以使用DataTemplate,但您应该设置一个DataContext对象让WPF为您创建视图,而不是在代码中创建视图.
更新:
所以,你想使用该DataTemplate功能.嗯,这不是"棱镜"做事的方式,因为在这种情况下,视图将由WPF创建,而不是由Prism创建IRegion.但无论如何这都是可能的.
我将解释其中的区别:在所有其他(Prism)方法中,视图是'master',它将首先创建,然后将创建一个适当的viewmodel并附加到视图.在Prism中,您可以定义应创建的视图,何时(导航)和位置(区域).在该DataTemplate方法中,viewmodel(data)是'master',它将首先创建,WPF确定如何显示它们创建视图.所以在这种情况下,你不能使用Prism的区域和导航,因为WPF处理所有的事情.
所以我真的建议你使用上面的任何方法,但不是DataTemplate一个.
您可以按如下方式创建视图:
_regionManager.Regions[RegionNames.MenubarRegion].Add(ServiceLocator.Current.GetInstance<MenuView>());
Run Code Online (Sandbox Code Playgroud)
我建议你把它改成:
_regionManager.RegisterViewWithRegion(RegionNames.MenubarRegion, typeof(MenuView));
Run Code Online (Sandbox Code Playgroud)
ServiceLocator直接使用不是一个好的模式(除非你无法避免),所以让Prism为你实例化视图.
在视图的构造函数中,只需添加viewmodel的依赖项注入,并将视图设置DataContext为它.瞧!你有它.
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class MenuView : UserControl
{
[ImportingConstructor]
public MenuView(MenuViewModel viewModel)
{
this.InitializeComponent();
this.DataContext = viewModel;
}
}
Run Code Online (Sandbox Code Playgroud)
使用DataTemplate,你只能用'旧的普通WPF方式'.您必须手动创建viewmodel的实例并将其作为父(shell的)viewmodel的属性公开(或使其静态,但这是一种糟糕的方法).
<Window>
<ContentControl Content="{Binding MenuViewModelInstace}"/>
</Window>
Run Code Online (Sandbox Code Playgroud)
然后,WPF将为您创建视图以显示视图模型,但您应该直接在XAML标记中定义它,如前所述.棱镜无法帮助你.