Control Template to modify background colour based on IsChecked - wpf

In order to change the visual behaviour of a control, it's necessary to extract the entire ControlTemplate into the xaml and modify the appropriate section. This is not a problem (form the properties view of the control, click on the little square to the right of the Template property and select Convert to New Resource), then edit like...
<ControlTemplate x:Key="ToggleButtonControlTemplate1" TargetType="{x:Type ToggleButton}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ContentPresenter x:Name="contentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}" Focusable="False"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}" RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsDefaulted" Value="True">
<Setter Property="BorderBrush" TargetName="border"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" TargetName="border" Value="#FFBEE6FD"/>
<Setter Property="BorderBrush" TargetName="border" Value="#FF3C7FB1"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" TargetName="border" Value="#FFC4E5F6"/>
<Setter Property="BorderBrush" TargetName="border" Value="#FF2C628B"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" TargetName="border" Value="#FFBCDDEE"/>
<Setter Property="BorderBrush" TargetName="border" Value="#FF245A83"/>
<Setter Property="Background" TargetName="border" Value="Red"/> <!-- mod -->
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" TargetName="border" Value="#FFF4F4F4"/>
<Setter Property="BorderBrush" TargetName="border" Value="#FFADB2B5"/>
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="#FF838383"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I can see that the Border element has been used as the root node in this case and that the ContentPresenter is in the Border Content property,
but, what is the logic behind using the Border element as the visual root and why is the background color of the button managed by setting the BackGround property of the Border?
Also, what elements are available to be used as root nodes and what are the inheritance rules? What is the visual structure of the standard controls and Where can I find some reasonable documentation about this?
I have some vague understanding about the VisualTreeRootNode and the need for a single root node for a template but... only vague.
References
There is some background here but no definitive reference.
The basic concept is documented here and you can drill down to individual controls to find examples. The link to Visual Tree looks promising but its broken. The button section gives some examples but does not give any definitive reference about the topic. I could not answer my questions based on what I read here.

The Border is like a box around your button. It has the Border and the Background properties of a control. So most Controls have Border in their Template, but you can template any Control like you want. You could even use an Ellipse to make a round Button or put every thing into a Grid. For the Functionality the important part is the ContentPresenter. It defines where the Content you set is shown.
The Microsoft documentation delivers good exsamples for every control you want to use. It contains every named Part of the Control and every VisualState it can take.
e.g. https://msdn.microsoft.com/de-de/library/ms753328(v=vs.110).aspx for a Button
For a detailed answer how Templating works in WPF see also:
https://msdn.microsoft.com/en-us/en-en/library/ee230084(v=vs.110).aspx
The Link shows how a Template is built, with the button example and how to set Visual States of a Button.

Related

WPF changing Button background temporarily on Click

