Saturday, 9 June 2012

Post/Email-2: Views, ViewModels and Composition

Recently a friend of mine asked me the following question on Facebook:

brother, hope i am not disturbing you. had a question. if i have mutliple tab items in one page in y silverlight app, while each tab serves different functionalities, would it be all right if i write separate view models for all of the tab items, and i expose this view models as properties from one view model and bind the page's datacontext to that main viewmodel?

and my answer was:

I think this is a good strategy. It's actually what I think to be the best practice, because you're enforcing separation of concerns by putting the different functionalities in different classes :)

You can go even further and simplify your view, by having each tab implemented as a UserControl, and just instantiating them in xaml and assigning the appropriate ViewModel to their DataContext ;)

The first part of the answer is straightforward. The second part, however, brought another question from my friend:

thanks for your answer. My conception regarding user control is not very clear. Lets say my user control has two text boxes and one button. its ready and available in toollbox for when i drag and and drop it one view i cant just access those elements inside it anymore, then how can I bind any property from my view model to any of the element(e.g.text box) of that control. What my question is that, as you suggested and what I know is that user control makes reusable elements. But the fact is for example we have a textbox,that's a control too. But when we use it we have access to it's text and other properties. And suppose in our custom user control we had some sort of properties too(which is may be a content of a textbox inside it) that I need to bind, I just don't get the way of doing it.

to be more specific lets say i have a string property in my viewmodel, i have a textbox in my user control. the datacontext of the usercontrol is set to the viewmodel. now i want to bind the text property of the text box to the string property in the viewmodel. how can i do that?

and following is my (kind of long) answer:

let's say your page's view model is "MainViewModel", and you have "TabOneViewModel" and "TabTwoViewModel", and let's say that TabOneViewModel contains a property called "Message" that you want to bind to a TextBlock. Now, if you want to use my approach with a separate UserControl for each tab, you would have something like TabOneView, which has a TextBlock bound to the Message property as follows:

<TextBlock Text="{Binding Message}"/>

and in your MainView (which has MainViewModel as its DataContext), you would have something like:

<MyControls:TabOneView DataContext="{Binding TabOneViewModel}"/>

This is enough to wire up your binding, and here's why:
when you set the DataContext on some element, you have direct access to its properties inside of that element (like when you set the MainViewModel as the DataContext for the page and have its properties available inside the whole page).
So, when you set the DataContext for the TabOneView to be the TabOneViewModel, you'll have access to all its properties (including the Message property).

Long story short: you'll only need to set one binding on the MainView, which is the binding for the DataContext.


If by any chance you needed more customization, or say, if you want to reuse the TabOneView in a different context, and have to add a certain property to it which you set differently in the different usage scenarios, then you can define custom DependencyProperties on the TabOneView and bind to those.

If the functionalities in the different tabs are really separate, meaning that each ViewModel is self-contained, and you don't have some code-behind that needs to access elements from different tabs, then you should have no problem applying this approach.

Hope this helps.

1 comment: