WPF Textbox Template assistance - wpf

I am working on setting up a template for a textbox - and I am running into an issue with setting up the style for the box and am hoping for some assistance. Here is what I have so far:
---
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border x:Name="Border">
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border">
<Setter.Value>
<Border BorderThickness="1" BorderBrush="Black" CornerRadius="0">
<Border.Background>
<RadialGradientBrush GradientOrigin="0.225,-0.616" RadiusY="0.863" RadiusX="0.757">
<GradientStop Color="#EEFFFFFF"/>
<GradientStop Color="#00FFFFFF" Offset="0.696"/>
</RadialGradientBrush>
</Border.Background>
</Border>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="False">
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
---
When I try to compile this, I get a message stating:
Severity Code Description Project File Line Suppression State
Error MC3000 'The 'Setter' start tag on line 22 position 34 does not match the end tag of 'Trigger'. Line 35, position 31.' XML is not valid.
Anyone have any idea on what I missed here or am doing wrong? I think that I have closed all of the lines correctly.

The setter isn't working quite how you think it is. You're currently creating a border in your template and giving it an x:Name="Border", but then trying to overwrite it in the trigger, with the assumption that it will replace the existing border altogether:
<Setter TargetName="Border">
<Setter.Value>
<Border BorderThickness="1" BorderBrush="Black" CornerRadius="0">
That's not how setters work. Setters need three things:
A reference to an object that you want to modify (although in some cases this is implied).
The name of a property of that object that you wish to modify.
The new value for that property.
You're only providing 1 and 3, hence the error. Instead of trying to replace the border you have to instead modify the properties of the existing border:
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="BorderThickness" Value="1" />
<Setter TargetName="Border" Property="BorderBrush" Value="Black" />
<Setter TargetName="Border" Property="CornerRadius" Value="0" />
<Setter TargetName="Border" Property="Background">
<Setter.Value>
<RadialGradientBrush GradientOrigin="0.225,-0.616" RadiusY="0.863" RadiusX="0.757">
<GradientStop Color="#EEFFFFFF"/>
<GradientStop Color="#00FFFFFF" Offset="0.696"/>
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
In practice you'd typically wrap all these up in their own style and apply that instead to keep the code clean, but that's an implementation detail.
If you really do want to replace the actual border object then there are ways to do it (e.g. by changing the template itself), but in most cases the above should suffice.

Related

How to build a button style based on another in WPF

I'm new to WPF and by following a quick tutorial I succeeded on getting a personalized button style as the following one:
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Margin="2" StrokeThickness="1" Stroke="#60000000" StrokeDashArray="1 2"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="NormalBrush" Color="#FF0C0C13"/>
<SolidColorBrush x:Key="LightBrush" Color="#FF2E2E3E"/>
<SolidColorBrush x:Key="PressedBrush" Color="#FF209FD4"/>
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#FF494968" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#FF2E2E3E" />
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<SolidColorBrush x:Key="HorizontalNormalBrush" Color="AntiqueWhite"/>
<SolidColorBrush x:Key="HorizontalLightBrush" Color="AntiqueWhite"/>
<SolidColorBrush x:Key="DarkBrush" Color="AntiqueWhite"/>
<SolidColorBrush x:Key="NormalBorderBrush" Color="Aqua"/>
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Border" BorderThickness="1" Background="{StaticResource NormalBrush}" BorderBrush="#FF2E2E3E">
<ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource LightBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It seems indeed that we need to override the ControlTemplate and that to keep some functionalities (like the behaviour while hovering the mouse) we need to define them. What is already strange to me here, is that under ControlTemplate it is defined a Border tag and then the trigger refer to that Border. So as a first question, why Border and not something else?
But now the main question is: suppose I want exactly the same button, with the same colors and functionalities but without the borders. I tried to use the BasedOn similar to the following:
<Style x:Key="MyButtonStyleNoBorder" TargetType="{x:Type Button}" BasedOn="{StaticResource MyButtonStyle}">
<Setter Property="BorderThickness" Value="0"/>
</Style>
but no way. The only solution I found is to copy the entire code of MyButtonStyle and then to change only one character (!) to have BorderThickness="0". But this to me looks stupid. Can you please help?
So as a first question, why Border and not something else?
The element named Border is the outermost element and its child will inherit most of its state, in this case the background.
But now the main question is: suppose I want exactly the same button, with the same colors and functionalities but without the borders.
If you like a button without borders you can just set the BorderThickness property of the button to 0. directly or as a setter in the style.
The only solution I found is to copy the entire code of MyButtonStyle and then to change only one character (!) to have BorderThickness="0". But this to me looks stupid. Can you please help?
Styles are kind of a list for what property to change on a target. The basedOn functionality will use the basedOn-style and "add" the new setter from the new style.
A Template is more like a drawing (and some graphical behavior) of the control and when you specify a new one you just throw away the old one. I would be possible to do a basedOn there. How would we determine what to use and what to replace?
Not the answer you wished for but hopefully you got it anyway.
<Border x:Name="Border" BorderThickness="{TemplateBinding BorderThickness}" Background="{StaticResource NormalBrush}" BorderBrush="#FF2E2E3E">
<ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
</Border>
Will pick up the value the Button object gets on BorderThickness.
I would have done this using MVVM (if you have not considering MVVM yet).
In my ViewModel bound to the Button, I would add a property "IsBorderLess".
Then, on the Triggers:
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsBorderLess}" Value="True">
<Setter TargetName="Border" Property="BorderThickness" Value="0"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsBorderLess}" Value="False">
<Setter TargetName="Border" Property="BorderThickness" Value="10"/>
</DataTrigger>
..........

