WPF XAML TextBox not editable after applying Control Template - wpf

I have a TextBox in WPF and I'm trying to make the border color change when the mouse hovers over the TextBox. Based on my experience with other elements in WPF, I need to insert a ControlTemplate value with TemplateBinding to the values I am trying to dynamically change. However, when I apply this, the box becomes uneditable (and the text disappears). If I remove the Template setter, the box becomes editable again, but the custom BorderBrush triggers do not work.
Here is the Style:
<Style x:Key="TextBoxBase" TargetType="TextBox">
<Setter Property="FontSize" Value="30"/>
<Setter Property="Background" Value="{StaticResource BrushLightGrey}"/>
<Setter Property="Foreground" Value="{StaticResource BrushNormalText}"/>
<Setter Property="IsReadOnly" Value="False"/>
<Setter Property="Height" Value="40"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border BorderBrush="{TemplateBinding BorderBrush}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource BrushBlue}"/>
</Trigger>
</Style.Triggers>
<Style.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="5"/>
</Style>
</Style.Resources>
</Style>
Any suggestions or help is appreciated. Thanks.

You missed out the critical part:
<ScrollViewer Margin="0"
x:Name="PART_ContentHost" />
This is what hosts the text.
See
https://learn.microsoft.com/en-us/dotnet/framework/wpf/controls/textbox-styles-and-templates
TextBox Parts
The following table lists the named parts for the TextBox control.
TEXTBOX PARTS
Part Type Description
PART_ContentHost FrameworkElement A visual element that can contain a FrameworkElement. The text of the TextBox is displayed in this element.

Related

Weird WPF Round TextBox

I am new to WPF and I am trying to do a rounded corner textbox. There are a lot of examples I gather from here. However I cannot seems to make it work. Below are the two ways I tried and the results I obtained.
First Way:
<SolidColorBrush x:Key="SubTransparentTextBoxBG" Color="#ffffff" Opacity="0.12"/>
<Style TargetType="TextBox">
<Style.Setters>
<Setter Property="Background" Value="{StaticResource SubTransparentTextBoxBG}" />
<Setter Property="FontSize" Value="24px" />
<Setter Property="FontFamily" Value="Segoe UI Semibold"/>
<Setter Property="Foreground" Value="White" />
<Setter Property="Padding" Value="10" />
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="2"/>
</Style.Setters>
<Style.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="10"/>
</Style>
</Style.Resources>
</Style>
Result:
Apparently all my Setters took effect but not the Corner Radius
Second Way:
<SolidColorBrush x:Key="SubTransparentTextBoxBG" Color="#ffffff" Opacity="0.12"/>
<Style TargetType="TextBox">
<Style.Setters>
<Setter Property="Background" Value="{StaticResource SubTransparentTextBoxBG}" />
<Setter Property="FontSize" Value="24px" />
<Setter Property="FontFamily" Value="Segoe UI Semibold"/>
<Setter Property="Foreground" Value="White" />
<Setter Property="Padding" Value="10" />
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border x:Name="border" CornerRadius="10" BorderBrush="#000" BorderThickness="2" Background="#fff"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
Result:
This time, only the Round Border takes place and the rest of the Setters' property are overwritten.
Can Someone Please Help to point out what are the mistakes in these two ways?
The best way to give the TextBox rounded corners is to overwrite the template.
The following is your Style, but fixed. It now contains the mandatory parts with their mandatory naming: a content host named PART_ContentHost. In order to make the style setters work, you need to bind the template's controls (in this case the Border properties) to the appropriate properties of the templated parent (the TextBox) using TemplateBinding.
<Style TargetType="TextBox">
<Style.Setters>
<Setter Property="Background"
Value="{StaticResource SubTransparentTextBoxBG}" />
<Setter Property="FontSize"
Value="24px" />
<Setter Property="FontFamily"
Value="Segoe UI Semibold" />
<Setter Property="Foreground"
Value="White" />
<Setter Property="Padding"
Value="10" />
<Setter Property="BorderBrush"
Value="Red" />
<Setter Property="BorderThickness"
Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border CornerRadius="10"
Margin="{TemplateBinding Margin}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<!-- The required part with the required name -->
<ScrollViewer x:Name="PART_ContentHost"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
Wrapping the TextBox in a Border with rounded corners will still leave the TexBox with sharp corners that will overlap the rounded corners of the surrounding Border.
Some controls have mandatory template elements (parts) which must be part of the ContorlTemplate and have a special name. When those parts are missing or the name doesn't match the required name, then the functionality of the templated control might get broken. To know the parts and their names of TextBox visit TextBox Parts. To know the named parts of all WPF controls visit Control Styles and Templates. This links also contains an example of the actual Style and Template.
An alternative approach to get the required template parts is to select the control you wish to template and then open the XAML designer view. Right click on the selected control and select Edit Template. In the popup select Edit a Copy. A dialog opens. Here you can give the extracted template a name and set the location where the extracted template will be moved to. Now you can edit this template which already has all the required parts.

