I have a UserControl with a Template property that I’ve set up as a DependencyProperty:
public partial class TemplateDetail : UserControl
{
public static readonly DependencyProperty _templateProperty =
DependencyProperty.Register(
"Template",
typeof(Template),
typeof(TemplateDetail)
);
public TemplateDetail()
{
InitializeComponent();
Template = new Template();
DataContext = Template;
}
public Template Template
{
get
{
return (Template)GetValue(_templateProperty);
}
set { SetValue(_templateProperty, value); }
}
}
I’m trying to use this UserControl in the XAML for a Window, SaveTemplateDialog, and I’m trying to set its Template property to the Template property in my SaveTemplateDialog class:
<local:TemplateDetail HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
MinWidth="100" Template="{Binding}"
Width="{Binding ElementName=Scroller, Path=ViewportWidth}"/>
The DataContext in SaveTemplateDialog is set to its Template property, which is also a dependency property. However, for the XAML above, Template="{Binding}" is underlined in blue in Visual Studio and it says:
A ‘Binding’ cannot be set on the ‘Template’ property of type ‘TemplateDetail’. A ‘Binding’ can only be set on a DependencyProperty of a DependencyObject.
Sure enough, when I load my app, the contents of the Template that TemplateDetail tries to display is blank, when I know there are values in it. Why does it give this message if I’ve registered TemplateDetail.Template as a dependency property?
Edit: now I’m quite confused. In my SaveTemplateDialog window, I’m doing the following to pass along the template to the TemplateDetail UserControl:
<local:TemplateDetail HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
TemplateData="{Binding Path=NewTemplate, Mode=OneWay}"
Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3"/>
I know Path=NewTemplate is getting the right data because I also have this XAML in SaveTemplateDialog, which shows the value I would expect in the MyType property of NewTemplate:
<TextBlock Text="{Binding Path=NewTemplate.MyType}" />
That TextBlock shows what I expect. However, apparently the right data is not getting to TemplateDetail or I’m not binding correctly there, because I can’t get that same MyType property (or the other Template properties) to show up in TemplateDetail. In TemplateDetail:
<TextBlock Text="{Binding Path=TemplateData.MyType}" Grid.Row="2" Grid.Column="2"
HorizontalAlignment="Stretch" Width="200"/>
And here’s the TemplateDetail class as it is now:
public partial class TemplateDetail : MainWindowUserControl
{
public static readonly DependencyProperty TemplateDataProperty =
DependencyProperty.Register(
"TemplateData",
typeof(Template),
typeof(TemplateDetail),
new PropertyMetadata(
new Template("Default Template", null)
)
);
public TemplateDetail()
{
InitializeComponent();
DataContext = this;
}
public Template TemplateData
{
get
{
return (Template)GetValue(TemplateDataProperty);
}
set { SetValue(TemplateDataProperty, value); }
}
}
You should change the name of your DependencyProperty to the use the standard convention like PropertyNameProperty instead of naming like a field. When you set something in XAML it calls SetValue and expects the property to be named using the string name (“Template” here) followed by “Property”.
UPDATE for second question:
When you set
DataContext = thisin your TemplateDetail constructor that is causing the source for your Path=NewTemplate Binding to be changed from the inherited DataContext to the TemplateDetail control itself. You should be seeing an error in your Output window that the NewTemplate property can’t be found on the object of type TemplateDetail. Instead of resetting the DataContext of the UserControl itself, I will usually give the root layout Panel in my UserControl XAML an x:Name and setLayoutRoot.DataContext = thisin the constructor instead.