I have a white XAML button in my program, that when I click it should change it's background color to green and then back to white (as a confirmation of it being clicked). I already tried this and this but could not get it to work either way. The problem with the second link being me not understanding the answers. The solution of the first link just does not change the color of the button. The following is my XAML style for the button.
<Style x:Key="ButtonStyleGeneral" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
<Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Button.Background" Value="Green" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So how can i achieve this short color change to confirm to the user that the button has been pressed?
UPDATE: The code below works like a charm for new buttons but stops working as soon as i change the background color of the button in the designer!
You should set the Background property of the Border element in your trigger:
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="border" Property="Background" Value="Green" />
</Trigger>
A local value takes precedence over the value set by a Style. Setting the Background of the Border´ element in the template instead of the Buttonitself fixes the issue. Then your template should work with aButton` element like this:
<Button Background="Yellow" Style="{StaticResource ButtonStyleGeneral}" Content="Button" />

Use a dependency property to set a value in ControlTemplate trigger

Creating a button that uses an image for the background, when button pressed a different image is used for the background. Inherited from button and added two dependency properties of type ImageBrush, one is PressedBackground. There will later be an enum type property, which will be used to select which set of image resources will be loaded in 'PressedImage' and 'NormalImage'.
In controltemplate, using a data trigger for "IsPressed", to set the background property to the imagebrush in 'PressedBackground'.
But when run, on press we have no background, debug tracing says "Cannot retrieve value using binding and no valid fallback value exists.
Problem is loading an imagebrush, from a dependency property into the background property, in the onpressed data trigger. I have tried all sorts to get a value into the background property, but with out success
Custom class snippet
public class QuantaImageButton : Button
{
static QuantaImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(QuantaImageButton),
new FrameworkPropertyMetadata(typeof(QuantaImageButton)));
}
public static readonly DependencyProperty PressedBackgroundProperty = DependencyProperty.Register(
"PressedBackground", typeof(ImageBrush), typeof(QuantaImageButton), new PropertyMetadata(default(ImageBrush)));
public ImageBrush PressedBackground
{
get { return (ImageBrush) GetValue(PressedBackgroundProperty); }
set { SetValue(PressedBackgroundProperty, value); }
}
// other dependency properties
}
style is:
<Style x:Key="BtnQuanta" TargetType="controls:QuantaImageButton">
<Setter Property="PressedBackground" Value="{StaticResource BtnMenuMediumDownImage}"/>
<Setter Property="Background" Value="{StaticResource BtnMenuMediumUpImage}"/>
<Setter Property= "Width" Value="500" />
<Setter Property="Height" Value="470"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontSize" Value="52"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template" Value="{StaticResource QuantaBtnControlTemplate}"/>
</Style>
<ControlTemplate x:Key="QuantaBtnControlTemplate" TargetType="{x:Type controls:QuantaImageButton}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{Binding PressedBackground, RelativeSource={RelativeSource TemplatedParent} }"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I found the answer - as the debug text suggests - it cant find the property. The binding was incorrect - I have changed it to :
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{Binding PressedBackground, RelativeSource={RelativeSource Self} }"/>
</Trigger>
</ControlTemplate.Triggers>
The RelativeSource needs to be {RelativeSource Self} for the local object - not TemplateParent - which does not have the new property.
As an 'old' Winforms guy, moving over to WPF - it would be a great help if any one has a link to more explanation about this sort of binding and how you use it.
if you add TargetName="border" to apply Background to the border inside Template, then {RelativeSource TemplatedParent} works:
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background"
Value="{Binding Path=PressedBackground, RelativeSource={RelativeSource TemplatedParent} }"/>
</Trigger>
</ControlTemplate.Triggers>
Adding TargetName changes target of binding, and apparently it affects RelativeSource resolution. However I cannot pin-point this subtle difference anywhere in the docs

Why is some of the text in my WPF 4.5 application still blurry?

I don't understand. I am going crazy - no matter what I do, the text in my WPF application is blurry. Well, some of it - one of the text elements is focused, and so are the close/minimize buttons. I have applied the TextOptions.TextRenderingMode="ClearType" and TextOptions.TextFormattingMode="Display" to the elements directly, and I have also tried applying it to the MainWindow.xaml, which is created by default using the ModernUI for WPF framework.
I'm going nuts - all the literature I find says this was fixed, but I'm still dealing with the issue. (I've changed the font to Calibri/Consolas and also played with the size and weight - still blurry.)
How can I fix this?
Edit: If I use the monitor I have at work (resolution 1920x1200) with standard DPI settings, I'm not so sure I have the issue. On the laptop display I am using I have a very high resolution (2880x1620) with the text scaling set to larger. On this display is where I'm currently seeing the text as "not crisp". I should also note that in the designer, the text appears fine. It is when the application runs that the text looks terrible.
So, I found out my issue is specifically with the Modern UI Framework. I'm not sure why. I switched over to using MahApps.Metro and I have no issues with font clarity.
to start with you can try with
<TextBlock Text="Am I Still Blurry." RenderOptions.ClearTypeHint="Enabled"/>
you might wanna have a look at this post for clearer understanding
It is by Design. Those different set of controls and they have different styles of font color. For Example Settings and Help in the Top right of the window they are using the style SystemButtonLink which is defined below
<Style x:Key="SystemButtonLink" TargetType="ButtonBase" BasedOn="{StaticResource SystemButtonBase}" >
<Setter Property="Foreground" Value="{DynamicResource LinkButtonText}"/>
<Setter Property="Width" Value="NaN" />
<Setter Property="Height" Value="NaN" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="11" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ButtonBase}">
<Border Name="Chrome"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
SnapsToDevicePixels="true">
<TextBlock DataContext="{TemplateBinding Content}"
Text="{Binding Converter={StaticResource ToUpperConverter}}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{DynamicResource LinkButtonTextHover}"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Foreground" Value="{DynamicResource LinkButtonTextPressed}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource LinkButtonTextDisabled}" />
</Trigger>
</Style.Triggers>
</Style>
If you refer the three colors used in the style for Mouse hover, Pressed and IsEnabled.
More code can be refered in the site. https://mui.codeplex.com/SourceControl/latest

Change width of checkable area of checkbox

I have a CheckBox object and it doesn't have any text associated with it.
It is in a Grid which has another coulmn used as the label for it.
The whitespace next to the checkbox you can click and it will toggle checkbox
I want it just be the area of the actual checkbox and not the white space around it(because the text for it is set to nothing).
I tried setting the width = 5 but that didn't make a difference
thank you very much!!
It sounds like your CheckBox is stretching to the whole width of the grid cell (with all of that width clickable), and you don't want it to stretch.
Set the CheckBox's HorizontalAlignment property to Left (or Center, or Right). Then it will be exactly the width it needs to be for the checkbox, rather than stretching to the entire width provided by its parent.
For that you will have to go into the checkboxes template and modify that. Specifically you should go in and remove the ContentPresenter which is what displays the text. Since you have no text, it is not a problem. The end result will look something like this. Just add that style to your checkbox.
The default templates ContentPresenter is housed under the Bullet. Because of this, clicking on that content presenter (even if empty, I think it has a default size) will activate the controls click logic.
<Style x:Key="CheckBoxStyle1" TargetType="{x:Type CheckBox}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Background" Value="{StaticResource CheckRadioFillNormal}"/>
<Setter Property="BorderBrush" Value="{StaticResource CheckRadioStrokeNormal}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource EmptyCheckBoxFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<BulletDecorator Background="Transparent" SnapsToDevicePixels="true">
<BulletDecorator.Bullet>
<Microsoft_Windows_Themes:BulletChrome BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" IsChecked="{TemplateBinding IsChecked}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}"/>
</BulletDecorator.Bullet>
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="HasContent" Value="true">
<Setter Property="FocusVisualStyle" Value="{StaticResource CheckRadioFocusVisual}"/>
<Setter Property="Padding" Value="2,0,0,0"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

WPF: How to programmatically give visual feedback that the keyboard focus is in a listbox?

I am writing an application with a listbox allowing multi-selection (SelectionMode=Multiple); the items in the lisbox are ingredients for a recipe.
Unfortunately, clicking on a listbox item selects this item, which may not be desired. I would like the following scenario:
the user clicks on the listbox in order to select the listbox (the listbox itself, not an item)
the user scrolls to the right item and selects it
What I did is style the ListBoxItem to include a checkbox and a ContentPresenter (like in this blog).
Still, clicking on the ingredient name selects it.
So, I trap the MouseDown event on the textblock containing the ingredient name, find the underlying ListBoxItem, call Focus() on it and set the Handled property of the event to true.
Now, the Listbox item has the focus but is not selected. Using the up and down keys shows that the focus was on the right item.
My problem is that the user cannot see that he has clicked on the right item. The dotted rectangle is not shown on this item.
Here is the result:
And here is what I'd like:
I've tried calling private WPF methods, like KeyboardNavigation.ShowFocusVisual, I've tried sending keystrokes to the listbox (when done by a human, pressing the right cursor key or the Alt key makes the dotted rectangle appear).
Any idea ?
SendInput is the only way I've found which gets past this. From this link.
PInvoke to SendInput – this is the
official way to simulate input. It
pushes the input through all of the
expected code paths, and is
indistinguishable from real input.
An easy way to use this is with InputSimulator from CodePlex.
Adding a reference to InputSimulator.dll we can do something like this
private bool m_waitingForFocusVisualStyle = false;
private void ListBoxItem_GotFocus(object sender, RoutedEventArgs e)
{
if (m_waitingForFocusVisualStyle == false)
{
m_waitingForFocusVisualStyle = true;
InputSimulator.SimulateKeyDown(VirtualKeyCode.TAB);
InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.SHIFT, VirtualKeyCode.TAB);
}
else
{
m_waitingForFocusVisualStyle = false;
}
}
But this might not be ideal for a lot of reasons (Shift+Tab to the ListBoxItem for example)
A better idea is probably to remove the FocusVisualStyle for ListBoxItem and add your own in the ControlTemplate like this. (Copied from Blend and added a "FocusVisualStyle" from standard FocusVisualStyle)
<ListBox ...>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template" Value="{StaticResource ListBoxItemTemplate}" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<ControlTemplate x:Key="ListBoxItemTemplate" TargetType="{x:Type ListBoxItem}">
<Grid>
<Rectangle Grid.ZIndex="1"
Name="focusVisualStyle"
StrokeThickness="1"
Stroke="Black"
StrokeDashArray="1 2"
SnapsToDevicePixels="true"
Visibility="Hidden"/>
<Border x:Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter TargetName="focusVisualStyle" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I have found Meleak's answer very helpful, but using GotFocus does not work for me. Instead I have bind the even handler to PreviewMouseLeftButtonDown even. Now you don't need boolean property to store the state and the code is very simple:
void SlideCanvasPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
InputSimulator.SimulateKeyDown(VirtualKeyCode.TAB);
InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.SHIFT, VirtualKeyCode.TAB);
}
This does the job for me very good.
P.S. I use this style - it has some fine animation of moving dashed rectanle

Resources