Button in WPF acting in strange way with isMouseOver - c#

I am having a funny issue here with WPF and a button styled. Problem is it contains a path and isMouseOver seems to only trigger when the mouse pointer is over the path but not over the button. That is I have to move the mouse pointer over the white figure inside the button to have the button shaded. Is as if the button wouldn't exist and only the path would.
This is the code for the button:
<Button Grid.Column="5" x:Name="btnClose" Width="30" Height="30" BorderBrush="White"
BorderThickness="0" Style="{StaticResource MenuButtonStyle}"
Command="{Binding ExitCommand}">
<Path Data="M0,0 L16,16 M16,0 L0,16" Stroke="White" StrokeThickness="3"
Margin="0,1,0,0" StrokeEndLineCap="Round" StrokeStartLineCap="Round" />
</Button>
And this is the style:
<Style x:Key="MenuButtonStyle" TargetType="Button">
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="80" />
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="FontSize" Value="13.5"/>
<Setter Property="Foreground" Value="Red"/>
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="ToolTipService.ShowOnDisabled" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="2.5"
x:Name="ButtonBorder"
BorderThickness="1.2"
RenderTransformOrigin="0.5,0.5">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0">
<Border x:Name="ButtonShine" Grid.Row="0" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" >
<!--<Border.Effect>
<DropShadowEffect ShadowDepth="1" BlurRadius="1" RenderingBias="Quality" Direction="270" />
</Border.Effect>-->
</Border>
<ContentPresenter Grid.Row="0" VerticalAlignment="Center"
HorizontalAlignment="Center" />
<Border x:Name="shadowMouseOver" Grid.Row="0" Visibility="Hidden"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Background="#7F171717">
</Border>
<Border x:Name="shadowDisabled" Grid.Row="0" Visibility="Hidden"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Background="#AFFFFFFF">
</Border>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsEnabled" Value="false">
<Setter Property="Button.Foreground" Value="#FFDEDEDE" />
<Setter Property="Visibility" TargetName="shadowDisabled" Value="Visible"/>
</Trigger>
<Trigger Property="Button.IsMouseOver" Value="true">
<Setter Property="Visibility" TargetName="shadowMouseOver" Value="Visible"/>
</Trigger>
<Trigger Property="Button.IsPressed" Value="true">
<Setter Property="Button.RenderTransformOrigin" Value="0.5,0.5"/>
<Setter Property="Button.RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="0.95" ScaleY="0.95"/>
</Setter.Value>
</Setter>
<Setter Property="Visibility" TargetName="shadowMouseOver" Value="Visible"/>
<Setter Property="Background" TargetName="ButtonShine" >
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFBFBFBF" Offset="1"/>
<GradientStop Color="#FF383838"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" TargetName="ButtonShine" >
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFBFBFBF" Offset="1"/>
<GradientStop Color="#FF383838"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Any ideas? Bug in WPF? Something not done properly?

This is definitely not a bug, but due to the fact that you provide no background for the control in its template for the default state; thus, the hit test fails in the region outside of the path. If you notice, both shadowMouseOver and shadowDisabled, the only two borders that provide any background, are not visible unless the appropriate events are triggered. Thus, they are not visible to hit tests. Your path, on the other hand, is visible and so it's able to trigger events.
See Hit Testing in the Visual Layer at MSDN for more information:
https://msdn.microsoft.com/library/ms752097(v=vs.100).aspx
Just in case that link fails some time in the future, here's one of the parts that pretty much summarizes what I'm trying to say here:
The purpose of the HitTest methods in the VisualTreeHelper class is to
determine whether a geometry or point coordinate value is within the
rendered content of a given object, such as a control or graphic
element. For example, you could use hit testing to determine whether a
mouse click within the bounding rectangle of an object falls within
the geometry of a circle. You can also choose to override the default
implementation of hit testing to perform your own custom hit test
calculations. The following illustration shows the relationship
between a non-rectangular object's region and its bounding rectangle.
You have a couple of options if you wish to maintain the transparent background look. Either set that background to Transparent, due to the fact that transparent objects are visible to hits tests, or set it to some color, while turning its opacity down to 0. Best place for that background would probably be at ButtonBorder (most outer border), but it'll be your decision to make.

Related

how to change button background in UWP

