implicit DataTemplates vs. sample data vs. blendability - wpf

I have two simple ViewModels, NodeViewModel and LeafViewModel that can be items in a TreeView. Just like below. Templates are applied implictly because I don't want a custom template selector.
<UserControl x:Class="MyProject.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:ViewModels="clr-namespace:MyProject.ViewModels" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" d:DataContext="{d:DesignData /SampleData/NodeViewModelSampleData.xaml}">
<UserControl.Resources>
<HierarchicalDataTemplate DataType="{x:Type ViewModels:NodeViewModel}" ItemsSource={Binding Children}>
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding Name}" IsChecked="{Binding Result}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type ViewModels:LeafViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</UserControl.Resources>
<TreeView ItemsSource="{Binding Children}" />
</UserControl>
How can I generate sample data in blend that contains a tree with both NodeViewModels and LeafViewModels and then display it as the data in the treeview while still using implict template selection?

In the absence of using some kind of mocking framework, I've found that the easiest way to do this is to just hack together a class that generates instances of my view models and use it as a data source in Blend.
It occurs to me that it might be even easier to just define the test data in XAML, though this is contingent on the view model classes being designed to allow that (e.g. with parameterless constructors and a ContentProperty attribute, among other things).

I think the answer is simple: You can't.
Blend doesn't really work well with implicit datatemplates and template selectors. This is not only true for sample data but also inplace wysiwyg template editing. So for blendability you should try to avoid implict templates and template selectors whenever you can.

Related

Associate User Control with ViewModel class

When I define a DataTemplate inline, Visual Studio knows about the type I'm binding to, and properties in that type come up in autocomplete (for example in the code below I was able to select DisplayName from the autocomplete list inside the FirstViewModel template).
<DataTemplate DataType="{x:Type viewmodels:FirstViewModel}">
<StackPanel >
<Label Content="{Binding DisplayName}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:SecondViewModel}">
<views:SecondView/>
</DataTemplate>
However, when the data template references an external control, as for SecondViewModel in the code above, when I'm in the file for the SecondView usercontrol, since it's just a control, the type isn't bound and the editor doesn't help me with anything.
I've tried wrapping my whole control (inside the UserControl element) in the same DataTemplate tag, but then my whole view just shows "System.Windows.DataTemplate".
<UserControl x:Class="Gui.Views.Tabs.ExamsTabViews.ExamInfoView"
xmlns:vm="clr-namespace:Gui.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<DataTemplate DataType="vm:ExamInfoViewModel">
<DockPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<!-- contents of the template -->
</DockPanel>
</DataTemplate>
</UserControl>
Is there a way to achieve this kind of binding for the editor?
<DataTemplate DataType="{x:Type viewmodels:SecondViewModel}">
<views:SecondView/>
</DataTemplate>
when this DataTemplate is instantiated, there will be created SecondView and that SecondView will have a SecondViewModel in DataContext. So there is no need any DataTemplate in SecondViewModel control - bind to DataContext instead ({Binding SecondViewModelProperty}). To have design-time support for such binding use d:DataContext="{d:DesignInstance}:
<UserControl d:DataContext="{d:DesignInstance Type=vm:ExamInfoViewModel,
IsDesignTimeCreatable=True}" ...>

Sharing DataTemplates between controls

I have a ContentPresenter in a couple of places in my application with exactly the same DataTemplates. For now I simply copy-pasted them, but I'd like to clean that up and share them between ContentPresenter instances. I tried this approach:
<ContentControl Content="{Binding DataEditorViewModel}">
<ContentControl.Resources>
<ResourceDictionary Source="pack://application:,,,/LogAnalyzer;component/PredicateDataEditors.xaml" />
</ContentControl.Resources>
</ContentControl>
Application runs, but DataTemplates aren't being applied, I simply see name of class being ContentPresenter's content instead of defined template. I put templates in ResourceDictionary in the following way:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LogAnalyzer"
xmlns:p="clr-namespace:LogAnalyzer.BusinessLogic.ViewModels.Processing;assembly=LogAnalyzer.BusinessLogic"
xmlns:xwt="http://schemas.xceed.com/wpf/xaml/toolkit">
<DataTemplate DataType="{x:Type p:MessageRuleDataEditorViewModel}" x:Key="{x:Type p:MessageRuleDataEditorViewModel}">
(...)
</DataTemplate>
(...)
</ResourceDictionary>
What should I do to embed DataTemplates correctly in ContentPresenter's resources?
if you define this data template in your app.xaml:
<DataTemplate DataType="{x:Type p:MessageRuleDataEditorViewModel}">
<TextBlock Text="testing" />
</DataTemplate>
and then do something like:
<ListBox ItemsSource="{Binding MyCollectionOfMessageRuleDataEditorViewModels}"/>
then your data template is automatically applied to every object of that same type. dont define a key for your global templates

