WPF: Changing button background and font is not working - c#

I have a list of button in a StackPanel. Those buttons allow me to switch between different usercontrols. When one button is pressed, I want the button style to change so that the user can know which button is active. To achieve this, I have implemented a style trigger that switches the background color as well as the text font on click or on MouseOver. Here is the code:
<Button Uid="0" Width="120" Content="Manage" Height="50" Background="{x:Null}"
BorderBrush="{x:Null}" FontWeight="DemiBold" Click="Button_Click">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsFocused" Value="True"/>
<Condition Property="IsPressed" Value="True"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="BorderBrush" Value="DarkBlue"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="TextBlock.FontWeight" Value="Bold"/>
</MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Uid="1" Width="120" Content="Settings" Height="50" Background="{x:Null}"
BorderBrush="{x:Null}" FontWeight="DemiBold" Click="Button_Click"/>
However, this code does not work and I can't figure out why.
I have seen multiple questions with the same problem and it seems like I am doing it right but somehow, when I run the app, the styling does not work. Any clue as of why?

It will be much simpler if you will using RadioButton and apply style from ToggleButton. Then you can use the property IsChecked to style your button.
Please look at my example:
<StackPanel>
<StackPanel.Resources>
<Style x:Key="MenuButtonStyle" TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}">
<Setter Property="Background" Value="{x:Null}"/>
<Setter Property="BorderBrush" Value="{x:Null}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="TextBlock.FontWeight" Value="DemiBold"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="BorderBrush" Value="DarkBlue"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="TextBlock.FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<RadioButton Uid="0" Width="120" Content="Manage" Height="50" Style="{StaticResource MenuButtonStyle}"/>
<RadioButton Uid="1" Width="120" Content="Settings" Height="50" Style="{StaticResource MenuButtonStyle}" />
</StackPanel>

Related

How to add parameters to an existing style

I have the following button in my XAML:
<Button Content="ADD"
Style="{DynamicResource MaterialDesignFlatButton}" />
I would like to keep the style and create a validation rule for it, like if some textbox is empty it should disable the button and set a tooltip on it.
So I tried this:
<Button Content="ADD"
Command="{Binding AddDateCommand}"
VerticalAlignment="Top"
Margin="8 0 0 0">
<Button.Style>
<Style BasedOn="{StaticResource MaterialDesignFlatButton}">
<triggers to check the rules and set the attributes/>
</Style>
</Button.Style>
</Button>
But I'm getting the following error:
Can only base on a Style with target type that is base type
'IFrameworkInputElement'
You will get an error : Style can be set only once in your present code.
You have to use TargetType with BasedOn styles.
So, define your style should be conceptually like this :
<Style x:Key="MaterialDesignFlatButton" TargetType="Button">
<Setter Property="Background" Value="Red"/>
</Style>
<Style x:Key="NewStyle" BasedOn="{StaticResource MaterialDesignFlatButton}"
TargetType="Button">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=tb, Path=Text.Length, Mode=OneWay}" Value="10"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True"/>
</MultiDataTrigger>
</Style.Triggers>
<Setter Property="BorderBrush" Value="Cornsilk"/>
<Setter Property="BorderThickness" Value="15"/>
<Setter Property="Foreground" Value="MediumPurple"/>
<Setter Property="FontSize" Value="15"/>
</Style>
use IsEnabled property of the button if you have one TextBox:
<Button Content="ADD"
Command="{Binding AddDateCommand}"
VerticalAlignment="Top"
Margin="8 0 0 0"
IsEnabled="{Binding ElementName=yourTxtBox, Path=Text.Length, Mode=OneWay}" />
But for two TextBoxes you should use:
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=firstTxtBox, Path=Text.Length, Mode=OneWay}" Value="0"/>
<Condition Binding="{Binding ElementName=lastTxtBox, Path=Text.Length, Mode=OneWay}" Value="0"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>

Style Basedon not working with checkbox

I have a style defined in a resource dictionary to make my checkbox look like a button:
<Style TargetType="CheckBox" x:Key="CBCheckBoxButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Border Name="BackgroundBorder" Background="Black" CornerRadius="20">
<Grid>
<Rectangle x:Name="UpperRect" Margin="1" Grid.Row="0" RadiusX="20" RadiusY="20" Fill="{StaticResource GrayGradient}"/>
<TextBlock Name="ButtonContent" Text="{TemplateBinding Content}" Margin="3" VerticalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="UpperRect" Property="Fill" Value="{StaticResource LightBlueGradient}" />
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="UpperRect" Property="Fill" Value="{StaticResource BlueGradient}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="BackgroundBorder" Property="Background" Value="DarkGray"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Effect" Value="{StaticResource ShadowEffect}" />
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontFamily" Value="Calibri"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="DarkGray"/>
<Setter Property="Effect" Value="{StaticResource DisableShadowEffect}" />
</Trigger>
</Style.Triggers>
</Style>
When used like so it works great:
<CheckBox Width="80" Height="80" Margin="10" Content="{Binding Y2LockAxisString}" Command="{Binding Y2LockDisableEnableCommand}" Style="{StaticResource CBCheckBoxButton}"/>
But I need to add a style so that different commands etc. are called depending on the state of the check box. So I used the basedon property:
<CheckBox Width="80" Height="80" Margin="10">
<Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource CBCheckBoxButton}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Command" Value="{Binding ExecuteX2LockDisable}" />
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Command" Value="{Binding ExecuteX2LockEnable}" />
</Trigger>
</Style.Triggers>
</Style>
</CheckBox>
But the checkbox looks like a default checkbox with none of the appearance elements applied to it.
Why isn't the appearance style working?
It seems you are adding the Style as Content, instead of Style.
Have you tried
<CheckBox Width="80" Height="80" Margin="10">
<CheckBox.Style>
<Style --- snip --->
</Style>
</CheckBox.Style>
</CheckBox>

How does one make a transparent portion of a button clickable in WPF?

I have a bunch of toolstrip buttons with transparent backgrounds in WPF. When the user mouses over the outside of the button, nothing happens, because that part of the button is transparent. Once the user mouses over one of the non-transparent areas of the button, the "hover" behavior changes the border and background color. The background is no longer transparent, so the hover behavior continues for a much larger area than before. I would like the transparent areas of the button to behave as if the button were non-transparent there.
That is, now I have this behavior where the button is unselected despite the mouse being clearly inside the button area:
and I'm trying to get the button to be selected like this even if the user has not previously moused over the foreground "white" part of the button:
I tried setting IsHitTestVisible on the button itself, but that didn't seem to make any difference.
Is there a way to ask WPF to consider the transparent areas of the button significant?
XAML currently looks like this:
<Button Style="{StaticResource MainToolstripButtonStyle}" Margin="5,0,0,0"
ToolTip="{StaticResource OpenButtonText}"
AutomationProperties.Name="{StaticResource OpenButtonText}"
Command="{StaticResource Open}"
Content="{StaticResource OpenIcon}" />
<Style x:Key="MainToolstripButtonStyle" TargetType="Button">
<Setter Property="Width" Value="24" />
<Setter Property="Height" Value="24" />
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Padding" Value="0" />
<!-- If users want to use keyboard navigation, they use the menu instead. -->
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="Border"
BorderThickness="1"
BorderBrush="{x:Null}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True" />
</Border>
<ControlTemplate.Triggers>
<!-- Low contrast triggers for selection / focus / enabled: -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Source={x:Static vvm:SystemParametersBindingTarget.Instance}, Path=HighContrast}" Value="False" />
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True" />
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter TargetName="Border"
Property="Background"
Value="{StaticResource HotToolbarButtonBrush}" />
<Setter TargetName="Border"
Property="BorderBrush"
Value="{StaticResource HotToolbarButtonBorderBrush}" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Source={x:Static vvm:SystemParametersBindingTarget.Instance}, Path=HighContrast}" Value="False" />
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Foreground"
Value="{StaticResource DisabledToolbarButtonBrush}" />
</MultiDataTrigger>
<!-- High contrast triggers omitted -->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Replace
<Setter Property="Background" Value="{x:Null}" />
by
<Setter Property="Background" Value="Transparent" />
and use the Background property in the ControlTemplate:
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="Border" Background="{TemplateBinding Background}" ...>
...
</Border>
</ControlTemplate>