I want to change button background by clicking the button. But, I don't want to change background using click event.
I have tried in WPF and achieved it using below code:
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Background"
Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Border CornerRadius="3"
BorderBrush="DarkGray"
BorderThickness="1"
Background="{TemplateBinding Background}" />
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers >
<Trigger Property="IsFocused"
Value="True">
<Setter Property="BorderThickness"
Value="2"/>
<Setter Property="FontWeight"
Value="Bold"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Button Content="All"
Height="20"
Width="50"
Margin="2"
Style="{StaticResource ButtonStyle}" />
Can anyone please suggest how to achieve the same functionality in UWP?
I want to change button background by clicking the button. But, I don't want to change background using click event.
For your requirement, You could realize it via use XamlBehaviors. For example:
<Button>
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="Click">
<Core:ChangePropertyAction PropertyName="Background">
<Core:ChangePropertyAction.Value>
<SolidColorBrush Color="Red"/>
</Core:ChangePropertyAction.Value>
</Core:ChangePropertyAction>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
It is open source model and host all behaviors code on GitHub will allow new features and fixes to be addressed more quickly.

C# WPF Dynamic Button STYLE that has different pictures based on the x:name or x:key they have

I would like to make a game like Shakes & Fidgets. I got stuck at the Main menu, where I already overcomplicated stuff as I always do. I made a grid layout, where I will put the buttons, but every button is a picture. I use ImageBrush for every button's picture I want to create.
I would like to create ONE style for every button so they change their backgrounds based on the x:Name or x:Key they have. So a Button with x:Name or x:Key "PlayGame" would find it's as the PlayGame.png, PlayGame_Hover, PlayGame_OnClick where "PlayGame" is a variable.
In other words I would like to have a style that can filter the x:name, or x:key of a button, and uses it as a variable later on so I can do this: {StaticResource VARIABLENAME}
The Code I have now is:
<ImageBrush x:Key="PlayGame">
<ImageBrush.ImageSource>
<BitmapImage UriSource="./Pictures/PlayGameButton.png"/>
</ImageBrush.ImageSource>
</ImageBrush>
<ImageBrush x:Key="PlayGame_Hover">
<ImageBrush.ImageSource>
<BitmapImage UriSource="./Pictures/PlayGameButton_Hover.png"/>
</ImageBrush.ImageSource>
</ImageBrush>
<ImageBrush x:Key="PlayGame_OnClick">
<ImageBrush.ImageSource>
<BitmapImage UriSource="./Pictures/PlayGameButton_OnClick.png"/>
</ImageBrush.ImageSource>
</ImageBrush>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="{StaticResource PlayGame}" />
<Setter Property="FontSize" Value="15" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border CornerRadius="4" Background="{TemplateBinding Background}">
<Grid>
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource PlayGame_Hover}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{StaticResource PlayGame_OnClick}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I found a not very effective solution, but not the one I actually want
You can give the Style an
x:Key="styleKey"
and you have to give the button this part:
Style="{StaticResource styleKey}"
This way you will have to make a style for every different button you want to have, but it will work, and you will be happy about it if efficency is not key.:D

How to create the round cross button for close popup in wpf?

I am working on wpf slider, i want a cross round button on the slider control, i have attached the image, can you please help me what style me should write to get attached ?
i tried in following way but didn't work for me its created according to
<Style x:Key="RoundCorner" TargetType="{x:Type Button}">
<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}">
<Grid x:Name="grid">
<Border x:Name="border" Background="Black" CornerRadius="15" BorderBrush="White" BorderThickness="1">
<ContentPresenter HorizontalAlignment="Center" TextElement.FontFamily="Arial Rounded MT Bold" VerticalAlignment="Center" TextElement.FontWeight="Bold"></ContentPresenter>
</Border>
</Grid>
<!--<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" TargetName="border">
<Setter.Value>
<RadialGradientBrush GradientOrigin="0.496,1.052">
<RadialGradientBrush.RelativeTransform>
<TransformGroup>
<ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="0.5" ScaleY="0.5"/>
<TranslateTransform X="0.02" Y="0.02"/>
</TransformGroup>
</RadialGradientBrush.RelativeTransform>
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>-->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style
One approach is using dingbat fonts.
<Button FontFamily="Wingdings 2">U</Button>
'U' can be replaced by 'V' for thick circular button. It's case sensitive. I mean using 'u' instead 'U' produces different results.
Edit the ControlTemplate of Slider and add your styled button to the existing slider template. Docking can be helpful to place the button correctly onto the slider

