2008-09-30 3 views
41

Jetez un oeil à ce programme par exemple WPF très simple:L'en-tête GroupBox de WPF avale-t-il les clics de souris?

<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 la tête est un CheckBox. Nous avons tous fait quelque chose comme ça - typiquement, vous liez le contenu de GroupBox de telle sorte qu'il est désactivé lorsque le CheckBox est décoché.

Cependant, lorsque j'exécute cette application et que je clique sur le CheckBox, j'ai constaté que parfois mes clics de souris sont avalés et que le statut du CheckBox ne change pas. Si j'ai raison, c'est quand je clique sur la rangée exacte de pixels sur laquelle se trouve la bordure supérieure de la GroupBox.

Quelqu'un peut-il reproduire cela? Pourquoi cela se produirait-il, et y a-t-il un moyen de contourner le problème? Editer: Le réglage de BorderThickness de GroupBox à 0 résout le problème, mais évidemment il supprime la bordure, de sorte qu'il ne ressemble plus à un GroupBox.

+4

Votons une solution pour ce bug: https : //connect.microsoft.com/VisualStudio/feedback/details/539427/any-control-placed-in-groupboxs-header-will-be-not-complete-accessable-click-hover-etc – codekaizen

+0

Merci beaucoup pour poster ceci! Maintenant, je sais que c'est un bug. –

+2

Correction dans .NET 4.5. Vous pouvez installer .NET 4.5 pour corriger votre application 4.0 aussi. –

Répondre

18

Il semble s'agir d'un bogue subtil dans le modèle de contrôle de GroupBox. J'ai trouvé en éditant le modèle par défaut pour le GroupBox et en déplaçant la bordure nommée 'Header' au dernier élément dans l'élément Grid des modèles de contrôle, le problème se résout lui-même.

La raison 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 souris, c'est pourquoi définir BorderBrush sur null a permis au CheckBox de recevoir correctement le clic de souris .

Voici le style résultant pour 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, et non 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> 
+0

Le ControlTemplate modifié est-il très complexe, Ian? Vous voulez le poster? –

+0

Merci Ian! J'ai dû ajouter une ressource BorderGapConverter en dehors de ce style pour que cela fonctionne, mais une fois que je l'ai fait, ça a bien fonctionné. –

+0

Attention à la commande de tabulation avec ceci. Le contenu sera maintenant avant l'en-tête. Voir ma réponse pour une réponse légèrement différente (il s'agit toujours de modifier le modèle de contrôle). – Ray

4

Si vous modifiez BorderBrush de la GroupBox, cela fonctionne! Je sais que cela va à l'encontre de l'objectif, mais cela prouve où se situe le problème!

+0

Cela pourrait aider de toute façon. Nous pourrions peut-être mettre une "vraie" Border derrière le GroupBox en quelque sorte pour le truquer. Je vais attendre pour voir si nous aurons d'autres réponses, mais cela pourrait être aussi proche que possible d'une réponse acceptée. –

+0

J'ai également posé des questions à ce sujet sur le groupe WPF Disciples: http://groups.google.com/group/wpf-disciples/browse_thread/thread/c21a1ecd6c843f75 – rudigrobler

+1

Rudi - nous avons essayé cela en vain. Si vous frappez le pixel où la bordure * serait *, l'état de la case à cocher ne change pas. Même définir BorderBrush sur Transparent n'aide pas. –

11

Une solution de rechange que je fait est la mise en œuvre 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; 
    } 
    } 
} 
+0

La bonne chose à propos de ma solution est que vous ne modifiez pas l'apparence de la zone de groupe, donc le thème par défaut ne sera pas affecté. En outre, il est très compact. –

+1

J'utilise WPF 4.0, et il semble que GroupBox.Children n'existe pas. Quel devrait être le code pour WPF 4? Devrait-il être if (VisualChildrenCount == 0)? – Mas

+0

+1, excellente solution, ne peut pas être plus simple! @Mas: Vous pouvez le remplacer par 'VisualChildrenCount == 0' mais je pense que tout le contrôle n'est pas nécessaire (il y a toujours un enfant visuel). En fait, je pense que toutes les vérifications ne sont pas nécessaires (plus l'appel 'base') donc tout le corps de la méthode pourrait simplement être une ligne:' ((Grid) GetVisualChild (0)). Children [3] .IsHitTestVisible = false; '. C'est ce que j'utilise et jusqu'à présent cela fonctionne. –

23

réponse Ian Oakes enfourne l'ordre de tabulation de telle sorte que l'en-tête vient 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, modifier le modèle afin que les 2e et 3e frontières (à la fois dans la grille Row 1) ont IsHitTestVisible=false

modèle complet 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> 
Questions connexes