WPF中的GroupBox标头是否会吞下鼠标单击?
看一下这个非常简单的示例WPF程序:
<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <GroupBox> <GroupBox.Header> <CheckBox Content="Click Here"/> </GroupBox.Header> </GroupBox> </Window>
所以我有一个GroupBox,其标题是CheckBox。我们都做了类似的事情,通常,我们将GroupBox的内容绑定为一种方式,即在未选中CheckBox时将其禁用。
但是,当我运行该应用程序并单击CheckBox时,我发现有时我的鼠标点击被吞咽了,并且CheckBox的状态没有改变。如果我是对的,那就是当我单击GroupBox顶部边框所在的确切像素行时。
有人可以复制吗?为什么会发生这种情况,并且有解决办法?
编辑:将GroupBox的BorderThickness设置为0可以解决此问题,但显然它会删除边框,因此它看起来不再像GroupBox。
解决方案
如果我们更改了GroupBox的BorderBrush,它将起作用!
<GroupBox BorderBrush="{x:Null}">
我知道这不能达到目标,但确实可以证明问题出在哪里!
它似乎是GroupBox控件模板中的一个细微错误。我发现通过编辑GroupBox的默认模板并将名为" Header"的边框移动到控件模板Grid元素的最后一项,问题得以解决。
原因是带有BorderBrush的TemplateBinding的其他Border元素之一在可视树中更靠下,并且捕获了鼠标单击,这就是为什么将BorderBrush设置为null可以使CheckBox正确接收鼠标单击的原因。
下面是GroupBox的最终样式。它与控件的默认模板几乎相同,除了名为" Header"的Border元素外,该元素现在是Grid的最后一个子级,而不是第二个。
<BorderGapMaskConverter x:Key="BorderGapMaskConverter"/> <Style x:Key="GroupBoxStyle1" TargetType="{x:Type GroupBox}"> <Setter Property="BorderBrush" Value="#D5DFE5"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type GroupBox}"> <Grid SnapsToDevicePixels="true"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> <RowDefinition Height="6"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="6"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="6"/> </Grid.ColumnDefinitions> <Border Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="1" Grid.RowSpan="3" Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4"/> <ContentPresenter Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="2"/> <Border Grid.ColumnSpan="4" Grid.Row="1" Grid.RowSpan="3" BorderBrush="White" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4"> <Border.OpacityMask> <MultiBinding Converter="{StaticResource BorderGapMaskConverter}" ConverterParameter="7"> <Binding Path="ActualWidth" ElementName="Header"/> <Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}"/> <Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}"/> </MultiBinding> </Border.OpacityMask> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3"> <Border BorderBrush="White" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2"/> </Border> </Border> <Border x:Name="Header" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" Padding="3,1,3,0"> <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header" RecognizesAccessKey="True"/> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
我提出的另一种解决方案是在派生的GroupBox中实现OnApplyTemplate:
public override void OnApplyTemplate() { base.OnApplyTemplate(); if (Children.Count == 0) return; var grid = GetVisualChild(0) as Grid; if (grid != null && grid.Children.Count > 3) { var bd = grid.Children[3] as Border; if (bd != null) { bd.IsHitTestVisible = false; } } }
伊恩·奥克斯(Ian Oakes)回答了制表符的排序问题,使标题位于内容之后。可以修改控件模板,使边框无法获得焦点。
为此,请修改模板,使第二个和第三个边界(在网格行1中)都具有" IsHitTestVisible = false"。
完整的模板如下
<BorderGapMaskConverter x:Key="GroupBoxBorderGapMaskConverter" /> <Style x:Key="{x:Type GroupBox}" TargetType="{x:Type GroupBox}"> <Setter Property="Control.BorderBrush" Value="#FFD5DFE5" /> <Setter Property="Control.BorderThickness" Value="1" /> <Setter Property="Control.Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type GroupBox}"> <Grid SnapsToDevicePixels="True"> <Grid.ColumnDefinitions> <ColumnDefinition Width="6" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="6" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="6" /> </Grid.RowDefinitions> <Border Name="Header" Padding="3,1,3,0" Grid.Row="0" Grid.RowSpan="2" Grid.Column="1"> <ContentPresenter ContentSource="Header" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" /> </Border> <Border CornerRadius="4" Grid.Row="1" Grid.RowSpan="3" Grid.Column="0" Grid.ColumnSpan="4" BorderThickness="{TemplateBinding Control.BorderThickness}" BorderBrush="#00FFFFFF" Background="{TemplateBinding Control.Background}" IsHitTestVisible="False" /> <ContentPresenter Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Margin="{TemplateBinding Control.Padding}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"/> <Border CornerRadius="4" Grid.Row="1" Grid.RowSpan="3" Grid.ColumnSpan="4" BorderThickness="{TemplateBinding Control.BorderThickness}" BorderBrush="#FFFFFFFF" IsHitTestVisible="False"> <UIElement.OpacityMask> <MultiBinding Converter="{StaticResource GroupBoxBorderGapMaskConverter}" ConverterParameter="7"> <Binding ElementName="Header" Path="ActualWidth" /> <Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" /> <Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}" /> </MultiBinding> </UIElement.OpacityMask> <Border BorderThickness="{TemplateBinding Control.BorderThickness}" BorderBrush="{TemplateBinding Control.BorderBrush}" CornerRadius="3"> <Border BorderThickness="{TemplateBinding Control.BorderThickness}" BorderBrush="#FFFFFFFF" CornerRadius="2" /> </Border> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>