WPF: Inheriting from HeaderedContentControl - wpf

I would like to create a simple control that inherits from HeaderedContentControl, and has some basic dependency properties called Title, Subtitle, Icon. I would like to be able to provide a default header template that databinds these properties. For this example, I have named this class HeaderedView.
I am having trouble in providing a default header template that can bind to the properties defined on the HeaderedView. I am experimenting with markup like the following:
<Style TargetType="{x:Type local:HeaderedView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedContentControl}">
<StackPanel>
<Grid>
<ContentPresenter ContentSource="Header"/>
</Grid>
<Grid>
<ContentPresenter ContentSource="Content"/>
</Grid>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock Text="{TemplateBinding local:HeaderedView.Title}" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Unfortunately, the Title is not being displayed.
The header template must be replaceable (which is why I want to utilize the HeaderedContentControl).
Every time I seem to want to inherit from this control, I seem to struggle with the implementation. Any help would be greatly appreciated!

In your template, you are using a ContentPresenter to display the Header, but you're not telling the ContentPresenter that it needs to use the HeaderTemplate. You should be able to do this in order to see your custom HeaderTemplate applied:
<ContentPresenter ContentSource="Header" ContentTemplate="{TemplateBinding HeaderTemplate}" />
Also, if you're only planning on changing the HeaderTemplate, then you don't need to override the Template in the first place. The default HeaderedContentControl will apply your HeaderTemplate appropriately.

Related

WPF Custom Control can not take direct content

I can't put any direct content in my custom control, have a look:
<Style TargetType="local:MyCustomControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyCustomControl">
<Grid>
<Viewport3D />
<!-- the viewport is working (proof provided) -->
<!-- both borders are needed -->
<Border>
<Border>
<ContentPresenter />
</Border>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
the class is derived from Control, in the static constructor DefaultStyleKeyProperty.OverrideMetadata is set.
When I try to use MyCustomControl:
<local:MyCustomControl VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<TextBlock Margin="10,0,0,0" FontSize="16" Text="some test value" />
</local:MyCustomControl>
this error message is shown:
Cannot add content to object of type MyCustomControl
MyNamespace.MyCustomControl
What could be the problem? Is somthing wrong with the Contentpresenter?
I think you should bind your Content to your Presenter
<ContentPresenter Content="{TemplateBinding Content}"/>
Thanks ZerO, this was an excellent hint:
MyCustomControl derived from Control - now it derives from ContenControl.
After changing the base class I am now able to bind like suggested by ZerO.
<ContentPresenter Content="{TemplateBinding Content}"/>
problem solved!

Ensuring AdornmentDecorator within a ContentPresenter, but without a ControlTemplate

I've searched around quite a bit and can't seem to crack this nut.
I've got an app with a main view that changes dynamically, and to do this I use content presenter with a binding to a control:
<ScrollViewer Grid.Column="2" x:Name="StepScrollViewer">
<StackPanel Margin="20,20,20,500">
<ContentPresenter Content="{Binding MainControl}"/>
</StackPanel>
</ScrollViewer>
Then I change the MainControl at runtime in my view model. The problem is that the controls getting bound don't reliably display their error templates... I suspect it is for the reasons discussed here:
Validation ErrorTemplate not showing on data errors
But the fix for this problem doesn't seem to work for me because I'm not using a control template around my content presenter. When I wrap an AdornmentDecorator tag around my content presenter, it doesn't seem to fix the problem. It DOES work if I put an AdornmentDecorator inside each control I load into the contentpresenter (as the root element), but I'd like to avoid this repetition if possible.
Any insights?
UPDATE
I tried this approach suggested by Dennis, but to no avail. The control binds okay, but it works no better than the current approach (also shown commented below). Note: I tried it both with the AdornerDecorator as a singleton element the way Dennis has it, and surrounding the ContentPresenter, as shown below. Neither show any difference - the adorners around my controls all disappear when the MainControl binding is changed.
<UserControl.Resources>
<Style x:Key="MainContentControl" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid>
<AdornerDecorator>
<ContentPresenter Content="{Binding MainControl}"/>
</AdornerDecorator>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
.....
<ScrollViewer Grid.Column="2" x:Name="StepScrollViewer">
<StackPanel Margin="20,20,20,500" >
<ContentControl Style="{StaticResource MainContentControl}"/>
</StackPanel>
</ScrollViewer>
<!-- THE BELOW WORKS IF I SURROUND EACH BOUND CONTROL WITH adornerdecorator -->
<ScrollViewer Grid.Column="2" x:Name="StepScrollViewer">
<StackPanel Margin="20,20,20,500">
<ContentPresenter Content="{Binding MainControl}"/>
</StackPanel>
</ScrollViewer>
-->
Instead of using a ContentPresenter directly, I would instead use a ContentControl. A ContentControl is the base class for controls that contain other elements and have a Content property, e.g. Button.
Then you can override the template to have an AdornerDecorator next to the ContentControl. This is different to what you previously tried as now the ContentPresenter is part of the same visual tree as the Adorner.
<Style TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Edit: Forgot that the AdornerDecorator needs to wrap the container, not just sit side-by-side.

