views:

753

answers:

4

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.

A: 

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 :)

DanM
+1  A: 

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.

almog.ori
@Ori, thanks for your answer. That Ward Bell post is intriguing, as is the followup (http://neverindoubtnet.blogspot.com/2009/06/screen-factory.html). The community still has a lot to figure out about MMVM. As for my particular issue, separating out the code that generates the separate columns definitely makes sense. I could see putting the code in its own class, then using my ScreenFactory to instantiate and run it. (I don't actually have any ScreenFactories yet, but I've been contemplating what to do about the rapidly expanding app.cs file--now I have a plan :))
DanM
Agree the ScreenFactory makes a lot of sense as far as the difference between screens and views the jury is still out.
almog.ori
A: 

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.

Oleg Mihailik
I'm trying to use a GridView to display a pivot table. The number of columns is not known at design time (it depends on data stored in the database). The DataTemplate is different for each column (each column binds to different data). How would you set this up with pure XAML? I would definitely prefer to do it that way if it's possible. (See http://stackoverflow.com/questions/1140382/dynamically-added-datatemplate-staticresource-for-converter-cant-be-found for a code sample.)
DanM
That would require separate answer, because comments are short.
Oleg Mihailik
A: 

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.

Oleg Mihailik
Thanks for your answer, but again, I don't know how many columns there are until I run the software and check the database. I could have 2 columns or I could have 20. I don't know this until I read from the database. Also, each column binds to a different column of a DataTable. So, if I have 2 columns, I have 2 DataTemplates. If I have 20 columns, I have 20 DataTemplates. But I don't know ahead of time.
DanM
By the way, I created a new question for this, so if you have any other ideas, would you mind posting them there? The link is: http://stackoverflow.com/questions/1156336/is-it-possible-to-configure-a-gridview-to-show-pivoted-data-using-static-xaml-if. Thanks.
DanM