Change usercontrol border on its GotFocus-event within the XAML of a WPF-Project

I have a UserControl, which contains a Grid which contains a Border:
<UserControl Focusable="True" Name="uctrlScenePanel">
<Grid MouseDown="Grid_MouseDown">
<Border BorderThickness="0" BorderBrush="Black">
</Border>
</Grid>
I now wanted to change the Border-Thickness once the usercontrol "uctrlScenePanel" got Focus, but i can't get it to work and i dont really find useful Eventtrigger-Tutorials which a can understand, because i am very new to WPF.
edit:
d.moncada's answer was very helpful! my xaml now looks like this:
<Border BorderBrush="Black">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderThickness" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsKeyboardFocusWithin, ElementName=uctrlScenePanel}" Value="true">
<Setter Property="BorderThickness" Value="2" />
<Setter Property="Background" Value="Green" />
</DataTrigger>
<DataTrigger Binding="{Binding IsKeyboardFocusWithin, ElementName=uctrlScenePanel}" Value="false">
<Setter Property="BorderThickness" Value="0" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
I have still problems,because the focusing of a user control behaves weird, but thats another story...this one is solved for me, thanks for the lesson ;)
You can bind to the UserControl IsFocused property using a DataTrigger.
<UserControl Focusable="True" x:Name="uctrlScenePanel">
<Grid MouseDown="Grid_MouseDown">
<Border BorderBrush="Black">
<Broder.Style>
<Style TargetType="Border">
<Setter Property="BorderThickness" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsFocused, ElementName=uctrlScenePanel}" Value="True">
<Setter Property="BorderThickness" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</Grid>
</UserControl>

Calling a Image style

Is there any way to create a call'able image style? I have multiple buttons on a user control that have the same button and button image style. I can set the button style so that it can be called from the style (dynamic resource) of the button.
Here is an example of my code and the image style code:
<Grid.Resources>
<Style TargetType="Button"
x:Key="ButtonEditSaveStyle">
<Setter Property="IsEnabled"
Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding CanEdit}"
Value="True">
<Setter Property="IsEnabled"
Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Button Width="32"
Height="22"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Name="gdEmployeeInfo_btnUpdateRecord"
Click="gdEmployeeInfo_btnUpdateRecord_Click"
Style="{DynamicResource ResourceKey=ButtonEditSaveStyle}"/>
I want to set my code up so that I can have the call above an have an additional call or a combined call to set the image style as well. I know I can have the image style in the button (as shown in this next code), but I want to have one place to update multiple buttons using the same style setup. Image style code:
<Image>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source"
Value="edit_32.png" />
<Setter Property="Stretch"
Value="Uniform" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsEditing}"
Value="True">
<Setter Property="Source"
Value="save_smallest.png" />
<Setter Property="Stretch"
Value="Uniform" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
Figured out the answer to my question. This set of code allows me to use dual style setting while creating only one call for the button's style:
<!--SaveEditImageSwitch-->
<Image x:Key="SaveEditImage" x:Shared="False">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source"
Value="edit_32.png" />
<Setter Property="Stretch"
Value="Uniform" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsEditing}"
Value="True">
<Setter Property="Source"
Value="save_smallest.png" />
<Setter Property="Stretch"
Value="Uniform" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<!--ButtonEditSaveStyle-->
<Style TargetType="Button"
x:Key="ButtonEditSaveStyle">
<Setter Property="IsEnabled"
Value="False" />
<Setter Property="Content"
Value="{DynamicResource ResourceKey=SaveEditImage}" />
<Style.Triggers>
<DataTrigger Binding="{Binding CanEdit}"
Value="True">
<Setter Property="IsEnabled"
Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
<Button Width="32"
Height="22"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Name="gdEmployeeInfo_btnUpdateRecord"
Click="gdEmployeeInfo_btnUpdateRecord_Click"
Style="{DynamicResource ResourceKey=ButtonEditSaveStyle}">

Categories