Flowdocument and Databinding

After thoroughly reading the documentation on flowdocuments (in WPF) it seems as though flowdocument does not easily support databinding. PLEASE say this is not true! I have a listbox using the sample data in Expression Blend and I inserted a textblock into the flowdocument. The textblock (text property) is databinded to the string data in the listbox. After running the project I would expect that the textblock text changes as the listbox selection changes, but nothing happens. Databinding is not working. What is the easiest way to make databinding work with flowdocument?
Here is the XAML.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication1.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<DataTemplate x:Key="ItemTemplate">
<StackPanel>
<TextBlock Text="{Binding Property1}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource SampleDataSource}}">
<FlowDocumentScrollViewer Margin="120,64,256,126">
<FlowDocument>
<Paragraph><Run Text="Only A Test"/><InlineUIContainer>
<TextBlock TextWrapping="Wrap" Text="{Binding Collection[0].Property1}" Height="56" Width="112"/>
</InlineUIContainer></Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
<ListBox ItemTemplate="{DynamicResource ItemTemplate}" ItemsSource="{Binding Collection}" Margin="0,83.847,52,62.153" HorizontalAlignment="Right" Width="200"/>
</Grid>
</Window>
According to this Microsoft documentation in April 2009, this is not possible:
While there are many great features in flow documents, if your documents are generated from dynamic data, you have a bit of a problem: there is no support for data binding in flow documents. The flow document elements (Section, Table, Run, Paragraph and the like) are dependency objects, but don't define any dependency properties that would allow you to dynamically change or generate content.

Use DataTemplate in WPF with a mocked object

I have following xaml code:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding MainWindow, Source={StaticResource Locator}}">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:KeyboardViewModel}">
<vw:Keyboard />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:WelcomeViewModel}">
<vw:Welcome />
</DataTemplate>
</Window.Resources>
<DockPanel>
<DockPanel>
<ContentControl Content="{Binding Path=Workspace}" />
</DockPanel>
</DockPanel>
</Window>
When Workspace is KeyboardViewModel, then the UserControl Keyboard is shown. When Workspace is Welcome, then the Welcome screen is shown. But when I test I mock the ViewModels with Moq. Workspace then get the type IKeyboardViewModelProxyxxxxxxxxxxxxx (where xxxxxxx is a random string), that don't maps to KeyboardViewModel in the DataTemplate and WPF don't now wish DataTemplate to show.
When I use the real KeyboardViewModel, it is no problem.
Can I fix it somehow, or do I have to redesign it?
I'm having a similar issue (without using Moq however). A PARTIAL solution that I used is to inherit both KeyboardViewModel and KeyboardViewModelMock from abstract KeyboardViewModelAbstract. Then you can do:
<DataTemplate DataType="{x:Type vm:KeyboardViewModelAbstract}">
<vw:Keyboard />
</DataTemplate>
Which will work for both, the real model object and the mock.
Unfortunately this solution doesn't scale when you're dealing with models that already have a base class or have any kind of inheritance involved. I'd be great if DataTemplate could be used with interfaces, but they can't.
You can omit the DataType="{x:Type vm:KeyboardViewModel}". If you do that, it is not expecting an instance of type KeyboardViewModel to bind against anymore but only an object of any type that just has all properties that are used in the template.

Binding to a data template control property

Is it possible to bind something to a property of a control in a data template entirely in XAML? The following code is a simplified version of the problem I'm running into. I'd like the text of the TextBlock (displayName) to be updated as the user types in the TextBox located in the DataTemplate.
<Window x:Class="WpfApplication4.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfApplication4="clr-namespace:WpfApplication4"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate DataType="{x:Type WpfApplication4:Foo}">
<TextBox Text="{Binding Path=Name}" />
</DataTemplate>
<WpfApplication4:Foo x:Key="testObject" Name="This is a test" />
</Window.Resources>
<StackPanel>
<TextBlock x:Name="displayName" Margin="5" />
<ContentControl x:Name="contentControl" Margin="5" Content="{StaticResource testObject}" />
</StackPanel>
No, at least, not from XAML. You could write code to traverse the visual tree and find the element you want to bind to, but that would be nasty.
But in your particular example, would it not make sense to just bind the TextBlock to the same data object (Foo instance)?

Resources