如何使用MEF初始化viewModel? [英] How to initialize viewModel using MEF?

查看:58
本文介绍了如何使用MEF初始化viewModel?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个名为ModuleMenu的模块.在此模块中,我有一个称为MenuView的UserControl和一个名为UserControlViewModel的对应ViewModel.我也有一个叫做模块的类.所有代码如下:

I have a Module called ModuleMenu. In this module I have a UserControl called MenuView and a Corresponding ViewModel called UserControlViewModel. I also have a class called Module. All the code is as given below:

MenuView.xmal

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>

MenuView.xaml.cs

MenuView.xaml.cs

[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class MenuView : UserControl
{
    public MenuView()
    {
        InitializeComponent();
    }
}

UserControlViewModel.cs

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

}

Module.cs

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

现在在我的主项目中,有一个名为BootStrapper.cs的类,如下所示:

Now in my main Project I have a class called BootStrapper.cs as follows:

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

在我的App.xaml中:

In my App.xaml:

<Application ..............>
    <Application.Resources>
        <DataTemplate DataType="{x:Type modMenu:MenuViewModel}">
            <modMenu:MenuView />
        </DataTemplate>
    </Application.Resources>
</Application>

最后是App.xaml.cs:

And finally in App.xaml.cs:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        Bootstrapper bootstrapper = new Bootstrapper();
        bootstrapper.Run();

    }
}

当我运行应用程序时,得到的外壳符合预期.它向我显示了MenuView,但是没有加载MenuView中的数据.我尝试使用虚拟转换器调试它,并显示viewModel从未初始化.

When I run the application I get shell as expected. It shows me MenuView, but the data in MenuView is not loaded. I tried to debug it using a dummy converter and show that the viewModel is never initialized.

所以,现在我的问题是如何初始化viewModel?

So, now my question is how can I initialize the viewModel?

更新:

尝试您的代码后,出现如下异常:

After trying your code I get exception as follows:

尝试将视图添加到"MenubarRegion"区域时发生异常.

An exception has occurred while trying to add a view to region '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().

当我查看内部异常时,出现以下错误消息:

When I take a look at inner exception, I get the following error message:

{"Activation error occured while trying to get instance of type MenuView, key \"\""}

更新2:

这是ServiceFactory类型的导出:

Here is the Export of type ServiceFactory :

[Export(typeof(IServiceFactory))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ServiceFactory : IServiceFactory
{
    public T CreateClient<T>() where T : IServiceContract
    {
        return ObjectBase.Container.GetExportedValue<T>();
    }
}

推荐答案

您将DataTemplate定义为viewmodel的视图,但实际上并未使用它.

You define a DataTemplate as a view for your viewmodel, but don't actually use it.

有很多方法可以使用Prism解决您的问题,请参考

There are many ways to solve your issue using Prism, please refer to this topic.

您可以在XAML中设置视图的DataContext属性:

You could set the DataContext property of your view in XAML:

<UserControl.DataContext>
    <my:MyViewModel/>
</UserControl.DataContext>

您可以在视图的构造函数中创建一个视图模型:

You could create a viewmodel in the view's constructor:

public MyView()
{
    InitializeComponent();
    this.DataContext = new MyViewModel();
}

更好的方法是通过依赖项注入导入视图模型:

A better way would be to import the viewmodel via dependency injection:

[ImportingConstructor]
public MyView(MyViewModel viewModel)
{
    InitializeComponent();
    this.DataContext = viewModel;
}

您可以使用Prism的viewmodel定位服务:

You could use the Prism's viewmodel location services:

<MyView prism:ViewModelLocator.AutoWireViewModel="True"/>

最后但并非最不重要:您也可以使用DataTemplate,但是您应该设置对象的DataContext,以便WPF为您创建视图,并且不要在代码中创建视图.

Last but not least: you could use a DataTemplate too, but you should set a DataContext of your objects to let WPF create the view for you, and don't create the view in code.

更新: 因此,您想使用DataTemplate功能.好吧,这不是做事情的"Prism"方式,因为在这种情况下,视图将由WPF创建,而不是由Prism的IRegion创建.但这还是有可能的.

UPDATE: So, you want to use the DataTemplate feature. Well, that is not a 'Prism' way to do things, because in that case the view will be created by WPF, not by the Prism's IRegion. But that's possible anyway.

我将解释区别:在所有其他(Prism)方法中,视图是主"视图,将首先创建该视图,然后将创建适当的视图模型并将其附加到该视图.在Prism中,您定义应创建哪些视图,何时(导航)以及在何处(区域).在DataTemplate方法中,视图模型(数据)是主模型",它将首先创建,并且WPF确定如何创建视图来显示它们.因此,在这种情况下,您将无法使用Prism的区域和导航,因为WPF可以处理所有事情.

I'll explain the difference: in all the other (Prism) methods, a view is the 'master', it will be created first, then an appropriate viewmodel will be created and attached to the view. In Prism, you define what views should be created, when (navigation) and where (regions). In the DataTemplate method, the viewmodel (data) is the 'master', it will be created first, and WPF determines how to display them creating a view. So in this case, you can't use Prism's regions and navigation, because WPF handles all the things.

因此,我真的建议您使用上述任何一种方法,而不要使用DataTemplate一种方法.

So I really suggest you to use any of the above methods for this, but not the DataTemplate one.

您可以如下创建视图:

_regionManager.Regions[RegionNames.MenubarRegion].Add(ServiceLocator.Current.GetInstance<MenuView>());

我建议您将其更改为:

_regionManager.RegisterViewWithRegion(RegionNames.MenubarRegion, typeof(MenuView));

直接使用ServiceLocator并不是一个好的模式(除非您无法避免),因此让Prism为您实例化视图.

Using ServiceLocator directly is not a good pattern (unless you can't avoid it), so let Prism instantiate the view for you.

在视图的构造函数中,只需添加视图模型的依赖项注入,然后将视图的DataContext设置为其.哎呀!知道了.

In your view's constructor, just add a dependency injection of the viewmodel, and set the view's DataContext to it. Voilà! You've got it.

[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class MenuView : UserControl
{
    [ImportingConstructor]
    public MenuView(MenuViewModel viewModel)
    {
        this.InitializeComponent();
        this.DataContext = viewModel;
    }
}

使用DataTemplate,您只能以旧的普通WPF方式"进行操作.您必须手动创建viewmodel的实例,并将其作为父(shell)viewmodel的属性公开(或使其静态化,但这是一种较差的方法).

Using DataTemplate, you can do it only in an 'old plain WPF way'. You have to create an instance of the viewmodel manually and expose it as a property of the parent (shell's) viewmodel (or make it static, which is a poor approach however).

<Window>
  <ContentControl Content="{Binding MenuViewModelInstace}"/>
</Window>

然后WPF将为您创建视图以显示视图模型,但是您应该直接在XAML标记中定义它,如前所述.棱镜无法在这里为您提供帮助.

WPF will then create the view for you to display the viewmodel, but you should define this directly in XAML markup, as I mentioned before. Prism can't help you here.

这篇关于如何使用MEF初始化viewModel?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