change background toggle button

I try to change the background of a toggle button everytime when it is checked or not,using 2 pictures. At runtime, the toggle button is simple and I don't see any image. This is my code:
<Canvas Height="25" Name="canvas1" Width="186" Background="White">
<ToggleButton Name="toggle1" Height="25" Padding="0" Width="27" Canvas.Left="131" Canvas.Top="0" BorderBrush="{x:Null}" Foreground="{x:Null}">
<ToggleButton.Resources>
<Style x:Key="OnOffToggleImageStyle" TargetType="ToggleButton">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/WpfApp;component/Images/image1.png" Stretch="Uniform" TileMode="None" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/WpfApp;component/Images/image2.png" Stretch="Uniform" TileMode="None" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Multiple things we can point here.
Firstly you're specifying the Style in ToggleButton.Resources with a x:Key. Thus it's not implicitly set. Setting a Style in resources of the item for which it applies in it's own scope is kinda weird. Rather just apply the Style directly as <ToggleButton.Style>.
Next, when working with Style.Triggers, make it a practice to specify a default Setter. This will prevent some headache for you. Also now instead of having two triggers for IsChecked=False/True, you only need one trigger as the other Background can be set as default.
Finally it seems like all you need from the ToggleButton is the IsChecked property and switch an Image. So just override the Template and provide a simple one yourself. This way you assure the behavior to stay consistent in all version of the OS and not rely on internal default template's working nicely for your customization(which wouldn't work in Windows-8 btw, what you're trying here for IsChecked=True due to internal overrides in default ControlTemplate)
So putting this all together we get:
<Style x:Key="MyToggleButtonStyle"
TargetType="ToggleButton">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/WpfApp;component/Images/image2.png"
Stretch="Uniform"
TileMode="None" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Background="{TemplateBinding Background}" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked"
Value="True">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/WpfApp;component/Images/image1.png"
Stretch="Uniform"
TileMode="None" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
and apply it to your ToggleButton
<ToggleButton Name="toggle1" Height="25" Padding="0" Width="27" Canvas.Left="131" Canvas.Top="0" BorderBrush="{x:Null}" Foreground="{x:Null}" Style="{DynamicResource MyToggleButtonStyle}">
you can ofc remove the Key in the Style and make it implicitly apply to "all" ToggleButton's within the scope of the Style definition if you choose to.
Can you check if the build action for your images is Embedded Resource please?
Define the style directly for your togglebutton like below. it should fix your issue:
<ToggleButton>
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/WpfApp;component/Images/image2.png" Stretch="Uniform" TileMode="None" />
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/WpfApp;component/Images/image1.png" Stretch="Uniform" TileMode="None" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
You should remove the x:Key and it will work.

Hover over label change rectangle background gradient

I have a rectange with several labels and images over it, and I have it so that when the user hovers their mouse over the rectangle the background changes to a gradient:
<Rectangle Height="88" HorizontalAlignment="Left" Margin="54,28,0,0" Name="rectangle2" VerticalAlignment="Top"
Width="327" Cursor="Hand">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Fill" Value="Transparent" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill">
<Setter.Value>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="White" Offset="0.0" />
<GradientStop Color="#eee" Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
However, when I hover over one of the labels that is over the rectangle the background gradient does not show.
I want to make it so that the gradient shows when hovering over the labels as well as the rectangle.
Is this possible?
If by "over" you mean overlayed and not above you can wrap the contents in a Grid (for above you could do this as well i guess, but you should define rows & columns) and use a DataTrigger which triggers if the mouse is over the wrapping grid and not only the rectangle itself, e.g.:
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle Width="100" Height="100" StrokeThickness="1" Stroke="Black">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Fill" Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType=Grid}}" Value="True">
<Setter Property="Fill">
<Setter.Value>
<!-- Brush here -->
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
<Label Name="label" Content="This is a Label" />
</Grid>
Alternatively if the label is overlayed you can make mouse events pass through the Label by setting IsHitTestVisible to false.
Because the other elements are on top of the rectangle, I think you will need to hook to the PreviewMouseMove event for the rectangle.
UIELement.PreviewMouseMove: http://msdn.microsoft.com/en-us/library/system.windows.uielement.previewmousemove.aspx
Preview events: http://msdn.microsoft.com/en-us/library/ms752279.aspx

Categories