WPF ContentPresenter overrides othe Controls on the same level - wpf

As in my last question I'm about to re-create an expander + it's toggle button.
Expander Template:
<ControlTemplate TargetType="Expander">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="15"/>
</Grid.RowDefinitions>
<ContentPresenter x:Name="ContentPresenter" Grid.Row="0"/>
<ToggleButton Grid.Row="1" >
....
</ToggleButton>
</Grid>
Now when I use this Custom Control in my Main View and add a Control (e.g. a Button):
<custom:FullWidthExpander Width="200" HeaderBackground="Gray">
<Button />
</custom:FullWidthExpander>
The ToggleButton (which is defined in my Expander Template above) gets overridden by this Button.

Are you binding the UserControl.Content to the Expander.Content property?
<!-- define a custom Template for the UserControl FullWidthExpander -->
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<!-- be sure to include the Content binding to pass the UC.Content to Expander -->
<Expander Content="{TemplateBinding Content}">
<!-- create a custom Template for this Expander -->
<Expander.Template>
<ControlTemplate TargetType="Expander">
<Grid>
<Grid.RowDefinitions>
<RowDefinition x:Name="ContentRow" Height="*"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Content="{TemplateBinding Content}"/>
<ToggleButton Grid.Row="1" Content="Test Toggle Button" />
</Grid>
</ControlTemplate>
</Expander.Template>
</Expander>
</ControlTemplate>
</UserControl.Template>
I did a quick test, and this works fine when using the control like this :
<local:FullWidthExpander>
<Button Content="Test Content Button"/>
</local:FullWidthExpander>
I also took a look at your other question, and if you have your ControlTemplate.Triggers here then you will also want to make sure to set Expander.IsExpanded to True in order to view your Content. Your trigger is hiding the top content row if IsExpanded=False, which is the default for an Expander.

When you do this, it means that the entire contents of the user control is replaced with the content you provided.
<custom:FullWidthExpander Width="200" HeaderBackground="Gray">
<Button />
</custom:FullWidthExpander>
To get around this you need to host the content little bit differently in your usercontrol
First add a dependency property for the user control
public object UserControlContent
{
get { return (object)GetValue(UserControlContentProperty); }
set { SetValue(UserControlContentProperty, value); }
}
public static readonly DependencyProperty UserControlContentProperty =
DependencyProperty.Register("UserControlContent", typeof(object), typeof(FullWidthExpander),
new PropertyMetadata(null));
Then bind this to the contentpresenter in the usercontrol's xaml also define a name for your usercontrol like x:Name="Root".
<Expander Header="My expander header">
<Expander.Template>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="15"/>
</Grid.RowDefinitions>
<ContentPresenter x:Name="ContentPresenter" Grid.Row="0" Content="{Binding UserControlContent, ElementName=Root}" />
<ToggleButton Grid.Row="1" Content="Togglebutton">
</ToggleButton>
</Grid>
</ControlTemplate>
</Expander.Template>
</Expander>
And finally you define the content content in maindwindow xaml as such
<custom:FullWidthExpander>
<custom:FullWidthExpander.UserControlContent>
<Button Content="Click me"/>
</custom:FullWidthExpander.UserControlContent>
</custom:FullWidthExpander>

Related

How to bind individual Border CornerRadius?

I made a round window with Border like this:
<Border CornerRadius="{Binding WindowCornerRadius}" Background="{StaticResource BackgroundDarkBrush}">
<Border.Effect>
<DropShadowEffect ShadowDepth="0" Opacity="0.2"></DropShadowEffect>
</Border.Effect>
</Border>
<Grid>
<Grid.RowDefinitions>
<!--Title Bar-->
<RowDefinition Height="{Binding TitleHeightGridLength}"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<!--Title Bar-->
<Border Background="{StaticResource TitleBrush}" CornerRadius="{Binding WindowCornerRadius,WindowCornerRadius,0,0}">
<Grid Grid.Column="0" Panel.ZIndex="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Button Style="{StaticResource SystemIconButton}" Command="{Binding MenuCommand}">
<Image Source="Images/Logo/Wink.png"></Image>
</Button>
</Grid>
</Border>
</Grid>
</Grid>
</Border>
As you see I changed Background of first GridRow and this prevents it to be round(To have corner radius) and I would like to correct it adding border to first GridRow But I failed. The code above gives me error in CornerRadius="{Binding WindowCornerRadius,WindowCornerRadius,0,0}" :
No constructor for type "Binding" has 4 arguments.
My question is am I doing it correct by adding another border? (If yes how can I Bind CornerRadius individually).
You cannot use a binding like this. A binding on CornerRadius will look for a Thickness-typed property from your DataContext, with the Path given inside the binding. The path is implicit in a binding, but you can set it explicitely : Property="{Binding Path=blabla}". More info here.
Does the CornerRadius dynamically change? Why?
Also have a look at this :
Binding only part of the margin property of WPF control

