I was reading this post and the author makes the suggestion that using DataTemplates to define a ViewModel is a lunatic's way to do it (#7). I do that all the time, is it really that bad?
<DataTemplate DataType="{x:Type local:MyViewModel}">
<Grid>
...
</Grid>
</DataTemplate>
Most of my Views are simply a ResourceDictionary that defines a DataTemplate or two. To me, it makes much better sense to do this than creating a UserControl for every ViewModel. Why would I want the extra layer in WPF's visual tree when it's not needed? And why would I want to take care of mapping ViewModels to Views when a DataTemplate does that for me? Is this syntax really a "lunatics approach"?
Nothing bad about it, except for incredibly large xaml files and the lack of edit support that DataTemplates have on the design surface.
If those issues are hurting you, you can always...
<DataTemplate DataType="{x:Type local:MyViewModel}">
<local:MyViewModelUserControl />
</DataTemplate>
The good thing with DataTemplate is that they are strongly typed to Viewmodel classes. All you need to do is create a ContentPresenter in View and Bind DataContext to VM. If your DataTemplate is defined in a ResourceDictionary and has a DataType attribute instead of Key, WPF will internally figure out the right DataTemplate for the VM class and display it.
But as you mentioned, we cannot create the DataTemplate in a seperate file. So the file where the DataTemplates exist in ResourceDictionary (e.g. App.xaml), the file gets really messy and it becomes difficult to manage the code soon.
So my take is, if the VM is simple create a DataTemplate. Or else it is always better to create a seperate UserControl and bind its content to the VM.
I run into the issue with performance. There is difference between next two case:
1.
<DataTemplate DataType="{x:Type local:MyViewModel}">
<!-- xaml is moved to separate user control -->
<local:MyViewModelUserControl />
</DataTemplate>
2.
<DataTemplate DataType="{x:Type local:MyViewModel}">
<!-- xaml is typed here directly -->
<Border>
...
</Border>
</DataTemplate>
In 1st case it takes longer to render results than in the 2nd. And this difference is in about 2 times.
I posted it as a separate question
Related
What can be the reasons that I see the UserControl path instead of UserControl content?
I have quite complex code, so I just would like to ask for some hints, where I should look for the problem..
This shows when I debug my app:
Presumably, you have ConteControl which binds to OrderEntryViewModel but it is not associated with any view. You need
<Window.Resources>
<DataTemplate DataType="{x:Type viewmodel:OrderEntryViewModel}">
<userControls:YourView/>
</DataTemplate>
</Window.Resources>
I have an app written in WPF (MVVM), which based on some conditions, will create instances of different UserControls, These UserControls are completely independent, used to display certain information. They have some custom logic inside, like timers and so on, so I can't use Templates.
Now I face the problem that I want to create a list of UserControls in the ViewModel, and bind the host UI to it. The problem is that I don't know how to bind and what to bind. In a non MVVM project, you would simply get the layout where you want to put your controls, and add them there as children. In MVVM app, I don't know how to do this. I imagine having a WrapPanel with ItemsSource, that will add all the controls and resize itself as needed, based on the UserControls.
Can someone suggest a solution?
EDIT:
My ViewModel exposes an ObservableCollection of IMyDriver right now. So that's what I thought, to break a little bit MVVM to get what I describe next:
Now, Each IMyDriver can be a different type of driver, and can implement different other interfaces. I need the UI to create specific UserControls that know how to get maximum from these Drivers, based on their capabilities. In short, the UserControls connect to the device through the driver for polling data. And each UserControl does it in a specific way.
You can do it quite simply and easily by declaring specific data type classes for the data in each UserControl and define DataTemplates that expose your UserControls in the App.xaml file:
<DataTemplate DataType="{x:Type YourViewModelsPrefix:YourViewModel">
<YourViewsPrefix:YourView />
</DataTemplate>
<DataTemplate DataType="{x:Type YourViewModelsPrefix:YourOtherViewModel">
<YourViewsPrefix:YourOtherView />
</DataTemplate>
<DataTemplate DataType="{x:Type YourViewModelsPrefix:AnotherViewModel">
<YourViewsPrefix:AnotherView />
</DataTemplate>
Now whenever the Framework comes across an instance of these view model classes, it will render the associated view/UserControl. You can display them by having a property of the type of your view model using a ContentControl like this:
<ContentControl Content="{Binding YourViewModelProperty}" />
...
public YourBaseViewModelClass YourViewModelProperty { get; set; }
Make sure that all of your view models extend this class:
public YourViewModel : YourBaseViewModelClass { }
...
public AnotherViewModel : YourBaseViewModelClass { }
Then you can swap each view model (and display each related view) like this:
YourViewModelProperty = new AnotherViewModel();
Based on what Will commented, and what Sheridan answered, I have found the solution to my problem.
So:
I don't break MVVM by leaving ViewModel types intact.
I create DataTemplates in my Window's Resources tag, and in each data template, I assign the DataTemplate to be my UserControl defined in another assembly (UICommons)
<DataTemplate x:Key="IMultiChannelMeasurementDCDataTemplate">
<uicommon:MeasurementMax8ChannelMonitoringUserControl/>
</DataTemplate>
I create a Template Selector in my application assembly, and based on the interfaces the DataTypes implement, I return the right DataTemplate, that I assign in the same Window's Resources tag
<!-- DataTemplate Selector -->
<local:DriverComponentDataTemplateSelector x:Key="templateSelector"
DefaultDCDataTemplate="{StaticResource DefaultDCDataTemplate}"
IIhcDCDataTemplate="{StaticResource IIhcDCDataTemplate}"
IMultiChannelMeasurementDCDataTemplate="{StaticResource IMultiChannelMeasurementDCDataTemplate}"
IProgrammablePowerSourceDCDataTemplate="{StaticResource IProgrammablePowerSourceDCDataTemplate}"
IEnvDCDataTemplate="{StaticResource IEnvDCDataTemplate}"/>
I create an ItemsControl in the Window, with the following XAML code, that binds itself to my ObservableCollection of items
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<ItemsControl ItemTemplateSelector="{StaticResource templateSelector}" ItemsSource="{Binding DriverComponentsInfo}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" x:Name="ucWrapPanel">
</WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
I enjoy dynamically created UserControls based on different Drivers!
P.S. I upvoted Will's comment and Sheridan's answer, because without these, I wouldn't be able to find the solution. Thx!
They have some custom logic inside, like timers and so on, so I can't use Templates.
This does not follow. I think you may have a misconception about the capabilities of WPF.
Also, as you want to use MVVM: Binding to a list of UserControls is breaking the pattern. View-models should only ever reference other view-models (and models); they do not know anything about the UI. Bind to a collection of view-models which have associated UserControls as their views (consider using implicit DataTemplates). To bind a WrapPanel you use an ItemsControl and set its ItemsPanel accordingly.
I want to set the background color separately for each item in a WPF ListBox. e.g. If I am adding Widgets to the ListBox, I might set the background color for each one based on the type of widget. This must be done in code (not XAML) as I only know what the items are at run time.
I know how to use ItemContainerStyle to set the style for all items, but how do you do it separately for each item?
Yes you do set ItemContainerStyle, using a StyleSelector.
This example at MSDN is exactly what you are looking for.
There are lots of ways to do this.
One is to use a StyleSelector, as loxxy suggests. This is pretty low on my list, because that kind of code is harder to read (well, find) and test than I'd like.
Another is to use a DataTrigger in the style. This is simple, if (and only if) the items all implement a common property that can be used in the trigger. You might be well served by implementing a wrapper class that exposes this common property, and contains the logic that figures out what value to assign to the property based on the object it's wrapping. (Whether or not this is easier than a StyleSelector is certainly arguable.)
If the items are really and truly heterogeneous, you can accomplish the result by using data templates, e.g.:
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:Foo}">
<TextBlock Text="{Binding FooText}" Background="Red"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Bar}">
<TextBlock Text="{Binding BarText}" Background="Yellow"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Baz}">
<TextBlock Text="{Binding BazText}" Background="PapayaWhip"/>
</DataTemplate>
</ListBox.Resources>
etc. This would generally be my first choice, but your question doesn't really explain enough about the circumstances to know if it's the right way to go or not.
When I searched the internet to find out how to set the itemtemplate for a listbox, I found an example where they used a Style Setter in the window.resources to do this. So, I have a couple of options, I can either create a datatemplate in my window.resources, or, create a Style Setter. Would it be overkill to set the style instead of the datatemplate? Which method should I use?
Thank You.
You can't say one or the other is better, it depends. Implicit DataTemplates are a nice way to globally (even if just for part of the visual tree) how a Type should look. While using ItemTemplate allows you to indicate how it should look in a given ItemsControl (i.e. ListBox, ComboBox, etc.).
You can even use a combination of both of them, which allows you have a "default" look, but customize it per control or control type.
Even with ItemTemplate, you can set it globally for say all ListBoxes, or for just one ListBox instance. So again, sometimes one method is better, sometimes not. It depends on what you are trying to do.
Styles in general are used to apply the same values to different instances of the same type. If this is not the case there is no need to create a style at all.
I prefer to make a <DataTemplate> for the type of item and then let WPF figure it out.
<DataTemplate DataType="{x:Type local:Task}">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
<List ItemSource="{Binding MyListOfTaskItems"/>
I had been setting the DataContext for UserControls like so:
<uc:DepartmentListingView DataContext="{Binding ., Mode=TwoWay}" />
Based on a sample project by Josh Smith I am trying to accomplish the same thing with a DataTemplate and DataType:
<!-- Template applies a DepartmentListingView to an instance of the DepartmentSelectionViewModel class. -->
<DataTemplate DataType="{x:Type model:DepartmentSelectionViewModel}">
<uc:DepartmentListingView />
</DataTemplate>
This works well, but of course there is a problem; I think it might arise from trying to set more than one view (UserControl) to the same view model(?). In the code below I am now associating the same viewModel from above with a different view in the same window.
<DataTemplate DataType="{x:Type model:DepartmentSelectionViewModel}">
<uc:ListSubjectHeaderView />
</DataTemplate>
The first view is wired the same as it was when I set the DataContext explicitly but the last view gets no binding, although no obvious DataBinding error in the console either.
So, would resusing the DataType / DataTemplate trick this way be the problem?
Thanks,
Berryl
Ideally you will have a one to one relationship between a view and viewmodel.
To get what you want perhaps subclass your viewmodel with nothing extra and have that subclassed viewmodel as the datatype in the datatemplate.
That way just creating the correct viewmodel will drive the correct datatemplate and therefore usercontrol