mardi 23 juillet 2019

Creating objects at runtime using dependency injection

I read a book about Dependency injection from Mark Seemann and Steven van Deursen and I'm trying to practice what I learned by programming a WPF application in C#.

In my application I need to create custom UserControls at runtime based on what the user is doing. Each custom UserControl naturally needs its own DataContext.

So my custom UserControls might look like this:

class ImageComponent : UserControl
{
    public ImageComponent(object dataContext)
    {
        InitializeComponent();
        DataContext = dataContext;
    }
}

class TextComponent : UserControl
{
    public TextComponent(object dataContext)
    {
        InitializeComponent();
        DataContext = dataContext;
    }
}

and of course the view models. For simplicity I omitted all dependencies if any

class ImageComponentVM : ViewModelBase
{
    // Image property...
}

class TextComponentVM : ViewModelBase
{
     // Text property...
}

Now that I have defined the UserControls and their view models, I need to create their instances somewhere.

I was thinking about something like this, so that the component producer wouldn't have to decide what type it needs to create. The component producer would also contain other logic which I again omitted. Instead of the 'Type' as the Dictionary's key, enum would also do the thing.

class ComponentProducer : IComponentProducer
{
    private Dictionary<Type, Func<object>> dataContextsDictionary;
    private Dictionary<Type, Func<object, FrameworkElement>> componentDictionary;

    public ComponentProducer(Dictionary<Type, Func<object>> dataContextsDictionary,
                             Dictionary<Type, Func<object, FrameworkElement>> componentDictionary)
    {
        this.dataContextsDictionary = dataContextsDictionary;
        this.componentDictionary = componentDictionary;
    }

    public FrameworkElement Produce(..data..)
    {
        var dataContext = dataContextsDictionary[data.something.GetType()].Invoke();
        var component componentDictionary[data.something.GetType()].Invoke(dataContext);
        return component;
    }
}

And in the composition root, I would have something as follows:

    var datacontextDictionary = new Dictionary<Type, Func<object>>
        {
            { typeof(Image), () => new ImageComponentVM() },
            { typeof(string), () => new TextComponentVM() }
        };
    var componentDictionary = new Dictionary<Type, Func<object, FrameworkElement>>
        {
            { typeof(Image), (dataContext) => new ImageComponent(dataContext) },
            { typeof(string), (dataContext) => new TextComponent(dataContext) }
        };
    var componentProducer = new ComponentProducer(componentDictionary);

My current solution looks similar to what I demonstrated. I would like to know different (and right) approaches, how this kind of problem is solved using dependency injection.

Thanks

Aucun commentaire:

Enregistrer un commentaire