Question

Découvrez cet exemple très simple de programme 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>

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

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

Quelqu'un peut-il dupliquer cela? Pourquoi cela se produirait-il et y at-il un moyen de le contourner?

Éditer: définir le paramètre BorderThickness de GroupBox sur 0 résout le problème, mais de toute évidence, il supprime la bordure. Ainsi, il ne ressemble plus à un GroupBox.

Était-ce utile?

La solution

Il semble s'agir d'un bogue subtil dans le modèle de contrôle de la GroupBox. J'ai trouvé en modifiant le modèle par défaut de GroupBox et en déplaçant la bordure nommée "En-tête" vers le dernier élément de l'élément Grille des modèles 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 était plus bas dans l'arborescence visuelle et capturait le clic de la souris, c'est pourquoi la définition de BorderBrush sur null a permis à la case à cocher 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 plutôt que le second.

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

Autres conseils

La réponse de Ian Oakes complète l'ordre de tabulation pour que l'en-tête vienne après le contenu. Il est possible de modifier le modèle de contrôle de sorte que la bordure ne puisse pas recevoir le focus.

Pour ce faire, modifiez le modèle de sorte que les deuxième et troisième bordures (toutes les deux de la ligne de grille 1) aient IsHitTestVisible = false

Complétez 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>

Une solution alternative que j'ai créée implémente OnApplyTemplate dans un GroupBox dérivé:

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

Si vous modifiez le BorderBrush de 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!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top