jbhelicon jbhelicon - 1 year ago 44
C# Question

Seperately Binding to Multiple Instances of a Templated Control

I'm looking to create a Grid of similar controls using a template and a MVVM pattern. Here is a simple description of the problem I'm having.
I first define a class Element to be instantiated for each element of the Grid:

public class Element : INotifyPropertyChanged
private string _dText = "Default";
public string dText { get { return _dText; } set { _dText = value; NotifyPropertyChanged("dText"); } }

internal Element(string aText) { dText = aText; }

public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(info));

Then I instantiate some Elements inside a ViewModel class that is the Page's DataContext, as follows:

public class MinVM
private Element _element0 = new Element("Element0 initialization text 0");
public Element element0 { get { return _element0; } set { _element0 = value; } }
private Element _element1 = new Element("Element1 initialization text 1");
public Element element1 { get { return _element1; } set { _element1 = value; } }

Now, in the XAML Page.PageResources I create a ControlTemplate designed to get me some nicely centered text, as follows:

<ControlTemplate x:Name="cTemplate" x:Key="CTemplate" TargetType="ContentControl">
<Grid x:Name="ctGrid" Background="Orange" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBox x:Name ="dtBox" Background="Orange" BorderThickness="0"
VerticalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap"
Text="{Binding dText, FallbackValue=Default control template text}">

Finally, in the Page content itself, I instantiate the grid and elements within it, as follows:

<ContentControl x:Name="element0" Grid.Row="0" Template="{StaticResource CTemplate}" Content="{Binding Path=element0}"></ContentControl>
<ContentControl x:Name="element1" Grid.Row="1" Template="{StaticResource CTemplate}" Content="{Binding Path=element1}"></ContentControl>

This all renders fine, as you would expect, but the binding doesn't work i.e., I can't change the default text, because WPF is looking for a field dText inside MinVM, not inside the Element. This is because ControlTemplate is ignoring Content="{Binding Path=element0}"

The obvious way to try and fix it is to use a DataTemplate instead of a ControlTemplate. I've done that - created a DataTemplate with exactly the same internals as the above ControlTemplate, with Key="DTemplate". I then change the instantiation XAML to

<ContentControl x:Name="element0" Grid.Row="0" ContentTemplate="{StaticResource DTemplate}" Content="{Binding Path=element0}"></ContentControl>
<ContentControl x:Name="element1" Grid.Row="1" ContentTemplate="{StaticResource DTemplate}" Content="{Binding Path=element1}"></ContentControl>

Now the binding works correctly, as you would expect - I can happily change the control text. But the rendering no longer works properly (the text is no longer centered in the Grid element, and the Orange no longer fills the Grid cell) because I've lost the ControlTemplate

So, I can either have it render correctly, or bind, but not both.

I know I'm missing something obvious, and any suggestions would be greatly appreciated.

Answer Source

If I understand what you need correctly then the following XAML works for me (you can just extract the Style):

<Window x:Class="WpfScratch.MainWindow"
        Title="MainWindow" Height="350" Width="525">
        <DataTemplate x:Key="ContentTemplate">
            <TextBox TextAlignment="Center" Text="The obvious way to try and fix it is to use a DataTemplate instead of a ControlTemplate. I've done that - created a DataTemplate with exactly the same internals as the above ControlTemplate, with Key=DTemplate. I then change the instantiation XAML to" TextWrapping="Wrap"/>
        <Style x:Key="ContentControlStyle1" TargetType="{x:Type ContentControl}">
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="VerticalAlignment" Value="Stretch"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="ContentTemplate" Value="{StaticResource ContentTemplate}"/>
            <Setter Property="Template">
                    <ControlTemplate TargetType="{x:Type ContentControl}">
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
        <ContentControl Margin="4" Style="{DynamicResource ContentControlStyle1}"/>
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download