Reputation: 65
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.
Upvotes: 1
Views: 5108
Reputation: 13194
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.
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 DataTemplate
s 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...
Upvotes: 5