41 votes

L'en-tête GroupBox de WPF avale-t-il les clics de souris ?

Jetez un coup d'œil à cet exemple de programme WPF très simple :

<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>

J'ai donc une GroupBox dont l'en-tête est une CheckBox. Nous avons tous fait quelque chose de ce genre - en général, vous liez le contenu de la GroupBox de manière à ce qu'il soit désactivé lorsque la CheckBox n'est pas cochée.

Cependant, lorsque j'exécute cette application et que je clique sur la case à cocher, je constate que mes clics de souris sont parfois avalés et que l'état de la case à cocher ne change pas. Si j'ai raison, c'est lorsque je clique sur la rangée exacte de pixels sur laquelle se trouve la bordure supérieure de la GroupBox.

Quelqu'un peut-il le reproduire ? Pourquoi cela se produit-il et existe-t-il un moyen de le contourner ?

Edit : Définir la BorderThickness de la GroupBox à 0 résout le problème, mais il est évident que cela supprime la bordure, ce qui fait que la GroupBox ne ressemble plus à une GroupBox.

24voto

Ray Points 22127

La réponse de Ian Oakes modifie l'ordre des tabulations de telle sorte que l'en-tête vienne après le contenu. Il est possible de modifier le modèle de contrôle de manière à ce que la bordure ne puisse pas recevoir le focus.

Pour ce faire, modifiez le modèle de manière à ce que les 2e et 3e bordures (toutes deux dans la rangée 1 de la grille) aient les caractéristiques suivantes IsHitTestVisible=false

Compléter le modèle ci-dessous

<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>

19voto

Ian Oakes Points 5796

Il semble qu'il s'agisse d'un bug subtil dans le modèle de contrôle de la GroupBox. J'ai découvert qu'en modifiant le modèle par défaut de la GroupBox et en déplaçant la bordure nommée "Header" vers le dernier élément de l'élément Grid du modèle de contrôle, le problème se résout de lui-même.

La raison en est que l'un des autres éléments Border avec un TemplateBinding de BorderBrush se trouvait plus bas dans l'arbre visuel et captait le clic de la souris, c'est pourquoi la définition de BorderBrush à null a permis à la CheckBox de recevoir correctement le clic de la souris.

Vous trouverez ci-dessous le style résultant pour la GroupBox. Il est presque identique au modèle par défaut du contrôle, à l'exception de l'élément Border nommé 'Header', qui est maintenant le dernier enfant de la grille, au lieu du deuxième.

<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>

11voto

Rune Andersen Points 1314

Une autre solution que j'ai trouvée consiste à implémenter OnApplyTemplate dans une GroupBox dérivée :

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;
    }
  }
}

4voto

rudigrobler Points 8883

Si vous modifiez le BorderBrush de la GroupBox, cela fonctionne !

<GroupBox BorderBrush="{x:Null}">

Je sais que cela va à l'encontre de l'objectif, mais cela prouve où se situe le problème !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X