Trigger won't change Background

I'm trying to create a template for buttons in order to prevent the graying of the control when it is disabled, it is working just fine, but for some reason it won't change Backgrounds once I set a colour for it the Button Properties.
Here is the button I made:
<Style TargetType="Button" x:Key="TestButton">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="MinHeight" Value="29px" />
<Setter Property="MinWidth" Value="103px" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Background" Value="#EEEEEE"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border TextBlock.Foreground="{TemplateBinding Foreground}" x:Name="Border">
<Border.Background>
<SolidColorBrush Color="{TemplateBinding Background}" />
</Border.Background>
<Border.BorderBrush>
<SolidColorBrush Color="Black" />
</Border.BorderBrush>
<Border.BorderThickness>
<Thickness Top="0.75" Bottom="0.75" Right="0.75" Left="0.75"/>
</Border.BorderThickness>
<Border.CornerRadius>
<CornerRadius TopLeft="3" TopRight="3" BottomLeft="3" BottomRight="3"/>
</Border.CornerRadius>
<ContentPresenter Margin="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True" />
</Border>
<!--Triggers-->
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#E1F3FD" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#C4E9FF" />
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="Black" Direction="500" ShadowDepth="1" BlurRadius="5" Opacity="0.5" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Background" Value="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Background}"/>
<Setter Property="Opacity" Value="0.5" />
</Trigger>
<Trigger Property="IsEnabled" Value="True" >
<Setter TargetName="Border" Property="Background" Value="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Background}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When I call it gives me two diferent results, the one that works is the following:
<Button Style="{DynamicResource ResourceKey=TestButton}" Content="Button" Height="66" Name="button1" Width="149" Click="button1_Click" />
But when I add a Background to it, it stops changing Backgrounds for some reason:
<Button Style="{DynamicResource ResourceKey=TestButton}" Background="Orange" Content="Button" Height="66" Name="button1" Width="149" Click="button1_Click" />
It wont change the color because you "overlay" the color with the color you defined in MainWindow.xaml.
Its called dependency property value precedence.
To read more about it take a look at this:
http://msdn.microsoft.com/en-us/library/ms743230.aspx
What happens in your case is that you defined a background in style and in trigger but then you set the value in MainWindow. The value set in MainWindow is considered the local value and the ones set in style are "style - values".
If you take a look at the link I gave you you will see that local value comes first before "style - values".
Local values are very powerful.
That is why your Button will always be Orange even when disabled. The property system will try to find a value for the background when your trigger fires and it will always find the local one because that is the value with the highest prority.
In order to make this work properly you will have to use SetCurrentValue instead of SetValue when setting the value of Button's background. (Means you will have to override Buttons code)
Take a look at this:
http://msdn.microsoft.com/en-us/library/system.windows.dependencyobject.setcurrentvalue.aspx
This method is used by a component that programmatically sets the value of one of its own properties without disabling an application's declared use of the property. The SetCurrentValue method changes the effective value of the property, but existing triggers, data bindings, and styles will continue to work.
Edit:
Example to understand dependency property value precedence and hit testing.
<Window.Resources>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Yellow" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<TextBlock Height="20" Margin="40" Text="asdfasdfa" VerticalAlignment="Bottom"/>
<Rectangle Height="100" VerticalAlignment="Bottom" >
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
If you look at DependecyProperty value precedence order you will see that setting a value explicitly (treated as local value) on a control will override any values set by the trigger.
Just a note: the Background property is of type Brush, so you should write
<Border Background="{TemplateBinding Background}" ...>
...
</Border>
instead of
<Border ...>
<Border.Background>
<SolidColorBrush Color="{TemplateBinding Background}" />
</Border.Background>
...
</Border>