ControlTemplate.Trigger not working on CellValuePresenter (TargetType)

I 'm using Infragistics 2014.2. I have a XamDataGrid which display the data with dynamic columns, the grid is formatted by its default style and in addition, some of the columns having alignment set to Right (decimal type columns) with some value masking on it and the rest are set alignment to Left.
I want to remove the default CellValuePresenter style of XamDataGrid and write a new style with only TextBlock/ContentPresenter on it instead of CellValuePresenter for each cell and after applying new style, the alignment and masking of the cells should remain same. The reason behind applying this new style is i want the copy of the same grid to display it on another window with no data manipulation in it.
Can you please look at the following code that i have tried to achieve the same(even with many of the changes in it), but it doesn't seems to work anyhow.
<Style x:Key="PreviewCellValuePresenterStyle" TargetType="{x:Type igDP:CellValuePresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate x:Name="CellValuePresenterControlTemplate" TargetType="{x:Type igDP:CellValuePresenter}">
<ContentPresenter x:Name="ContentValuePresenter"
VerticalAlignment="Center"
ContentSource="Value"
Margin="4,3,4,4"/>
<ControlTemplate.Triggers>
<Trigger Property="HorizontalAlignment" Value="Right">
<Setter TargetName="ContentValuePresenter" Property="TextBlock.TextAlignment" Value="Right"></Setter>
</Trigger>
<Trigger Property="HorizontalContentAlignment" Value="Right">
<Setter TargetName="ContentValuePresenter" Property="TextBlock.TextAlignment" Value="Right"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Right now, i'm only setting the alignment property for testing. In the above code, have tried TextBlock in place of ContentPresenter but the trigger never gets invoked. I don't where i'm doing wrong.
Can you anyone help me to get out of this?
Thanks
If your intention of using text block is only for alignment/appearance, then you can just use setters to set these values.
<Style x:Key="PreviewCellValuePresenterStyle" TargetType="{x:Type igDP:CellValuePresenter}" >
<Setter Property="FontWeight" Value="DemiBold"/>
<Setter Property="Height" Value="24"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
</Style>
But, If you need some other implementations like value masking, or need text block to be there,
<Style x:Key="PreviewCellValuePresenterStyle" TargetType="{x:Type igDP:CellValuePresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type igDP:CellValuePresenter}">
<TextBlock HorizontalAlignment="Center" Style="{DynamicResource TextStyle}">
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TextStyle" TargetType="{x:Type TextBlock}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>

why this style is not working in wpf

I have a style for button as follow:
<Style TargetType="Button" x:Key="BlackButton">
<Setter Property="Background" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="red" />
</Trigger>
</ControlTemplate.Triggers>
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and a button on which is defined as follow:
<Button Canvas.Left="19" Canvas.Top="520" Height="34" Width="107"
Style="{StaticResource BlackButton}" />
But when I run application, I can not see the button. Its background set to none.
If I change the style as follow:
<Style TargetType="Button" x:Key="BlackButton">
<Setter Property="Background" Value="Black"/>
</Style>
(Removing the template) then the button is shown but its background is not changing.
What is wrong with this xaml code?
You've overridden the template of your control in order to set the MouseOver trigger. That means your control template is otherwise empty - and so nothing is drawn for your button.
You can fix that by moving your triggers to the style itself, like this:
<Style TargetType="Button" x:Key="BlackButton">
<Setter Property="Background" Value="Black"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="red" />
</Trigger>
</Style.Triggers>
</Style>
However, what you're likely to run into now is that the button's built-in MouseOver animation will override your red background. You'll see a flash of red, followed by a transition to the default Windows colour. One way to fix that thoroughly is to take a full copy of the default Button template (using Expression Blend is the easiest way to do this) and remove the animations from it.
Well your ControlTemplate is simply empty, although you have a ControlPresenter in it. But since its Content property is not set, it's also empty. To have a Background you will have to add a Border.
<Style TargetType="Button" x:Key="BlackButton">
<Setter Property="Background" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="red" />
</Trigger>
</ControlTemplate.Triggers>
<Border Background="{TemplateBinding Background}">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This should show you something.

Change disabled listbox background to gray

