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>
つまり、ヘッダーがCheckBoxであるGroupBoxがあります。私たちは皆このようなことをしました-通常、チェックボックスがチェックされていないときに無効になるようにGroupBoxのコンテンツをバインドします。
ただし、このアプリケーションを実行してCheckBoxをクリックすると、マウスクリックが飲み込まれ、CheckBoxのステータスが変わらないことがあります。正しい場合は、GroupBoxの上部の境界線が置かれているピクセルの正確な行をクリックしたときです。
誰かがこれを複製できますか?なぜこれが発生するのか、そしてそれを回避する方法はありますか?
編集:GroupBoxのBorderThicknessを0に設定すると問題が解決しますが、明らかに境界が削除されるため、GroupBoxのようには見えなくなります。
解決
GroupBoxのコントロールテンプレートの微妙なバグのようです。 GroupBoxのデフォルトテンプレートを編集し、「Header」という名前のBorderをコントロールテンプレートのGrid要素の最後のアイテムに移動すると、問題は解決します。
理由は、BorderBrushのTemplateBindingを持つ他のBorder要素の1つがビジュアルツリー内でさらに下にあり、マウスクリックをキャプチャしていたためです。
以下はGroupBoxの結果のスタイルです。これは、コントロールのデフォルトのテンプレートとほぼ同じですが、「Header」という名前のBorder要素が2つ目ではなく、グリッドの最後の子になります。
<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>
他のヒント
Ian Oakesは、ヘッダーがコンテンツの後に来るように、タブオーダーを埋めます。境界線がフォーカスを受け取れないようにコントロールテンプレートを変更することは可能です。
これを行うには、2番目と3番目の境界(両方ともグリッド行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>
私が作成した代替ソリューションは、派生グループボックスに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;
}
}
}
GroupBoxのBorderBrushを変更すると、機能します!
<GroupBox BorderBrush="{x:Null}">
これは目標を破ることは知っていますが、問題のある場所を証明しています!