How to change TextBlock default properties for a ContentPresenter in a template

Based on the following code :
<GroupBox>
<GroupBox.Template>
<ControlTemplate TargetType="{x:Type GroupBox}">
<ContentPresenter TextElement.FontSize="28" />
</ControlTemplate>
</GroupBox.Template>
<TextBlock>Test</TextBlock>
</GroupBox>
I was expecting "Test" to be displayed with FontSize=28. But it uses the default size instead.
If I remove the TextBlock like this :
<GroupBox>
<GroupBox.Template>
<ControlTemplate TargetType="{x:Type GroupBox}">
<ContentPresenter TextElement.FontSize="28" />
</ControlTemplate>
</GroupBox.Template>
Test
</GroupBox>
The text is now the displayed with 28 as FontSize.
Shouldn't the property value be inherited when I use a TextBlock ?
This other question How do I Change the FontFamily on a ContentPresenter? doesn't help, as it works only for default content too.
This question also : How do I Change the FontFamily on a ContentPresenter?.
Both works whe you use the default content handler, but fails when you manually create a textblock.
Edit: As demonstrated in this other question, I've tried by simply using a ContentControl :
<StackPanel>
<StackPanel.Resources>
<ControlTemplate x:Key="UsingBorderTemplate" TargetType="{x:Type ContentControl}">
<Border BorderBrush="Red" BorderThickness="1" TextElement.FontFamily="Courier New" Margin="5">
<ContentPresenter/>
</Border>
</ControlTemplate>
<ControlTemplate x:Key="MyTemplate" TargetType="{x:Type ContentControl}">
<ContentPresenter TextElement.FontFamily="Courier New" Margin="5" />
</ControlTemplate>
</StackPanel.Resources>
<ContentControl Template="{StaticResource MyTemplate}">
I'm courier new!
</ContentControl>
<ContentControl Template="{StaticResource MyTemplate}">
<TextBlock>I'm default!</TextBlock>
</ContentControl>
</StackPanel>
You can change the template from "MyTemplate" to "UsingBorderTemplate" with the same result.
I had an odd problem with ContentPresenter. I remember that I have analyzed the source of the problem and have found out that it was by design - Probably you have here the same issue.
Look at this post, maybe it helps you.
I think the text that the content presenter is presenting is the GroupBox.Header, and you may just be tacking another TextBox in there that isn't part of the Group Box.
In your first code block, add the line below and see if that works:
<GroupBox.Header>Test</GroupBox.Header>
HTH,
Berryl

Silverlight: Binding a custom control to an arbitrary object