How to implement DateTimeAxis control template in System.Windows.Controls.DataVisualization.Toolkit

I want to override the default template but i have trouble implementing it.
I tried this:
<charting:Chart Title="{Binding Title}" Name="chChart" LegendStyle="{StaticResource LegendStyle1}">
<charting:Chart.Axes>
<charting:DateTimeAxis
x:Name="Abcisa"
Orientation="X"
ShowGridLines="True"
Title="{Binding Abscissa}">
<charting:DateTimeAxis.Template>
<ControlTemplate TargetType="{x:Type charting:DateTimeAxis}">
<Grid x:Name="AxisGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--generate content for x axis-->
<datavis:Title x:Name="AxisTitle" Grid.Row="1" />
</Grid>
</ControlTemplate>
</charting:DateTimeAxis.Template>
</charting:Chart>
But i got this for visual tree:
What should i do to create template for DateTimeAxis control?
You should set the Style property of the Title to the TitleStyle of the Axis. This is what the default template looks like:
<ControlTemplate TargetType="charting:DateTimeAxis">
<Grid x:Name="AxisGrid" Background="{TemplateBinding Background}">
<datavis:Title x:Name="AxisTitle" Style="{TemplateBinding TitleStyle}" />
</Grid>
</ControlTemplate>
But you also need to add some data series to the chart for the axis to show up. Please refer to the following article for more information about this and an example: https://www.codeproject.com/Articles/196502/WPF-Toolkit-Charting-Controls-Line-Bar-Area-Pie-Co

Custom control with several datatemplates/contentpresenters

