WPF中Button空白区域无法点击的解决方法

前言

WPF的Button有一点特别奇怪的地方是

当您单击按钮的空白区域时,该按钮不会触发 Click 事件,因为该事件只会在按钮的可见内容区域内发生。

解决方式有两种

  1. 改变可见区域。
  2. 使用PreviewMouseDown事件中触发Click事件。

推荐使用第一种方法,第二种按钮的悬浮样式依旧不会触发。

方式1

改变可见区域

核心代码

1
2
3
4
5
6
7
8
9
<Button Content="My Button">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<ContentPresenter />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>

就是在Button内部渲染区域的外层添加了一个Grid,并且设置背景色为Transparent

优点奇葩的是

这个背景色必须设置,即使是设置的透明色,也算是可见区域了。

完整自定义按钮代码如下:

StyleZRoundButton.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ZView">
<Style x:Key="ZRoundButtonStyle" TargetType="{x:Type local:ZRoundButton}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<Border
Name="border"
Padding="{TemplateBinding Padding}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{Binding Path=BorderRadius, RelativeSource={RelativeSource TemplatedParent}}"
IsHitTestVisible="True">
<ContentPresenter
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.6" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

ZRoundButton.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System.Windows;
using System.Windows.Controls;

namespace ZView
{
internal class ZRoundButton : Button
{
public int BorderRadius
{
get { return (int)GetValue(BorderRadiusProperty); }
set { SetValue(BorderRadiusProperty, value); }
}

public static readonly DependencyProperty BorderRadiusProperty = DependencyProperty.Register(
"BorderRadius",
typeof(int),
typeof(ZRoundButton),
new FrameworkPropertyMetadata()
);
}
}

方式2

要在单击按钮的任何位置时都触发 Click 事件,您可以使用 PreviewMouseDown 事件。该事件会在鼠标按下时触发,并在 Click 事件之前发生。

您可以通过以下方式将 PreviewMouseDown 事件处理程序绑定到按钮:

1
<Button Content="My Button" PreviewMouseDown="Button_PreviewMouseDown" Click="Button_Click"/>

然后,在您的代码中实现 Button_PreviewMouseDown 和 Button_Click 事件处理程序。

在 Button_PreviewMouseDown 处理程序中,您可以使用以下代码触发 Click 事件:

1
2
3
4
5
6
7
8
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
Button button = (Button)sender;
button.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
}
}

这将在单击按钮的任何位置时触发 Click 事件。

请注意,如果您希望仅在单击左键时触发 Click 事件,则可以使用上面示例代码中的 if 语句来检查 e.ChangedButton 的值。