ContentControl in ControlTemplate does not show Border nor Background - wpf

I have a Button with a custom control template and a Grid as sibling. The Button should use the TriangleUpPath when the Grid is visible, otherwise it should use the TriangleDownPath. So far so good.
But I want that the Path has a Border around. So I thought I could just set the BorderBrush of the ContentControl, but this does not work. I don't see the Border. Same goes for the Background of the ContentControl.
Here is my minimal not working example:
<Button x:Name="_hideButton"
Grid.Row="0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Click="ToggleResultsVisibility">
<Button.Style>
<Style TargetType="Button" >
<Style.Resources>
<Path x:Key="TriangleUpPath"
Data="M 0,5 L 7,0 L 14,5"
Margin="3,3,0,0"
Stroke="Black"
StrokeThickness="1"/>
<Path x:Key="TriangleDownPath"
Data="M 0,0 L 7,5 L 14,0"
Margin="3,3,0,0"
Stroke="Black"
StrokeThickness="1"/>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ContentControl Margin="0,5,5,0"
Width="20"
Height="11"
BorderBrush="Magenta"
BorderThickness="1"
Background="Cyan">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Background"
Value="HotPink"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Visibility, ElementName=_resultsGrid}"
Value="Collapsed">
<Setter Property="Content"
Value="{StaticResource TriangleDownPath}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Visibility, ElementName=_resultsGrid}"
Value="Visible">
<Setter Property="Content"
Value="{StaticResource TriangleUpPath}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
<Grid x:Name="_resultsGrid"/>
As you can see, I even tried to set the Background in the Style (to HotPink) to no avail.
It helps, when I have a Border around the ContentControl.
But the question is: why do the Background-Property and the BorderBrush-Property of the ContentControl have no influence?

The default style for ContentControl does not contain a way of rendering the Border and Background properties, it just has a ContentPresenter.
You need to add a custom template for the content control yourself, containing the additional elements you need:
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
<ContentPresenter
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Cursor="{TemplateBinding Cursor}"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</ContentControl.Template>

It's because BorderThickness/Brush aren't some magic properties that make the border magically appear out of nowhere.
For making borders, you should be using a <Border> element somewhere.
By default, the Button has some Border inside. That's why setting BorderBrush/BorderThickness on a Button does something. Specifically, the default control template of a Button contains a Border as one of its topmost elements, and that Border's definition looks similar to:
// the DEFAULT Button's control template
<ControlTemplate>
...
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
.... >
...here goes other things like textblock/content..
</Border>
...
</ControlTemplate>
so, as you see, there's a border and it has its settings bound to the owning control, so bound to the Button. The TemplateBinding binds BBrush of Border to BBrush of the Button that owns it.
Now, as you have completely rewritten/overridden the Button's control template, it now does not contain any Border, so there is nothing that would draw the border.
Try:
<Border BorderBrush=... BorderThickness=...>
<ContentControl ....
and it should be like you wanted.
Afterthought: If I understood you wrong and if you meant your setting the BorderBrush/Thickness on the ContentControl inside the buttons' template that it didn't add border around your paths, then there's a surprise.
The properties are available on the CC only because they are commonly used, they came from base Control class. But the ContentControl does not use them at all. They are ignored. ContentControl does not draw any borders, texts, etc. ContentControl shows its content, and the content is just a Path which draws itself only. So, either add a Border around the ContentControl as above, or change your resources and wrap each Path into a Border and use that as a content resource.
<Style.Resources>
<Border x:Key="TriangleUpPath"
BorderBrush=... BorderThickness=...
<Path Data="M 0,5 L 7,0 L 14,5"
.... />
</Border>
<Border x:Key="TriangleDownPath"
BorderBrush=... BorderThickness=...
<Path Data="M 0,0 L 7,5 L 14,0"
.... />
</Border>
</Style.Resources>

Related

Round the corners of a button in Window.Resources WPF

