Hi, I guess this is sort of a philosophical question, but I have some code that dynamically adds templated columns to a GridView by generating the XAML at runtime from Model data and loading it using XamlReader. My question is, In an MVVM world, where should I put the code that accomplishes this? At the moment, I have it in the codebehind, but I'm contemplating whether I should move it to the ViewModel.
Thinking about this a little more, I think I have an answer to my own question, but I would love your feedback because I'm still very new to this.
Normally, the View knows only about the properties inside the ViewModel, while the ViewModel provides data that the View will bind to but doesn't really know about any of the widgets/UI elements of the View. So, my thinking is that I should actually leave this code in the code-behind. The code refers to properties in the DataContext (which is set to the ViewModel), hooks into the visual tree, and dynamically adds more branches and leaves to the tree. When I put it that way, it all of a sudden sounds very "View" to me :)
There is a big movement in WPF/Silverlight developers circles to move to a MVVM architected solution however they dont go futher than simple or general examples. This isn’t much of an issue in simple applications. It becomes serious when you contemplate
- a screen or page constructed dynamically, perhaps in response to user action
- a composite page consisting of regions holding views, perhaps nested views, each implemented as MVVP triads
- a wizard closing a collection of views with a single click
There is significant logic here. Where does it go?
Its exactly this (your question) where I get hangups moving my stuff to MVVM, Ive read a nice article that gave me kind of an ahha moment, this kind of architecture fits into a bigger one where some class is composing the lifecycle of the MVVM trio
If you want to read the full article here Ward Bell gos into greater detail. It may be that this is just a smaller piece in the bigger picture, another great article discusses the main players in the Composite Application Neighborhood , see here.
What does this all have to do with your problem? Well its my belief that the ViewModel represents the view, and your view is determined at runtime, so if the problem requires that there be dynamically generated columns it may be the responsiblity of something else to create the rendered view and the appropriate viewmodel that fits your final result and instance them.
I don't think it's a good idea to generate XAML and process it with XamlReader.
Why would you do that thing?
If you want your view be dependent on type of data, or on certain conditions, it all is achievable through DataTemplate. DataType property makes it choose template by type. Triggers and enclosed ContentControl can swap templates based on data conditions.
I you're up for storing actual XAML in database, that's completely other story nothing to do with MVVM. There's number of potential problems with it like performance, XamlReader syntax limitations, assembly/resource resolution quirks, security not the least.
Now the questions is a bit clarified, here's one more try.
First of all, charts probably better handled by WPF Charting control in WPF Toolkit, they have put pretty juicy stuff in there. I would definitely recommend trying it out before creating custom ListView-based charts.
But anyway, coming back to your clarified questions. That's where you should user either DataTemplateSelector, or trigger with a custom IValueConverter that sets template.
DataTemplateSelector was invented exactly for this reason, but in practice it's kind of messy. You would need to deal with loading templates from resources, which little better than XamlReader.
With IValueConverter it would be something like this:
<DataTemplate>
<ContentControl x:Name="content" Content="{Binding}">
</ContentControl>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Converter={local:TemplateChoseConverter}}" Value="SystemType">
<Setter TargetName="content" Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="[system type] "/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Converter={local:TemplateChoseConverter}}" Value="Action">
<Setter TargetName="content" Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Button Text="[action] "/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Here you're putting ContentControl inside DataTemplate, and triggers inside DataTemplate swap inner nested template based on things calculated in IValueConverter.
In that way you never deal with XAML, even your template-choosing ValueConverter yeilds just strings, which then being processed by triggers.