I am planning on writing a hierarchical organizational control, similar to an org chart. Several org chart implementations are out there, but not quite fit what I have in mind.
Binding fields in a DataTemplate to a custom object does not seem to work.
I started with a generic, custom control, i.e.
public class NodeBodyBlock : ContentControl
{
public NodeBodyBlock()
{
this.DefaultStyleKey = typeof(NodeBodyBlock);
}
}
It has a simple style in generic.xaml:
<Style TargetType="org:NodeBodyBlock">
<Setter Property="Width" Value="200" />
<Setter Property="Height" Value="100" />
<Setter Property="Background" Value="Lavender" />
<Setter Property="FontSize" Value="11" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="org:NodeBodyBlock">
<Border Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"
Background="{TemplateBinding Background}" CornerRadius="4" BorderBrush="Black" BorderThickness="1" >
<Grid>
<VisualStateManager/> ... clipped for brevity
</VisualStateManager.VisualStateGroups>
<ContentPresenter Content="{TemplateBinding Content}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My plan now is to be able to use this common definition as a base definition of sorts, with customized version of it used to display different types of content.
A simple example would be to use this on a user control with the following style:
<Style TargetType="org:NodeBodyBlock" x:Key="TOCNode2">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=NodeTitle}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
and an instance defined as
<org:NodeBodyBlock Style="{StaticResource TOCNode2}" x:Name="stTest"
DataContext="{StaticResource DummyData}" />
The DummyData is defined as
<toc:Node NodeNumber="mynum" NodeStatus="A"
NodeTitle="INLine Node Title!"
x:Key="DummyData"/>
With a simple C# class behind it, where each of the fields is a public property.
When running the app, the Dummy Data values simply do not show up in the GUI. A trivial test such as
<TextBlock Text="{Binding NodeTitle}" DataContext="{StaticResource DummyData}"/>
works just fine.
Any ideas around where I am missing the plot?
Update: Binding to the datacontext in the definition in generic.xaml works fine, but any binding in the ContentPresenter is lost.
Your control template is missing a binding on the ContentPresenter, it should look like this:-
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
I just ended up using this example as a base:
http://10rem.net/blog/2010/02/05/creating-customized-usercontrols-deriving-from-contentcontrol-in-wpf-4
Not quite sure what I missed, but the example works.

ItemContainerStyle in Custom Control Derived From ListBox

Please bear with me Silverlight Designer Gurus, this is compicated (to me).
I'm creating a custom control which derives form the Silverlight 3.0 ListBox. In an effort not to show tons of code (initially), let me describe the setup.
I have a class library containing a class for my control logic. Then I have a Themes/generic.xaml that holds the styling details. In generic.xaml, I have a style that defines the default layout and look for the ListBox where I'm setting a values for the Template, ItemsPanel and ItemTemplate.
In my test app, I add my control on to MainPage.xaml and run it and it works great. I dynamically bind data to my control and that works fine.
Now I want to set the ItemContainerStyle for my derived control. If I create a style in the MainPage.xaml file and set the ItemContainerStyle property to that control as in:
<dti:myControl x:Name="MyControl1" ItemContainerStyle="{StaticResource MyListBoxItem}"
Height="500"
Width="200"
Margin="10"
Background="AliceBlue"
/>
It works as expected.
However, I'd like to do this in the class library or, more specifically, in generic.xaml. I tried to this Setter to my current Style:
<Setter Property="ItemContainerStyle">
<Setter.Value>
<ControlTemplate>
<Grid Background="Red" Margin="3">
<ContentPresenter x:Name="contentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="Stretch" Margin="3"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
And it fails miserably with:
"System.ArgumentException: 'System.Windows.Controls.ControlTemplate' is not a valid value for property 'ItemContainerStyle'."
Note: This is not my actual style I'd like to use for ItemContainerStyle. I'm actually looking to plug in some VSM here for the various selected/unselected states of the a ListBoxItem (for a dynamically bound control).
So, to the question is how do I apply the ItemContainterStyle to my custom control when it's defined using generic.xaml? I do not want that property set when I actually use the control later on.
Thanks,
Beaudetious
You missed to put Style tag inside your Setter.Value. ItemContainerstyle explects a Style to ListBoxItem(Unless you subclassed ListBoxItem to your own derived version.)
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType=”{x:Type ListBoxItem}“ >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Background="Red" Margin="3">
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" HorizontalAlignment="Stretch" Margin="3"/>
</Grid>
</ControlTemplate>
<Setter.Value>
</Style>
</Setter.Value>

Resources