I am very new to WPF. I've familiarized myself with ControlTemplate, ContentPresenter, and some other pieces, but I am struggling to create a button with rounded corners as defined from Window.Resources (or potentially a separate style file).
Anyways, when I add this to a <Button/> tag, I get a button with rounded corners:
<Button.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="5"/>
</Style>
</Button.Resources>
However, when I try to include it up in the Window.Resources the button will not apply the border style:
<ControlTemplate x:Key="roundbutton" TargetType="Button">
<Grid>
<Rectangle Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}"/>
<Border CornerRadius="5"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
This doesn't look right to me, but I guess I don't know where to add the specification of CornerRadius such that it will apply to the button. I did see this post, How to create/make rounded corner buttons in WPF? but it's a little over my head. I just want to round all the buttons in my application!
The Border in your ControlTemplate is not visible, because you have neither set its Background, nor its BorderBrush.
You could have something like shown below. Note however that with a simple ControlTemplate like this a Button loses all the visualizations of its states, like focused, mouse-over, pressed etc.
<ControlTemplate x:Key="roundbutton" TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="5">
<ContentPresenter/>
</Border>
</ControlTemplate>
You may instead just declare a default Button Style (i.e. a Style resource without x:Key) that applies your original Border Style like this:
<Style TargetType="Button">
<Style.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="5"/>
</Style>
</Style.Resources>
</Style>

How to get the content width of the TextBox except for width of the VerticalScrollBar?

I created HighlightTextBox that derived TextBox. And the code is as shown below.
<Style TargetType="{x:Type host:HighlightTextBox}">
<Setter Property="AcceptsReturn" Value="True" />
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="TextWrapping" Value="NoWrap"/>
<Setter Property="Foreground" Value="#00000000"/>
<Setter Property="FontSize" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=FontSize, Mode=TwoWay}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate x:Name="textArea" TargetType="{x:Type host:HighlightTextBox}">
<Border BorderThickness="{Binding BorderTickness}"
BorderBrush="{Binding BorderBrush}"
Background="{Binding BackGround}">
<Grid Margin="{TemplateBinding Padding}" x:Name="PART_Grid">
<host:TextCanvas x:Name="PART_RenderCanvas" ClipToBounds="True"
TextOptions.TextRenderingMode="ClearType" TextOptions.TextFormattingMode="Display"
LineHeight="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=LineHeight}"/>
<ScrollViewer x:Name="PART_ContentHost" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The important point is ControlTemplate.
As you can see, the content of the HighlightTextBox consists of TextCanvas and ScrollViewer.
The HighlightTextBox highlights the currently selected line by painting on the TextCanvas but it invasion the VerticalScrollViewer section as below.
I want to display it by not invasion the VerticalScrollViewer.
I think that maybe the cause is TextCanvas occupy entire section of TextBox.
So I tried to move TextCanvas into the ScrollViewer but this way make facing the run-time error that "PART_ContentHost can't have child element.
I think that another way is to get the content width of the TextBox except for the width of the VerticalScrollBar and binding it to the width of the TextCanvas. But I don't know how to get it.
What I should do to solve this problem?
If you have a better way or another way to solve then please let me know.
Thank you for reading.
Search the visual tree for your ScrollViewer's content presenter, that will give you the width of the content area itself:
var scrollViewer = yourTextBox.Template.FindName("PART_ContentHost", yourTextBox) as ScrollViewer;
var contentPresenter = UIHelper.FindChild<ScrollContentPresenter>(scrollViewer, String.Empty);
var width = contentPresenter.ActualWidth;
UPDATE: You can bind to the ScrollContentPresenter's content control directly like this:
<Grid Margin="{TemplateBinding Padding}" x:Name="PART_Grid">
<TextBlock Text="{Binding ElementName=PART_ContentHost, Path=Content.ActualWidth}" Background="CornflowerBlue" VerticalAlignment="Top"/>
<ScrollViewer x:Name="PART_ContentHost" />
</Grid>
Keep in mind though that this gives you the area of the ScrollViewers content width which, in the example I've given above, will a bit smaller than the TextBlock's width due to the fact that the ScrollViewers content is a TextBoxView with a 2,0,2,0 margin:
To compensate for this you'll probably want to bind your Canvas margin to the TextBoxView margin (which in my case is a TextBlock rather than a Canvas):
<Grid Margin="{TemplateBinding Padding}" x:Name="PART_Grid">
<TextBlock Text="{Binding ElementName=PART_ContentHost, Path=Content.ActualWidth}"
Margin="{Binding ElementName=PART_ContentHost, Path=Content.Margin}"
Background="CornflowerBlue" VerticalAlignment="Top" />
<ScrollViewer x:Name="PART_ContentHost" Padding="0" Margin="0"/>
</Grid>
This will keep your Canvas's alignment in the parent Grid the same as for the TextBoxView so that everything lines up properly:
(You could also just remove the TextBoxView's margin, but that's probably not what you want).

