I have this parent control that displays its CurrentViewModel, and instance of ViewModelWithExam, using the data templates provided.
<UserControl x:Class="Gui.Views.ExamsTabView"
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:views="clr-namespace:Gui.Views.Tabs.ExamsTabViews"
xmlns:viewmodels="clr-namespace:Gui.ViewModels"
xmlns:data="clr-namespace:DataManagement;assembly=DataManagement"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate DataType="{x:Type viewmodels:ExamInfoViewModel}">
<views:ExamInfoView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:ExamAcquireImageViewModel}">
<views:ExamAcquireImageView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:ExamEditImageViewModel}">
<views:ExamEditImageView/>
</DataTemplate>
<!--Reuseable control to display the simple exam header-->
<DataTemplate x:Key="ExamHeader" DataType="{x:Type data:Exam}">
<views:ExamHeaderView/>
</DataTemplate>
</UserControl.Resources>
<Grid VerticalAlignment="Stretch" Margin="20">
<ContentControl Content="{Binding CurrentViewModel}"/>
</Grid>
</UserControl>
Any of the views can use the ExamHeader to display their Exam property:
<ContentControl ContentTemplate="{StaticResource ExamHeader}" Content="{Binding Exam}"/>
The ExamHeader template, however, exhibits odd behavior. If I remove the x:Key property from the template, and remove the ContentTemplate property from the ContentControl, and then launch the app, the header displays, as expected.
While the app is running, if I add back the x:Key, the header disappears and is replaced by the classname of the Exam. As expected. If I then add back the ContentTemplate to point to the header template, the header template shows. It works.
However, if I launch the app with the x:Key and ContentTemplate in place, then loading a page with a header puts the application in break mode, saying "The resource 'ExamHeader' could not be found." Indeed, when the app isn't running, this error appears under the ControlTemplate property.
But, to say again, if I add in the x:Key and ContentTemplate while the app is running, it work fine!
How do I fix this?
It might be a resource ordering issue. Try moving the ExamHeader resource to the top of your resources:
<UserControl.Resources>
<!--Reuseable control to display the simple exam header-->
<DataTemplate x:Key="ExamHeader" DataType="{x:Type data:Exam}">
<views:ExamHeaderView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:ExamInfoViewModel}">
<views:ExamInfoView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:ExamAcquireImageViewModel}">
<views:ExamAcquireImageView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:ExamEditImageViewModel}">
<views:ExamEditImageView/>
</DataTemplate>
</UserControl.Resources>
Related
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}" ...>
None of my datatemplates are showing up based on DataType of the DataContext. The actual object being passed to the DataContext of UserControl is an Entity (EntityFrameWork 6.0).
I am specifying DataType="{x:Type pf:Promotion}" which is the name of a POCO class that the entity is based on.
(xmlns:pf="clr-namespace:PFModel;assembly=PFModel")
I am lost here, don't know where the problem lies. Thankful for any help or hints.
<UserControl x:Class="PFPromoEditor.UserControls.CenterEditor"
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:local="clr-namespace:PFPromoEditor.UserControls"
xmlns:wpfTool="clr-namespace:Xceed.Wpf.Toolkit;assembly=Xceed.Wpf.Toolkit"
xmlns:pf="clr-namespace:PFModel;assembly=PFModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Border BorderBrush="#FF000000" Margin="2" Padding="5" BorderThickness="1,1,1,1" CornerRadius="8,8,8,8">
<ContentControl>
<ContentControl.Resources>
<DataTemplate DataType="{x:Type pf:Promotion}">
<TextBox Text="Promotion DATA type" />
</DataTemplate>
<DataTemplate DataType="{x:Type pf:Casino}">
<TextBox Text="Casino DATA type" />
</DataTemplate>
<DataTemplate DataType="{x:Type pf:Progressive}">
<TextBlock Text="Progressive DATA type" />
</DataTemplate>
<DataTemplate DataType="{x:Type pf:Detail}">
<TextBox Text="Detail DATA type" />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</Border>
</Grid>
<local:CenterEditor x:Name="CenterContent" DataContext="{Binding ElementName=promoMenu,Path=MySelectedItem }"/>
Answer to first question, the control has datacontext properly set with an entity, of either Promotion, Casino, Promotion or Detail.
I have also tried it with a bound property like:
<DataTemplate DataType="{x:Type pf:Progressive}">
<Grid>
<TextBlock Text="Progressive DATA type" />
<TextBox Text="{Binding Path=Detail.Title, FallbackValue= 'Select any Item in list to edit'}"/>
</Grid>
</DataTemplate>
Still nothing, blank.
I have also just placed something like:
<TextBox Text="{Binding Path=Detail.Title, FallbackValue= 'Select any Item in list to edit'}"/>
In the code above like thus:
</DataTemplate>
</ContentControl.Resources>
<TextBox Text="{Binding Path=Detail.Title, FallbackValue= 'Select any Item in list to edit'}"/>
</ContentControl>
And the textbox binding is fine, the entity is there and I get the expected data.
Its not a data binding problem. I have context and I have a proper object.
I thought of a couple other things I need to try. Let me get back to you.
It turns out the thing I was doing wrong (besides being stubborn about what I thought was wrong, and trying to solve my problem at 3 am, and over thinking the problem in general) was leaving out:
Content="{Binding}"
which BTW way I missed in the comments, I recall reading datacontext, that happened because I was tired and frustrated.
The thing that solved the problem was this modification to the content control
<ContentControl Content="{Binding}" >
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
I've modified my question since it has changed focus when trying things out.
I narrowed the problem down to the following...
I try to bind the selected Item of a TreeView to a StackPanel (or some other container that can hold User Controls). This container will then display a UserControl, depending on the type of the selected item.
Here is the xaml of the StackPanel (both treeview and stackpanel are in the same window ==> different grid column)
<StackPanel Grid.Column="2" MinWidth="500" DataContext="{Binding ElementName=myTree, Path=SelectedItem, Mode=OneWay}">
<StackPanel.Resources>
<DataTemplate DataType="{x:Type mvTypes:MyTypeA}">
<controls:UserControlA DataContext="{Binding}" />
</DataTemplate>
<DataTemplate DataType="{x:Type mvTypes:MyTypeB}">
<controls:UserControlB DataContext="{Binding}" />
</DataTemplate>
</StackPanel.Resources>
</StackPanel>
When I place a user control directly under the stackpanel (not in the resources), it displays it with the selected object as their datacontext.
Idem if I place a TextBox in it, it will show the correct type of the selected item.
<TextBox Name="textBox1" Text="{Binding}" />
For some reason, placing it within a DataTemplate (even without setting the DataType) results in nothing to display.
Any sugestions. I'm thinking that maybe a StackPanel is not the right control for this, though I can't seem to find other controls that look suitable as containers like this.
Thanks in advance.
Replace the StackPanel in your example with ContentPresenter and instead of DataContext set the Content property. That should work.
Although you have set the Binding on the second custom control, are you setting the DataContext, as the binding is the route to the information and the DataContext is the information it applies this binding information to.
Andrew
You can create a UserControl to display the TreeView and the selection info on the right, all in one. It saves you from creating any custom control. A custom control is basically unnecessary since you do not create anything which didn't exist before.
<UserControl x:Class="NameSpace.SelectionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="namespace.Controls"
Height="300" Width="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TreeView Name="customTree">
<!--Items go here-->
</TreeView>
<StackPanel Grid.Column="1" MinWidth="50" DataContext="{Binding ElementName=customTree, Path=SelectedItem, Mode=OneWay}">
<StackPanel.Resources>
<DataTemplate DataType="{x:Type StylingTest:CustomViewModelA}">
<controls:CustomADetailsControl />
</DataTemplate>
<DataTemplate DataType="{x:Type StylingTest:CustomViewModelB}">
<controls:CustomBDetailsControl />
</DataTemplate>
</StackPanel.Resources>
<TextBlock Text="{Binding}"/>
</StackPanel>
</Grid>
</UserControl>
Any other custom behaviour, I'm sure you could create or set in styles/templates here.
Also, you might find one of my other answers useful.
Good luck with wpf, cheers.
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)?