In a card game I need a control that shows a collection of 4 items (North, East, South and West) in a grid. The grid is 3x3 and it shows content in cells (0,1) (1,0) (1,2) and (2,1) respectively.
<bridge:SeatCollection ItemsSource="{Binding Path=Hands}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</bridge:SeatCollection>
The above I have already working and is done by creating a control that inherits from ItemsControl and does something smart in GetContainerForItemOverride as described in another post on StackOverflow.
My question is how I can add another property to the control that contains the content that must be shown in the middle cell and how can the control render that content?
Using the control should be like:
<bridge:SeatCollection ItemsSource="{Binding Path=Hands}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<bridge:SeatCollection.MiddleCell>
<TextBlock Text="Content for middle cell"/>
</bridge:SeatCollection.MiddleCell>
</bridge:SeatCollection>
According to this article (http://www.ikriv.com/dev/wpf/displayingcontent/index.html) I can add a property and use that property in a ContentPresenter
public object MiddleCell
{
get { return (object)GetValue(MiddleContentProperty); }
set { SetValue(MiddleContentProperty, value); }
}
public static readonly DependencyProperty MiddleContentProperty =
DependencyProperty.Register("MiddleCell", typeof(object), typeof(SeatCollection), new PropertyMetadata(0));
But where and how in code can I add this content to my grid's middle cell?
I don't know what smart thing you're doing in GetContainerForItemOverride so I'll just ignore that part. Your bridge:SeatCollection is a custom control that derives from ItemsControl, correct? So it will have a default style defined in Themes\Generic.xaml. Change that style so it has a ContentPresenter with the ContentSource set to MiddleCell (your DP). Something like this:
<Style TargetType="{x:Type bridge:SeatCollection}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type bridge:SeatCollection}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter ContentSource="MiddleCell" Grid.Row="1" Grid.Column="1" />
<!-- This is for the 'Hands' - Change as needed -->
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Add ControlTemplate to AnimatedTabControl without overwriting animation behavior

I am using MahApps AnimatedTabControl and I need to create a ControlTemplate to add a ScrollViewer for header tabs. Here is my template:
<TabControl.Template>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ScrollViewer x:Name="_MainTabControlScrollViewer" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Disabled">
<TabPanel x:Name="HeaderPanel" IsItemsHost="True" Margin="0,4,0,0"/>
</ScrollViewer>
<ContentPresenter x:Name="PART_SelectedContentHost" Margin="4" ContentSource="SelectedContent" Grid.Row="1"/>
</Grid>
</ControlTemplate>
</TabControl.Template>
However, this kills the animation. Is there a way to inherit the default AnimatedTabControl behavior?
Instead overriding the TabControl just use the MetroAnimatedSingleRowTabControl.
<Controls:MetroAnimatedSingleRowTabControl x:Name="AnimatedTabControl">
<TabItem Header="tab test"></TabItem>
</Controls:MetroAnimatedSingleRowTabControl>
with xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Hope that helps.

WPF - Hosting content inside a UserControl

I'm trying to create a user control that has a Grid with two rows.
the first row for a title and the second one for a content that will be defined outside the user control such as a Button in our example.
Somehow I didn't get it to work.
UserControl1 xaml:
<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Title" FontSize="30" Margin="10,0,0,0"/>
</Grid>
MainWindow xaml:
<Grid>
<local:UserControl1>
<Button>Click me</Button>
</local:UserControl1>
</Grid>
The picture below should explain what's my problem:
The following code
<local:UserControl1>
<Button>Click me</Button>
</local:UserControl1>
Means that you set UserControl1's Content property to be that button. This button simply replaces that UserControls1's markup. So all the things that you have in UserControl1.xaml are not there any more.
EDIT
If you want your UserControl to host some markup that will be set somewhere outside of it, you can add a DependencyProperty to it, for example:
/// <summary>
/// Gets or sets additional content for the UserControl
/// </summary>
public object AdditionalContent
{
get { return (object)GetValue(AdditionalContentProperty); }
set { SetValue(AdditionalContentProperty, value); }
}
public static readonly DependencyProperty AdditionalContentProperty =
DependencyProperty.Register("AdditionalContent", typeof(object), typeof(UserControl1),
new PropertyMetadata(null));
And add some element to it's markup to host that additional content. Here's an example extending the markup you provided:
<UserControl ... Name="userControl">
<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Title" FontSize="30" Margin="10,0,0,0"/>
<ContentPresenter Content="{Binding AdditionalContent, ElementName=userControl}" />
</Grid>
</UserControl>
Now you can use it as following:
<local:UserControl1>
<local:UserControl1.AdditionalContent>
<Button>Click me</Button>
</local:UserControl1.AdditionalContent>
</local:UserControl1>
You have to set the ControlTemplate:
<UserControl>
<UserControl.Resources>
<Style TargetType="{x:Type local:UserControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:UserControl1}">
<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Title" FontSize="30" Margin="10,0,0,0"/>
<ContentPresenter Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
</UserControl>
You can template the user control to add additional visuals like the TextBlock.
<UserControl>
<UserControl.Style>
<Style TargetType="{x:Type UserControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Title" FontSize="30" Margin="10,0,0,0"/>
<ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Style>
<Button>
Click me!
</Button>
</UserControl>
Use template with
< ContentControl />
Instead of using Content Presenter
So place this:
<UserControl.Style>
<Style TargetType="{x:Type UserControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type UserControl}" >
<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Title" FontSize="30" Margin="10,0,0,0"/>
<ContentControl Grid.Row="1" Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Style>
to your userControl
This is the simple general template for a user control (without using styles or properties to set the content):
<UserControl ...>
<UserControl.Template>
<ControlTemplate TargetType="{x:Type UserControl}">
<!-- control contents here -->
<ContentPresenter/><!-- outside contents go here -->
<!-- control contents here -->
</ControlTemplate>
</UserControl.Template>
</UserControl>
The <ControlTemplate> represents the user control's XAML duplicated for each control.
The <ContentPresenter> is where the Content gets put when consuming the control.

Resources