I am extending the ItemsControl
(class EnhancedItemsControl : ItemsControl
), because I want to add several dependecy properties to it - like AlternativeContent
which will be displayed when there are no items in collection (think of 'enter a search terms and hit search' label in a itemscontrol for results of search).
I have subclassed ItemsControl
and added an AlternativeContent
dep. property of type FrameworkElement
to it. Now I want to provide default style in Themes/Generic.xaml (I have added ThemeInfoAttribute
to AsseblyInfo, and provided metadata in static costructor as said in this excellent tutorial).
The style contains a ControlTemplate
, and I need to use second ControlTemplate
inside of ItemsControl template, where I add a ContentPresenter
that should show the AlternativeContent
.
Now, my problem is how do I tell the ContentPresenter
that it should take its content from the top-level EnhancedItemsControl
? If I were inside style's ControlTemplate
, I would use:
Content="{Binding AlternativeContent, RelativeSource={RelativeSource TemplatedParent}}"
but as I am in ItemsControl
's ControlTemplate
inside style's ControlTemplate
, this obviously doesn't work, I'd need to refer not to parent template, but to grandparent template, however, TemplateBinding
doesn't have the AncestorLevel
parameter.
I also tried:
Content="{Binding AlternativeContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WPFControls:EnhancedItemsControl}}}"
but that results in empty ContentPresenter
as well. I cannot name the TemplatedParent
(because it is outside the ControlTemplate
), so I cannot refer to it by name. I cannot use TemplatedParent
RelativeBinding
, because that doesn't reach over two levels of controltemplates. And RelativeSource
FindAncestor
strangely doesn't work.
Any idea how to solve this? Thank you!
Generic.xaml (excerpt):
<Style TargetType="{x:Type WPFControls:EnhancedItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type WPFControls:EnhancedItemsControl}">
<ItemsControl
x:Name="RootItemsControl"
ItemsSource="{Binding}"
ItemTemplate="{TemplateBinding ItemTemplate}"
Background="{TemplateBinding Background}"
HorizontalContentAlignment="Stretch"
>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer
x:Name="ContentScrollViewer"
CanContentScroll="False"
>
<StackPanel
x:Name="InnerPanel"
>
<ItemsPresenter
Width="{Binding ActualWidth, ElementName=InnerPanel}"
MaxWidth="{Binding ActualWidth, ElementName=InnerPanel}"
HorizontalAlignment="Stretch"
/>
<ContentPresenter
Content="{Binding AlternativeContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WPFControls:EnhancedItemsControl}}}"
>
<ContentPresenter.Style>
<Style>
<Setter Property="ContentPresenter.Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger
Binding="{Binding Items.Count, ElementName=RootItemsControl}"
Value="0">
<Setter Property="ContentPresenter.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
</StackPanel>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Control code:
public class EnhancedItemsControl : ItemsControl
{
static EnhancedItemsControl()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(EnhancedItemsControl),
new FrameworkPropertyMetadata(typeof(EnhancedItemsControl)));
}
public FrameworkElement AlternativeContent
{
get { return (FrameworkElement)GetValue(AlternativeContentProperty); }
set { SetValue(AlternativeContentProperty, value); }
}
// Using a DependencyProperty as the backing store for AlternativeContent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AlternativeContentProperty =
DependencyProperty.Register("AlternativeContent", typeof(FrameworkElement), typeof(EnhancedItemsControl), new UIPropertyMetadata(null));
}
Usage (a List<string>
is provided as DataContext
):
<WPFControls:EnhancedItemsControl Height="120" x:Name="EnhancedCollection"
>
<WPFControls:EnhancedItemsControl.AlternativeContent>
<WPFControls:CenteredLabel>
Alternative content
</WPFControls:CenteredLabel>
</WPFControls:EnhancedItemsControl.AlternativeContent>
<WPFControls:EnhancedItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</WPFControls:EnhancedItemsControl.ItemTemplate>
</WPFControls:EnhancedItemsControl>
Oops, my bad,
Content="{Binding AlternativeContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WPFControls:EnhancedItemsControl}}}"
actually works (but my markup was more complicated than the one in example, and the content was not shown because of other bug...)