Freitag, 4. Februar 2011

Button in GroupBox.Header: wrong element-order in default Style

I had a request to implement a GroupBox with a Button in GroupBox.Header like this:

  <GroupBox>
    <GroupBox.Header>
      <Button Height="50">Some Button</Button>
    </GroupBox.Header>
  </GroupBox>

That's not quite a hard task, but what happend during testing was following:

When trying to click this Button with the Mouse, or just trying to move the Mouse over the Button (what starts the "Glowing-Effect"), there are some regions in that Button which cannot be clicked (or which don't start the "Glowing-Effect").

After some trying it was easy to figure out, that those regions are exactly where the "unvisible" Border(s) of the GroupBox are running through it.

I manipulated the Style of the GroupBox a bit to make it visible:


The corresponding Style looks like this (a copy of the original one with some changes for BorderBrush and BorderThickness):

      <Style 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.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 Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4" Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="1" Grid.RowSpan="3"/>
                <Border x:Name="Header" Padding="3,1,3,0" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2">
                  <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header" RecognizesAccessKey="True"/>
                </Border>
                <ContentPresenter Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="2"/>
                <Border BorderBrush="Red" BorderThickness="5" CornerRadius="4" Grid.ColumnSpan="4" Grid.Row="1" Grid.RowSpan="3">
                  <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="Green" BorderThickness="5" CornerRadius="3">
                    <Border BorderBrush="Blue" BorderThickness="5" CornerRadius="2"/>
                  </Border>
                </Border>
              </Grid>
            </ControlTemplate>
          </Setter.Value>
        </Setter>
      </Style>

Next I removed the property OpacityMask:


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

That showed which regions aren't clickable and where the problem comes from:
By knowing this it's very easy to get the Button to work properly: Just change the order of the elements in the Style:


  <GroupBox>
    <GroupBox.Style>
      <Style 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.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 Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4" Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="1" Grid.RowSpan="3"/>
                <!--<Border x:Name="Header" Padding="3,1,3,0" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2">
                  <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header" RecognizesAccessKey="True"/>
                </Border>-->
                <ContentPresenter Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="2"/>
                <Border BorderBrush="Red" BorderThickness="5" CornerRadius="4" Grid.ColumnSpan="4" Grid.Row="1" Grid.RowSpan="3">
                  <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="Green" BorderThickness="5" CornerRadius="3">
                    <Border BorderBrush="Blue" BorderThickness="5" CornerRadius="2"/>
                  </Border>
                </Border>
                <Border x:Name="Header" Padding="3,1,3,0" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2">
                  <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header" RecognizesAccessKey="True"/>
                </Border>
              </Grid>
            </ControlTemplate>
          </Setter.Value>
        </Setter>
      </Style>
    </GroupBox.Style>
    <GroupBox.Header>
      <Button Height="50">Some Button</Button>
    </GroupBox.Header>
  </GroupBox>

Now the Button is drawn after the Border(s) and it works just fine :-)

Keine Kommentare:

Kommentar veröffentlichen