How are WPF Buttons and TextBlock styles related?

I have a custom style for my 'default' Buttons, and also created a custom style for TextBlocks. If I remove the TextBlock style entirely, everything works fine, but once the TextBlock styling is added in for some reason the Button style is used on the Button's text 'default' state. It seems like some kind of inheritance is going on here but I can't see where in the msdn docs. What's going on?
I'm using Expression Blend 4-- and also another odd thing is that the preview in Blend looks fine, but when I RUN the application, the button styles are incorrect in their default state. Here's the styles which seem to be conflicting:
<ResourceDictionary>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Foreground">
<Setter.Value>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="#FFFDFF00" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
<Setter Property="RenderTransform">
<Setter.Value>
<TransformGroup>
<ScaleTransform ScaleY="1.20" ScaleX="1.20"/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<ContentPresenter RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<ContentPresenter.Effect>
<DropShadowEffect BlurRadius="3" ShadowDepth="4"/>
</ContentPresenter.Effect>
</ContentPresenter>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True"/>
<Trigger Property="IsDefaulted" Value="True"/>
<Trigger Property="IsMouseOver" Value="True"/>
<Trigger Property="IsPressed" Value="True"/>
<Trigger Property="IsEnabled" Value="False"/>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="FontFamily" Value="/Rtk;component/Fonts/#Segoe Print"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Background" Value="{x:Null}"/>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextWrapping" Value="NoWrap"/>
<Setter Property="TextTrimming" Value="None"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="3" ShadowDepth="4"/>
</Setter.Value>
</Setter>
<Setter Property="FontFamily" Value="/Rtk;component/Fonts/#Segoe Print"/>
</Style>
</ResourceDictionary>
This is how I am using the Button control itself:
<Button Content="Button Text" FontSize="24"/>
(note that this fontsize is different from the size I specified in the default style, 18 - I want to override it in this button's case)
Edit:
The actual button entry looks like this in MainWindow.xaml, there's no other customizations other than the style changes I posed from App.xaml:
<Button Content="Button" HorizontalAlignment="Left" Margin="336,0,0,274.226" VerticalAlignment="Bottom" Width="75"/>
To illustrate what I'm seeing:
Just a fast wild guess, but when the content of a button is a string, isn't it default a textblock?
As people have suggested, your Button contains a Textblock created to hold the content, it is picking up the style from app.xaml, you can work around this in a few ways, here are a couple:
Put an explicit textblock into your button, and apply no style:
<Button Margin="272,192,277,0" VerticalAlignment="Top">
<TextBlock Text="Button" Style="{x:Null}"/>
</Button>
Put a textblock into your button style, also with a null style:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<TextBlock Text="{TemplateBinding Content}" Style="{x:Null}">
<TextBlock.Effect>
<DropShadowEffect BlurRadius="3" ShadowDepth="4"/>
</TextBlock.Effect>
</TextBlock>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True"/>
<Trigger Property="IsDefaulted" Value="True"/>
<Trigger Property="IsMouseOver" Value="True"/>
<Trigger Property="IsPressed" Value="True"/>
<Trigger Property="IsEnabled" Value="False"/>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
Hopefully one of those 2 will work for you.
Looking only ay the code you posted, I can't see how the TextBlock Style would in any way influence the appearance of the Buttons - unless the Content of the Buttons consists (directly or indirectly) of TextBlocks. Can you post a more complete code sample, possibly including the Button's XAML?

Whats wrong with this trigger?

I'm learning about WPF templates and I'm creating one for a button. I'm using a trigger to change the Fill property of the Ellipse on 'IsMouseOver'. When I set the trigger to the 'Fill' property directly, it works. But when I try to reference a specific SolidColorBrush, I get a compile error.
This works:
<ControlTemplate x:Key="GhostButtonTemplate" TargetType="Button">
<Grid>
<Ellipse Name="Border" Stroke="DarkGray" Fill="Gray">
</Ellipse>
<ContentPresenter ... />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Fill" Value="Black"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
This causes error:
<ControlTemplate x:Key="GhostButtonTemplate" TargetType="Button">
<Grid>
<Ellipse Name="Border" Stroke="DarkGray">
<Ellipse.Fill>
<SolidColorBrush Color="Gray" x:Name="FillBrush"/>
</Ellipse.Fill>
</Ellipse>
<ContentPresenter ... />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="FillBrush" Property="Color" Value="Black"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Error is:
Cannot find the Trigger target
'FillBrush'. (The target must appear
before any Setters,Triggers, or
Conditions that use it.)
Can anyone explain why the second case doesn't work? Thanks.
Rather than naming the brush you use the Ellipse
Edit, yeah you know this :P
<ControlTemplate x:Key="GhostButtonTemplate" TargetType="Button">
<Grid>
<Ellipse Name="Border" Stroke="DarkGray">
<Ellipse.Fill>
<SolidColorBrush Color="Gray" x:Name="FillBrush"/>
</Ellipse.Fill>
</Ellipse>
<ContentPresenter ... />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Fill" Value="Black"/>
</Trigger>
</ControlTemplate.Triggers>

Using WPF, what is the best method to update the background for a custom button control?

In WPF, we are creating custom controls that inherit from button with completely drawn-from-scratch xaml graphics. We have a border around the entire button xaml and we'd like to use that as the location for updating the background when MouseOver=True in a trigger. What we need to know is how do we update the background of the border in this button with a gradient when the mouse hovers over it?
In your ControlTemplate, give the Border a Name and you can then reference that part of its visual tree in the triggers. Here's a very brief example of restyling a normal Button:
<Style
TargetType="{x:Type Button}">
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="customBorder"
CornerRadius="5"
BorderThickness="1"
BorderBrush="Black"
Background="{StaticResource normalButtonBG}">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger
Property="IsMouseOver"
Value="True">
<Setter
TargetName="customBorder"
Property="Background"
Value="{StaticResource hoverButtonBG}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If that doesn't help, we'll need to know more, probably seeing your own XAML. Your description doesn't make it very clear to me what your actual visual tree is.
You would want to add a trigger like this...
Make a style like this:
<Style x:Key="ButtonTemplate"
TargetType="{x:Type Button}">
<Setter Property="Foreground"
Value="{StaticResource ButtonForeground}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="{x:Type Button}">
<Grid
SnapsToDevicePixels="True"
Margin="0,0,0,0">
<Border Height="20"
x:Name="ButtonBorder"
BorderBrush="{DynamicResource BlackBorderBrush}">
<TextBlock x:Name="button"
TextWrapping="Wrap"
Text="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}"
SnapsToDevicePixels="True"
Foreground="#FFFFFFFF"
Margin="6,0,0,0"
VerticalAlignment="Center"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<!-- Disabled -->
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="ButtonBorder"
Property="Background"
Value="{DynamicResource ButtonBackgroundMouseOver}" />
<Setter TargetName="ButtonBorder"
Property="BorderBrush"
Value="{DynamicResource ButtonBorderMouseOver}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then add some resources for the gradients, like this:
<LinearGradientBrush x:Key="ButtonBackgroundMouseOver"
EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop Color="#FF000000"
Offset="0.432"/>
<GradientStop Color="#FF808080"
Offset="0.9"/>
<GradientStop Color="#FF848484"
Offset="0.044"/>
<GradientStop Color="#FF787878"
Offset="0.308"/>
<GradientStop Color="#FF212121"
Offset="0.676"/>
</LinearGradientBrush>
Please let me know if you need more help with this.

Resources