Foreground does not apply for labels

I have some labels like this:
<Label x:Name="ledCalculate" Width="Auto" Content="Calculate" Height="25" FontFamily="Cambria" FontSize="18.667" VerticalContentAlignment="Bottom" Padding="10,0,0,0" BorderThickness="0,0,0,1" Style="{StaticResource RightPanelLabels}" Grid.Row="11"/>
I defined a style for it:
<Style TargetType="Label" x:Key="RightPanelLabels" BasedOn="{StaticResource
{x:Type Label } }">
<Setter Property="Foreground" Value="#FFEABEBE"></Setter>
<Setter Property="BorderBrush" Value="#FF818080"></Setter>
<Setter Property="BorderThickness" Value="0,0,0,1"></Setter>
</Style>
Border thickness setter in style applies for control and it has upper precedence in style, and visual studio ignores its values in local, but foreground setter in style does not applies, when i set it locally it applies,
Why border thickness in style have upper precedence but foreground in local have upper precedence????????
I know there is a default template for labels like bellow that have a trigger for IsEnabled , that template is something like this:
<ControlTemplate TargetType="{x:Type Label}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Can anyone help me?
Sorry for my English.
All the local styles take precedence over defined styles, so inline styles will always override other styles defined in resources.
When I run the code I found that Style's BorderThickness also get overridden by the local inline style value for BorderThickness as well.
I too would love to hear if there's a workaround for this.
I found this article as well, hope it helps Same problem with a style trigger though
EDIT:
Upon trying it few different ways I found using this method we can override the label's inline Foreground style, but I don't know whether it's the healthiest way
<Style TargetType="{x:Type Label}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}"
Foreground="Red" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Label FontSize="50" Foreground="Black"> I am Black inline, Red on Style </Label>
This is me just editing the ContentTemplate of the Label .
Basically, this possible for Label because Label derives from ContentControl whereas TextBlock lives in the System.Windows.Controls namespace, it is not a control. It derives directly from FrameworkElement.
TextBlock are used in many other controls to show texts, including in Label.
Refer this article to know more about this
you have to change property name in style setter
<Setter Property="TextBlock.Foreground" Value="#FFEABEBE">

Stop button color change on Mouseover

