0

I have an app that needs to display an input form for each property of a class. The thing is, I don't know which class I'm dealing with at the compile-time, so it needs to be dynamic. Now, I used to do this before in code-behind like:

foreach (var P in TheType.GetProperties()){

   this.ControlStackPanel.Children.Add(/.../)
// and so on 

}

But this time I'm trying to do this in pure MVVM pattern, so I cannot use code-behind. My idea was to inject an instance of my window to the ViewModel through constructor, but I've been told that it completely ruins the MVVM pattern.

So, any ideas on how this could be done?

4
  • Sounds like you are re-inventing PropertyGrid. I don't understand the problem though. Why it has to be dynamic? ViewModel should not know anything about view. ViewModel can prepare a list of something what will be used by view to generate children though. Pure MVVM - move code into behaviors/converters/etc, but please don't move it into ViewModel. Commented Aug 10, 2016 at 9:25
  • "ViewModel can prepare a list of something what will be used by view to generate children though." Alright, how do I do that? Do i prepare a list of UIElements or just a list of Properties? And how does the view know what to do with it? What do I bind to that list? Commented Aug 10, 2016 at 9:52
  • You do not prepare list of UI elements. But it can be a list of ViewModels for items - ObservableCollection<Item>, where Item holds all necessary properties (Icon, Text, whatever you will need). The view can check its DataContext in Loaded to get ViewModel instance. Then you can bind that collection to ItemsControl.ItemsSource and using data templates or template selectors visualize those (in data template you can define binding to concrete property of either ViewModels). Or if you don't mind non-pure MVVM (I don't), do it in the View as you already do, but using that collection. Commented Aug 10, 2016 at 10:12
  • You need to learn how MVVM works in WPF. A book would help. No, I will not suggest one. Search on Amazon, sort by rating, read reviews, make your choice. Commented Aug 10, 2016 at 20:28

2 Answers 2

2

I'm trying to do this in pure MVVM pattern, so I cannot use code-behind.

No, nope. No.

The MVVM design pattern is about putting code where it belongs. View code does not belong in the View Model. I've gone into a little more detail about this here.

I don't know which class I'm dealing with at the compile-time, so it needs to be dynamic.

In this scenario, you can probably make use of a DataTemplate for each class type you need. See the documentation. This will allow your view to dynamically change depending on the class types you're dealing with.

Sign up to request clarification or add additional context in comments.

2 Comments

I also don't understand idea of pure MVVM (unless you are working with cooperation with designer and your code cause troubles to him I guess). Putting code into Window.xaml.cs is totally fine. View can know about ViewModel. I never yet had to reuse same View with multiple ViewModels, but if I would then a simple base class/interface solves all troubles.
I agree, I too have never had to use the same view with with different view models. It's great that you can, but it's unlikely that you ever will.
0

Create a viewmodel and give it the type of the object via setter or property (or constructor if it is not changed later on). Then publish the properties as a List property with public getter and private setter. You could build the children in the getter of the property, but it will be more efficient to do it when the type is given to the viewmodel so you do it only once. Then bind to the list through XAML. You need to create a new class per supported type. This will also help you to catch the input. Then on the xaml side you need to use template selectors to get the correct form for the given class.

This would be the viewmodel side of the implementation.

public class TheViewModel
{
    public void SetTheType(Type theType)
    {
        Inputs.Clear();
        foreach (var prop in theType.GetProperties())
        {
            if (prop.PropertyType == typeof(DateTime)
            {
                Inputs.Add(new DateTimeInput...); // create the descriptors here
            }
            // other known types here
        }
    }

    public IList<InputBase> Inputs{get; private set;}

}

public class InputBase
{
}

public class DateTimeInput : InputBase
{
    DateTime Value {get;set}
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.