How to Bind To Data within a Datatemplate of a ContentControl - wpf

I have the following simplified Example:
<Window x:Class="TemplateBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="pack://application:,,,/TemplateBinding;component/PersonTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ContentControl ContentTemplate="{StaticResource PersonTemplate}" />
</Grid>
</Window>
With:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="PersonTemplate">
<Border Width="100" Height="100" Background="RosyBrown">
<TextBlock Text="{Binding Path=FirstName}" VerticalAlignment="Center" TextAlignment="Center"/>
</Border>
</DataTemplate>
</ResourceDictionary>
as my DataTemplate in a separate ResourceDictionary file.
I set my DataContext in the Construcor of my MainWindow and have verified it by just displaying the first name like this: <ContentControl Grid.Row="1" Content="{Binding FirstName}"/>.
In an other scenario wher I use a DataTemplate with a ListBox I do the Binding exactly the same way in my DataTemplate and it just works.
I know that the DataTemplate is working except the binding because it correctly shows the size and background color.
What am I doing wrong? How would the Binding in my DataTemplate have to look?

You need to bind the Content property of the ContentControl
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource PersonTemplate}" />
This will set the DataContext of the ContentControl as Content of the control.
Setting only the ContentTemplate property is not enough. The ContentControl does not implicitly use its DataContext as Content.

Related

How to allow DataContext to pass through to a CustomControl.ContentTemplate [duplicate]

I have the following simplified Example:
<Window x:Class="TemplateBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="pack://application:,,,/TemplateBinding;component/PersonTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ContentControl ContentTemplate="{StaticResource PersonTemplate}" />
</Grid>
</Window>
With:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="PersonTemplate">
<Border Width="100" Height="100" Background="RosyBrown">
<TextBlock Text="{Binding Path=FirstName}" VerticalAlignment="Center" TextAlignment="Center"/>
</Border>
</DataTemplate>
</ResourceDictionary>
as my DataTemplate in a separate ResourceDictionary file.
I set my DataContext in the Construcor of my MainWindow and have verified it by just displaying the first name like this: <ContentControl Grid.Row="1" Content="{Binding FirstName}"/>.
In an other scenario wher I use a DataTemplate with a ListBox I do the Binding exactly the same way in my DataTemplate and it just works.
I know that the DataTemplate is working except the binding because it correctly shows the size and background color.
What am I doing wrong? How would the Binding in my DataTemplate have to look?
You need to bind the Content property of the ContentControl
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource PersonTemplate}" />
This will set the DataContext of the ContentControl as Content of the control.
Setting only the ContentTemplate property is not enough. The ContentControl does not implicitly use its DataContext as Content.

wpf, binding data to child of another usercontrol

i am trying to bind a Textblock's Text property of MainWindow to a Listbox's Items.Count of another Usercontrol, and the textblock of Mainwindow failed to read the value of binding source.
but if both textblock and listbox belong to the same usercontrol or window, the binding is fine.
i know i can define a property of in the usercontrol and make it exposed to other framework elements(e.g. textblock) of Mainwindow, and then it can be bond.
i just cannot understand why the binding to the listbox of another usercontrol failed. any suggestion will be greatly appreciated.
here below is the sample i made for better explanation.
UserControl Xaml:
<UserControl x:Class="stackoverFlow.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:local="clr-namespace:stackoverFlow"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<ListBox x:Name="lbDemo">
<ListBoxItem>
<TextBlock Text="element 01"/>
</ListBoxItem>
<ListBoxItem>
<TextBlock Text="element 02"/>
</ListBoxItem>
<ListBoxItem>
<TextBlock Text="element 03"/>
</ListBoxItem>
</ListBox>
<!--binding to the control of the same usercontrol is fine-->
<TextBlock Text="{Binding ElementName=lbDemo,Path=Items.Count,StringFormat='there are {0} items in the listbox.'}"/>
</StackPanel>
</UserControl>
MainWindow Xaml:
<Window x:Class="stackoverFlow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:stackoverFlow"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<local:UserControl1 x:Name="userControlDemo"/>
<Separator Margin="0 5 0 15"/>
<!--binding failed-->
<TextBlock Text="{Binding ElementName=userControlDemo,Path=lbDemo.Items.Count, StringFormat='there are {0} items in the listbox of Usercontrol1'}"/>
</StackPanel>
</Window>
In your User control, you should add a get accessor like this:
public int ListCount {
get {
return this.lbDemo.Items.Count;
}
}
And in your Main Window, you call it like this:
<TextBlock Text="{Binding ElementName=userControlDemo, Path=ListCount, StringFormat='there are {0} items in the listbox of Usercontrol1'}" />

UserControl databinding inside ListView fails

