Silverlight Constructor Injection into View Model + Design Mode

99封情书 提交于 2019-12-18 11:12:19

问题


Im trying to get to grips with writing testable ViewModels in Silverlight 4. Im currently using MVVM light.

Im using AutoFac and the IoCContainer is doing its job fine. However to inject into the constructor of ViewModels, which are bound to Views I have this constructor chaining:

    public UserViewModel() : this(IoCContainer.Resolve<IUserServiceAsync>())
    {

    }

    public UserViewModel(IUserServiceAsync userService) 
    {
        if (this.IsInDesignMode) return;

        _userService = userService;
    }

Which doesn't feel clean, but is the best option i have found so far. This works and my app works as desired, and is testable with control inverted.

However, with my VM bound to my view like this:

 <UserControl.DataContext>
            <ViewModel:UserViewModel />
 </UserControl.DataContext>

In my XAML page attributes, design mode in both VS2010 and Blend doesnt work.

Is there are nicer way to achieve what im trying in Silverlight that still works with design mode? Losing design mode isnt a deal breaker but will be handy while learning XAML. A cleaner none chained way would be nice though!

Im thinking it maybe possible to use AutoFac / IoC to resolve viewmodels to views, as apposed to the XAML mark-up approach above, and go down this route?

Thanks.


回答1:


Instead of implementing the first constructor, I suggest you implement a ViewModelLocator, like this:

public class ViewModelLocator
{

    IoCContainer Container { get; set; }

    public IUserViewModel UserViewModel
    {
        get
        {
            return IoCContainer.Resolve<IUserViewModel>();
        }
    }

}

Then in XAML you declare the locator as a static resource:

<local:ViewModelLocator x:Key="ViewModelLocator"/>

While you initialize your application, it is necessary to provide the locator with the instance of the container:

var viewModelLocator = Application.Current.Resources["ViewModelLocator"] as ViewModelLocator;
if(viewModelLocator == null) { // throw exception here }
viewModelLocator.Container = IoCContainer;

Then in XAML you can use the resource cleanly:

<UserControl
    DataContext="{Binding Path=UserViewModel, Source={StaticResource ViewModelLocator}}"
    />
    <!-- The other user control properties -->

For design time, you can implement a MockViewModelLocator:

public class MockViewModelLocator
{

    public IUserViewModel UserViewModel
    {
        get
        {
            return new MockUserViewModel();
        }
    }

}

Declare it in XAML appropriately:

<local:MockViewModelLocator x:Key="MockViewModelLocator"/>

And finally use it in your user control:

<UserControl
    d:DataContext="{Binding Path=UserViewModel, Source={StaticResource MockViewModelLocator}}"
    DataContext="{Binding Path=UserViewModel, Source={StaticResource ViewModelLocator}}"
    />
    <!-- The other user control properties -->

You can make your mock view model locator return safe and easily readable data for Blend to use and during runtime you will be using your real service.

This way you do not lose design time data and you do not have to sacrifice the cleanliness of the dependency injection methodology in your view models.

I hope this helps.



来源:https://stackoverflow.com/questions/4033600/silverlight-constructor-injection-into-view-model-design-mode

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!