I have a ListBox which I need to gray out when it's disabled. Per user request it's not enough to disable it, but it also has to appear differently. shrugs I've looked in several other places and followed the examples, and it seems as if it should work, but it does not. Here are some examples I looked at: Example1, Example2.
Here is my code:
<Style x:Key="ListBoxStyle" TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="BorderBrush" Value="Black"></Setter>
<Setter Property="Foreground" Value="LightGray"></Setter>
<Setter Property="Background" Value="LightGray"></Setter>
<Setter Property="BorderBrush" Value="LightGray"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It seems fairly straightforward. I did the same basic process on ComboBox and TextBox with success. Can anyone help me see where my code is wrong? An example of how to do it correctly would be great. The first example listed above seemed to be exactly what I needed, but the correct answer was "The only way to do this is by overriding the template" which doesn't help me.
I've already tried several simple solutions. It's possible some other style may be affecting this because we're working with a couple different Resource Dictionaries. Does anybody know what may be a good way to track this down?
Edit:
I did a search on the entire solution and the only place ListBox is being used is my portion and the only place it's being styled is the styles I've set. According to MSDN there are no "parts" of a ListBox, so it's not possible I inadvertently styled part of the ListBox in the process of styling for some other control. With no styling, when I disable the ListBox, it is frozen but visible with no text, and has a default background. When I try to apply the Property="Background" Value="LightGray" it seems to be hidden (i.e. nothing is visible). If anybody knows why it may be doing this or how to accomplish my task, I'd appreciate the help.
sa_ddam213's answer didn't work for me so i thought i'd add what i found i had to do. In my case, i had set transparent background, but when i disabled the box it would turn gray. I wanted to be able to control the background of the listbox when the control was disabled and here's what i found to work.
note: For your case, you'd want to change the Transparent color to whatever shade of Gray you want.
note2: This will likely only work if you haven't changed the template for the listbox. (changing the datatemplate is ok).
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent"/>
</Style.Resources>
</Style>
</ListBox.Style>
Both answers didn't really work for me so I found a solution that overwrites the ControlTemplate which works for me:
<Style TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Grid Width="Auto" Height="Auto">
<Border x:Name="Border"
BorderThickness="1"/>
<ScrollViewer Focusable="false" IsTabStop="False" HorizontalScrollBarVisibility="Disabled">
<StackPanel IsItemsHost="true"/>
</ScrollViewer>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Border.Background" Value="{StaticResource DisabledBackgroundBrush}"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="{StaticResource DefaultBackground}"/>
</Style>
This helped me: https://social.msdn.microsoft.com/Forums/vstudio/en-US/4b033268-864e-488c-b371-80818daf7f71/can-i-override-the-disabled-background-color-for-a-listbox?forum=wpf
I don't think you need to override the ControlTemplate, just adding a Style.Trigger worked fine for me.
Example:
<ListBox>
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="LightGray" />
<Setter Property="Background" Value="LightGray" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>

A problem with using triggers in XAML

At the code below, when a mouse is over the grid, the grid's Background is expected to be red, but it isn't executed as expected.
<Grid>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
However, if I added the Setter to make the Background green, it would be executed properly.
<Grid>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Setter Property="Background" Value="Green"/><!-- at the former, added code-->
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
I don't know why it is, but guess that there is a precedence for setting the Background, causing the issue. Here is Dependency Property Value Precedence from MSDN, and I understand the precedence of of that reference, but I cannot link this issue to the precedence(MSDN).
Additionally, at above the code snippets, if the Grid is replaced with a Button, both these code will not be executed as expected.
UPDATED: Adding Button case about this issue
<Button>
<Button.Style >
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
The problem is your Grid has a null background, so it is not visible for mouse hit testing. If you set the Background to Transparent, then it will be hit-testable like when you set it to green.
More information can be found here.
I tried your Button and the Background is initialized to Transparent properly. The Red value is only shown for a fraction of a second. This is because in the Aero theme (I'm on Windows 7) the Button's ControlTemplate uses a custom chrome to provide animated state transitions (i.e. when hovering etc). This custom chrome element uses internal brushes and it ignores the Background property.
This has nothing to do with property precedence. For the Grid, it's simply a matter of your Grid not being hit-testable. So it's IsMouseOver will not be set to true, unless it has a non-null background (or a child which renders something).
You can see the Precedence in action here:
<Grid Background="Blue">
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Setter Property="Background" Value="Green"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
In the above, the Grid will always be Blue as that has the highest precedence (i.e. local or #3). While Red (#6) takes precedence over Green (#8).
In the case of the Button, you have something like this:
<Button Background="Blue">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="border" Background="{TemplateBinding Background}">
<ContentPresenter />
</Border >
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
In this case, there are two Background properties in play: the Button and the Border in the control template. The Button's Background property is used by the Border by default, but when the mouse is hovering it uses a Red brush. At that point, it doesn't matter what the value of the Button's Background property is set to.

Resources