开发者

Creating child view models when using MEF

I have a traditional MVVM approach, so for example a view model called 'PatientManagementViewModel' that is used by a view called 'PatientManagementView'. Everything is injected using MEF, so I don't create any instance myself.

Now suppose that 'PatientManagementViewModel' has a property Patients, which is an ObervableCollection of 'PatientViewModel'. What I do now to create an instance of 'PatientViewModel' and pass the selected patient is like:

var patientViewModel = _container.GetExportedValue<IPatientViewModel>();
patientViewModel.Patient = patient;

This works, however, I was wondering if this makes sense. It would be nicer to pass a patient to the constructor because 'Pa开发者_JAVA技巧tientViewModel' cannot exist without a Patient:

var patientViewModel = new PatientViewModel(patient);

but then I can't use dependency injection.

So the question is: does it make sense to inject a subviewmodel, or should I only inject the main view model, and instantiate all subviewmodels without injection?


You can do the following. You can create your child view model using usual constructor and then call ComposeParts on the instance to inject everything:

var patientViewModel = new PatientViewModel(patient);
_container.ComposeParts(patientViewModel);

But doing this every time is not very good for a variety of reasons. To address this scenario and to encapsulate usage of MEF I created a helper service for creating view models. It is called IViewModelFactory and here is how it looks:

[Export(typeof(IViewModelFactory))]
[PartCreationPolicy(CreationPolicy.Shared)]
internal class ViewModelFactory : IViewModelFactory
{
    [ImportingConstructor]
    public ViewModelFactory(CompositionContainer container) {
        Contract.Requires(container != null);

        Container = container;
    }

    protected CompositionContainer Container { get; private set; }

    public T Create<T>(params object[] args) where T : class {
        T result;

        try {
            bool populateDependencies = false;

            if (args == null || args.Length == 0) {
                // There are no parameters for contructor, so
                // try to create an instance by asking the container.
                result = Container.GetExportedValueOrDefault<T>();

                if (result == null) {
                    // The view model is not exported. Just create an instance using reflection
                    // and then populate all the dependencied using the container.
                    result = Activator.CreateInstance<T>();
                    populateDependencies = true;
                }
            }
            else {
                // There are constructor parameters. Create an instance using those parameters
                // and then populate all the dependencied using the container.
                result = (T)Activator.CreateInstance(typeof(T), args);
                populateDependencies = true;
            }

            // Populate dependencies if needed
            if (populateDependencies) {
                Container.ComposeParts(result);
            }

            // Initialize the object if applicable
            var initializable = result as IInitializable;

            if (initializable != null) {
                initializable.Initialize();
            }
        }
        catch (Exception ex) {

            throw new ViewModelCreationException(
                string.Format(
                    "Unable to create and configure an instance of view model of type {0}. An error occured. See inner exception for details.",
                    typeof (T)), ex);
        }

        return result;
    }
}

Using this factory you can create child view models like this:

var patientViewModel = ViewModelFactory.Create<PatientViewModel>(patient);

The downside here is that, when you use constructor parameters, you loose compile-time check for the parameters' types, count, order etc.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