I have a custom control, here simplified:
<UserControl x:Class="WPF.TestControl"
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:WPF"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
</Grid>
</UserControl>
With usage:
<local:TestControl Grid.Row="0">
<Border BorderBrush="Red" BorderThickness="1">
<TextBlock Text="{Binding Path=Message, Mode=OneWay}"/>
</Border>
</local:TestControl>
So far so good, that works.
However, when used as part of a DataTemplate for a ListView, the binding no longer works:
<ListView ItemsSource="{Binding Path=Items}" Grid.Row="2">
<ListView.Resources>
<DataTemplate DataType="{x:Type local:TestViewModel}">
<local:TestControl>
<TextBlock Text="{Binding Path=Message, Mode=OneWay}"/>
</local:TestControl>
</DataTemplate>
</ListView.Resources>
</ListView>
What incantations are needed to make the databinding above work?
You are creating an infinite loop.
The UserControl already is a ContentControl as it derives from it. So as long as you don't override its ControlTemplate, you should not add a ContentPresenter to it, which binds to the TemplatedParent while being used inside a template.
The trigger of this misery is the TemplatedParent binding. The TestControl is part of a template. The template is applied to the ListViewItem, which is therefore the templated parent of TestControl.
Now the value of the binding source of the ContentPresenter inside the TestControl is the value of the ListViewItem.Content property, which holds the TestControl itself. This way you add the templated parent ListViewItem.Content, which is the TestControl, to the ContentPresenter of TestControl, which now contains the same TestControl (itself), which binds the templated parent ListViewItem.Content, which is the TestControl, to the ContentPresenter of TestControl, which now contains the same TestControl (itself), which binds the templated parent... StackOverflow.
If you want to use the UserControl like a ContentControl i.e. to display the visuals of other "external" controls, then use it like the ContentCobtrol it is:
TestControl
<UserControl x:Class="TestControl">
</UserControl>
DataTemplate
<DataTemplate DataType="{x:Type local:TestViewModel}">
<local:TestControl>
<TextBlock Text="{Binding Path=Message, Mode=OneWay}"/>
</local:TestControl>
</DataTemplate>
Or if the content is more complex and advanced than simple presentation, you should override the ControlTemplate:
TestControl
<UserControl x:Class="TestControl">
<UserControl.Template>
<ControlTemplate TargetType="local:TreeIndex">
<Border>
<Grid>
...
<ContentPresenter Content="{TemplateBinding Content}" />
</Grid>
</Border>
</ControlTemplate>
</UserControl.Template>
</UserControl>
DataTemplate
<DataTemplate DataType="{x:Type local:TestViewModel}">
<local:TestControl>
<TextBlock Text="{Binding Path=Message, Mode=OneWay}"/>
</local:TestControl>
</DataTemplate>

StackPanel visibility VS Grid Visibility

I am using MVVM application and there is something that don't understand..
What is the difference between StackPanel visibility and Grid Visibility.
if I have this Grid...
<UserControl x:Class="Envitech.Setup.Presentation.Views.MonitorScreenViews.MonitorAlertViews.MonitorAlertView" 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" mc:Ignorable="d" d:DesignHeight="438" d:DesignWidth="842" xmlns:popup="clr-namespace:Envitech.Setup.Presentation.Views.GlobalViews">
<DockPanel DataContext="{Binding MonitorAlertViewModel}" Width="824" HorizontalAlignment="Left" VerticalAlignment="Top" Height="435">
<Grid DataContext="{Binding CurrentMonitorAlert}" Height="422" Visibility="{Binding Path=NoMonitorsMessageVisibility, Converter={StaticResource visibilityConverter}}">
<Label Content="Value" Height="28" HorizontalAlignment="Left" Margin="10,103,0,0" VerticalAlignment="Top" />
</Grid>
</DockPanel>
</UserControl>
visibility does not work, but if i do it this way...
<UserControl x:Class="Envitech.Setup.Presentation.Views.MonitorScreenViews.MonitorAlertViews.MonitorAlertView" 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" mc:Ignorable="d" d:DesignHeight="438" d:DesignWidth="842" xmlns:popup="clr-namespace:Envitech.Setup.Presentation.Views.GlobalViews">
<DockPanel DataContext="{Binding MonitorAlertViewModel}" Width="824" HorizontalAlignment="Left" VerticalAlignment="Top" Height="435">
<StackPanel Visibility="{Binding Path=NoMonitorsMessageVisibility, Converter={StaticResource visibilityConverter}}">
<Grid DataContext="{Binding CurrentMonitorAlert}" Height="422">
<Label Content="Value" Height="28" HorizontalAlignment="Left" Margin="10,103,0,0" VerticalAlignment="Top" />
</Grid>
</StackPanel>
</DockPanel>
</UserControl>
visibility works just fine.
Why?
The DataContext of the Grid is CurrentMonitorAlert. The DataContext of the StackPanel is MonitorAlertViewModel. Thus, the binding to NoMonitorsMessageVisibility is resolving against the wrong thing in your Grid case.
Setting the DataContext like that all over your view is somewhat unorthodox. Normally when doing MVVM you let WPF handle setting the DataContext (except possibly at the root level) and use deeper paths in your bindings if necessary. You might want to consider taking that approach.

WPF: How do I handle events in a DataTemplate that is in a separate file?

I have a DataTemplate with a ListView control. This DataTemplate is located in Templates.xaml (which is a ResourceDictionary). Template.xaml is then included into my main UserControl SourceManager.xaml through ResourceDictionary.MergedDictionaries. I want to raise the SelectionChanged event of the DataTemplate's ListView but I want the handler in the code behind to be in SourceManager.xaml.cs.
How can I achieve that?
Templates.xaml:
<ResourceDictionary x:Class="LawBib.Templates"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="SectionTemplate">
<StackPanel>
<TextBlock Text="{Binding XPath=#Title}" />
<ListView x:Name="GroupList" ItemsSource="{Binding XPath=Source}">
<ListView.Template>
<ControlTemplate>
<WrapPanel IsItemsHost="True">
</WrapPanel>
</ControlTemplate>
</ListView.Template>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="images/source.png" />
<TextBlock Text="{Binding XPath=#Title}" HorizontalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</DataTemplate>
SourceManager.xaml:
<UserControl x:Class="LawBib.SourceManager"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Background="#f5f7f8">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources.xaml" />
<ResourceDictionary Source="Templates.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
...
</UserControl>
Since SelectionChanged is a RoutedEvent, you can apply it to your UserControl like so:
<UserControl ...
ListView.SelectionChanged="MyEventHandler" />
Be aware that this event handler will be called for all Selector derived classes (as Selector is where the event is defined and raised) that are descendants of your UserControl, which includes ComboBox, Menu, ListBox, etc.
Create a Behavior
drop it in the Data Template.
that's it.

Resources