ok..this is very strange....i have defined a button, actually 5 buttons and each have a different color but on mouse over, they just change the color to that icy blue color....i tried to override that by using the below code :
<Button Name="btn1" Content="Button" Width="65" Height="45" Background="Green" Margin="1,1,0,1" FontWeight="Bold">
<Button.Style>
<Style>
<Style.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Background" Value="Yellow" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.LayoutTransform>
<RotateTransform Angle="270"/>
</Button.LayoutTransform>
</Button>
but it still does not work...what i want is that it should maintain the background color it has been given(different for each button), so the question is : do i have to define it again and again for each button (the trigger did not work, color becomes ice blue) OR can i define it in the resource file with a generic value that it either stops the color change or just sets background to existing property
EDIT : Just to be clear, i want the button to stop changing its color on mouseover and just retain whatever color i have assigned it.....
Based on the comments and the SO post I linked: that article applied the style to anything of type Button, that's why it applies to all buttons (which is not necessarily a bad thing). But the salient point to take away from that article is that what you need to modify is the ControlTemplate.
So you would want to do something like this:
<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Chrome" BorderBrush="Black" BorderThickness="2" CornerRadius="2" Background="{TemplateBinding Property=Background}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
This isn't perfect, because it loses all of it's interactivity (ie. you can't tell you've pushed it, hovered over it or anything). But it gives you a good starting point.
One of the key points here is that the Background property of the Border is bound to the TemplateParent Property=Background, that means it's going to read the background property of the button the that it is templating and use it's background color. This makes it easier for you to use a single style to cover all 5 of your buttons. You could (and may want to) do similar things with the BorderBrush property.
Also notice my style has a x:Key value, so in order to use this, you would do:
<Button x:Name="btn1" Content="Button" Width="65" Height="45" Background="Green" Margin="1,1,0,1" FontWeight="Bold" Style="{DynamicResource ButtonStyle1}">
<Button.LayoutTransform>
<RotateTransform Angle="270"/>
</Button.LayoutTransform>
</Button>
You can remove the x:Key attribute and the style will indeed apply to all buttons inside of whichever container you declare the resource.

Canvas with Children as a Style

Sorry if this is a dense question, but I'm looking to see if there is a way to have a style resource of Canvas in App.xaml and also have children on that Canvas and just refer to it in the Style of another canvas. The resource I envision would be something like:
<Style x:Key="Background1" TargetType="Canvas">
<Setter Property="Width" Value="500"/>
<Setter Property="Height" Value="600" />
<Setter Property="Background" Value="Red"/>
<Setter Property="Children">
<Setter.Value>
<Rectangle Canvas.Top="20" Canvas.Left="20" Width="100" Height="100" Fill="Yellow"></Rectangle>
</Setter.Value>
</Setter>
</Style>
And then calling it would be as simple as:
<Canvas Style="{StaticResource Background1}" x:Name="CanvasRoot" >
<Rectangle x:Name="PageRectangle" Canvas.Left="114" Canvas.Top="84" Height="378" Width="210" Stroke="#92D050" Fill="#C0504D" />
</Canvas>
Any thoughts on if something like this can be done. What I've done doesn't work because there is no "Children" property on Canvas.
So you want to add a child to a Canvas via a Style? I'm afraid this is simply not possible. Styles allows you to set the values of an elements dependency properties, such as Height, Background, Stroke etc ... You can also use them to set attached properties. However, the Children property you are trying to set is not a dependency property, it is the collection of children elements that describe the children of your canvas in the visual tree.
The only way to add new elements t the visual tree using styles is to add them to some controls template. Unfortunately you canot template panels (Canvas, Grid, StackPanel). You could use a ContentControl and add your rectangle as part of its template.
A ContentControl is a lookless container of a single child. See the template described here:
http://msdn.microsoft.com/en-us/library/dd334411%28VS.95%29.aspx
Here it is, templates to add a rectangle. I am not sure what layout you are trying to achieve, but it should give you the general idea
<Style TargetType="ContentControl" x:Key="myContentControl">
<Setter Property="Foreground" Value="#FF000000"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Canvas>
<Rectangle Canvas.Top="20" Canvas.Left="20" Width="100" Height="100" Fill="Yellow"></Rectangle>
<ContentPresenter
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Cursor="{TemplateBinding Cursor}"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
<ContentControl Style={StaticResource myContentControl}>
<Canvas x:Name="CanvasRoot" >
<Rectangle x:Name="PageRectangle" Canvas.Left="114" Canvas.Top="84" Height="378" Width="210" Stroke="#92D050" Fill="#C0504D" />
</Canvas>
</ContentControl>

Resources