1

I'm facing a problem since I have a view where all the controls are created at runtime.

I'm trying to bind each of them to my viewmodel, but I think I might have the wrong approach.

We'll use a combobox as an example.

My model to contain the data:

public class ModelToContainTheData
{
    public string BuildType { get; set; }
    public string Section { get; set; }
    public string QuestionID { get; set; }
    public string Values { get; set; }
    public int Selectable { get; set; }
    public DateTime Changed { get; set; }
    public string User { get; set; }
}

I then create an array of this model, bind the following method to ComboBox.SelectionChanged

private void ComboboxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var box = sender as ComboBox;

    foreach(ModelToContainTheDatamodel in currentSettingsModel)
    {
        if(model != null)
            if(model.QuestionID == box.Name)
            {
                model.Changed = DateTime.Now;
                model.Values = box.SelectedValue.ToString();
                model.User = "wc_set";
            }
    }
}

What I would then like to do is bind the array to the viewmodel. Is there any proper way to do this, or do I need to change my approch entirely?

I figured an ObservableCollection might be the way to go, but I couldn't figure out how I'd then bind it.

Xaruth
  • 4,034
  • 3
  • 19
  • 26
Ronni
  • 65
  • 1
  • 6
  • You don't `Bind` a data array to the view model. You just add it as a `public` property and then `Bind` *that* to a control in the UI. – Sheridan Oct 09 '13 at 09:32

1 Answers1

4

There is a bit of mixing up going on in your example. I would recommend to get familiar with the MVVM pattern and then get back to your actual problem. The main idea of MVVM (and I suppose you're aiming at MVVM, as your talking about models, views, and ViewModels) is to decouple views and models. Subscribing to UI events (like SelectionChanged) is to be avoided in MVVM.

You are coupling UI and models tightly, especially by matching model properties with UI control properties (model.QuestionID == box.Name).

I will briefly explain what the general concept would be to solve your problem in a MVVM way:

Your models need to draw the complete picture of what is going on in the domain-world of your app. You've got questions, etc., etc., all of this needs to be represented in the domain logic, also called business logic. You'd have a couple of classes. I just make something up from what I understand from your code, no idea whether it matches what you're trying to do...

// Model for an answer ('Value' in your question
public class Answer { ... }

// Model for a question containing possible answers and the actual answer
public class Question 
{ 
   private Answer _answer;

   public List<Answer> PossibleAnswers { get; set; }

   public Answer Answer { get; set; }

   public DateTime Changed { get; set; }

   public Question()
   {
       // Acquire the values from wherever
       PossibleAnswers = ...;
   }
}

Note that the model is entirely standalone, this means it doesn't know anything about the ViewModel or the View.

Now you create a ViewModel for this model which will expose properties in a way that a view can be bound to the data you want to display. In the easiest case you just relay the properties of the model. Use a proper base class which implements INotifyPropertyChanged. There are lots of MVVM frameworks for this, like MVVMLight:

public class QuestionViewModel : NotifyingObject
{
    public Question Model { get; private set; }

    public List<AnswerViewModel> PossibleAnswers 
    {
        get { return _possibleAnswers; }
    }

    public DateTime Changed 
    {
        get { return Model.Changed; }

    public AnswerViewModel Answer 
    {
        get { return _answer; }
        set 
        {
            _answer = value;
            // Set properties on your model which are effected
            _model.Answer = _answer.Model;
            _model.Changed = DateTime.Now;
            // Raise property changed events. They are needed
            // to update the UI
            RaisePropertyChanged("Answer");
            RaisePropertyChanged("Date");
        }
    }

    public QuestionViewModel(Question model)
    {
        Model = model;
        _possibleAnswers = Model.Answers.Select(a => new AnswerViewModel(a));
    }

}

public class AnswerViewModel { ... }

As you see, the ViewModel knows about the Model and relays changes in it's own values to the model. But again, the ViewModel doesn't knwo anything about the View.

The View binds to the ViewModel, using WPF magic. You just need to make sure that the DataContext of your View is set to the ViewModel. There are a number of ways to achieve this, again MVVM frameworks like MVVMLight offer ways to do that. I will only show the usage here. Say you have ComboBox:

<ComboBox ItemsSource="{Binding PossibleAnswers}" 
          SelectedItem="{Binding Answer}" />

That's it. WPF takes care of the rest.

In a more complex scenario, where you have collections on your model which can change actively, i.e. not only by the user in the UI but also for other reasons, it gets a little more complex. Then you need to synchronize the collections on the Model and on the ViewModel.

VMCollection synch

This is more advanced stuff which you will run into eventually. If you're getting there, this answer might help you:

SO Answer on Collections on Model and ViewModels

This answer might be a little bit overwhelming at first, so I recommend to dig into MVVM using one of the many very good resources on the web. Use my answer as a guideline to find a solution for your actual problem. If you understand this answer with the help of the MVVM tutorials and docus out there, you'll be fit to solve your problem in a proper MVVM fashion.

EDIT: Regarding the dynamic creation of UI elements

What you describe as the dynamic creation of controls is a quite natural concept in WPF and MVVM. The basic idea is to use an ItemsControl which is bound to a collection of ViewModelItems and use DataTemplates to specify how each ViewModel is rendered. There are no limitations, each item can be rendered in a complex control and the layout can be specified through the 'ItemsControl's ItemsPanel property. Things will get clear for you while digging into MVVM and your scenario is a very common thing to solve with MVVM. Just keep your eyes open for WPF's ItemsControl and what you can do with it...

Community
  • 1
  • 1
Marc
  • 12,706
  • 7
  • 61
  • 97
  • Thank you so much for the elaborate answer. I am still quite new to MVVM, but I was tasked with modifying an existing program. The main problem, as I have also made other modifications, is the fact that all the GUI components of this view are dynamically created - and for each ComboBox I will have to save all the properties in the model I showed you. Therefore, I think simply binding the ComboBoxs propery to the viewmodel will be too little, as I need a way to identify which combobox was changed, and I can't pass the combobox itself, as that would also violate MVVM. – Ronni Oct 09 '13 at 10:25
  • @Ronni: These problems will be easily solved when you've got a deeper understanding of MVVM. I'll edit my answer to point you in the right direction and I'm sure you'll manage, once you've got the concept. Good luck with your project!! – Marc Oct 09 '13 at 10:33
  • 1
    @Ronni: There you go, does it help? – Marc Oct 09 '13 at 10:39
  • It certainly does - I'll try to go from here, the previous developer has added the controls by creating an instance and setting the properties, and then adding it to the MainGridView.Children list it seems. I'm sure it would be possible to find a work-around, but I'd like to do it properly in order to learn the framework. Again, thank you very much for taking the time to write such a detailed answer! – Ronni Oct 09 '13 at 10:49
  • You're very welcome! Learning MVVM is not easy but it's definitely worth it for what you can do with it and from a strategic point of view as a developer. Have fun and let me know if you have further questions. – Marc Oct 09 '13 at 10:53