简体   繁体   English

如何使用MEF初始化viewModel?

[英]How to initialize viewModel using MEF?

I have a Module called ModuleMenu. 我有一个名为ModuleMenu的模块。 In this module I have a UserControl called MenuView and a Corresponding ViewModel called UserControlViewModel. 在这个模块中,我有一个名为MenuView的UserControl和一个名为UserControlViewModel的Corresponding ViewModel。 I also have a class called Module. 我还有一个名为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>());
    }
}

Now in my main Project I have a class called BootStrapper.cs as follows: 现在在我的主项目中,我有一个名为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));
    }
}

In my App.xaml: 在我的App.xaml中:

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

And finally in App.xaml.cs: 最后在App.xaml.cs中:

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

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

    }
}

When I run the application I get shell as expected. 当我运行应用程序时,我按预期获得shell。 It shows me MenuView, but the data in MenuView is not loaded. 它显示了我的MenuView,但未加载MenuView中的数据。 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? 那么,现在我的问题是如何初始化viewModel?

update: 更新:

After trying your code I get exception as follows: 在尝试您的代码后,我得到如下异常:

An exception has occurred while trying to add a view to region 'MenubarRegion'. 尝试将视图添加到区域'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 \"\""}

update2: UPDATE2:

Here is the Export of type ServiceFactory : 这是ServiceFactory类型的Export:

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

You define a DataTemplate as a view for your viewmodel, but don't actually use it. 您将DataTemplate定义为viewmodel的视图,但实际上并未使用它。

There are many ways to solve your issue using Prism, please refer to this topic . 使用Prism有很多方法可以解决您的问题,请参阅此主题

You could set the DataContext property of your view in XAML: 您可以在XAML中设置视图的DataContext属性:

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

You could create a viewmodel in the view's constructor: 您可以在视图的构造函数中创建一个viewmodel:

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

A better way would be to import the viewmodel via dependency injection: 更好的方法是通过依赖注入导入viewmodel:

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

You could use the Prism's viewmodel location services: 您可以使用Prism的viewmodel位置服务:

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

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 ,但是您应该设置对象的DataContext以让WPF为您创建视图,而不是在代码中创建视图。


UPDATE: So, you want to use the DataTemplate feature. 更新:所以,您想要使用DataTemplate功能。 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 . 嗯,这不是“棱镜”做事的方式,因为在这种情况下,视图将由WPF创建,而不是由Prism的IRegion But that's possible anyway. 但无论如何,这是可能的。

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. 我将解释其中的区别:在所有其他(Prism)方法中,视图是'master',它将首先创建,然后将创建一个适当的viewmodel并附加到视图。 In Prism, you define what views should be created, when (navigation) and where (regions). 在Prism中,您可以定义应创建的视图,何时(导航)和位置(区域)。 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. DataTemplate方法中,viewmodel(data)是'master',它将首先创建,WPF确定如何显示它们创建视图。 So in this case, you can't use Prism's regions and navigation, because WPF handles all the things. 所以在这种情况下,你不能使用Prism的区域和导航,因为WPF处理所有的事情。

So I really suggest you to use any of the above methods for this, but not the DataTemplate one. 所以我建议您使用上述任何方法,但不要使用DataTemplate

You create your view as follows: 您可以按如下方式创建视图:

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

I would suggest you to change that to: 我建议你把它改成:

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

Using ServiceLocator directly is not a good pattern (unless you can't avoid it), so let Prism instantiate the view for you. 直接使用ServiceLocator不是一个好的模式(除非你无法避免),所以让Prism为你实例化视图。

In your view's constructor, just add a dependency injection of the viewmodel, and set the view's DataContext to it. 在视图的构造函数中,只需添加viewmodel的依赖项注入,并将视图的DataContext设置为它。 Voilà! 瞧! You've got it. 你有它。

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

Using DataTemplate , you can do it only in an 'old plain WPF way'. 使用DataTemplate ,您只能以“旧的纯WPF方式”执行此操作。 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). 您必须手动创建viewmodel的实例并将其作为父(shell的)viewmodel的属性公开(或使其静态,但这是一种糟糕的方法)。

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

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. 然后,WPF将为您创建视图以显示视图模型,但您应该直接在XAML标记中定义它,如前所述。 Prism can't help you here. 棱镜无法帮助你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM