سؤال

Hi I have been trying to override the Expander control in WPF to use a dividing line that sits "underneath" the header.

I have found an example of how to get an Expander to look like a group box, but I do not want the border to appear all the way around, only on the top side of the border.

The problem I face is that the border uses an opacity mask to deal with the border sitting under the header, but when I set the border thickness to 0,1,0,0 the opacity mask seems to fail. Setting any other border to 1+ seems to get it working again (1,1,0,0) but I am confused as to why this should make any difference and if there is any other way to achieve the desired result.

My existing XAML is as follows

        <ControlTemplate TargetType="{x:Type Expander}">
            <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 CornerRadius="4" Grid.Row="1" Grid.RowSpan="3" Grid.Column="0" Grid.ColumnSpan="4" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="Transparent" Background="{TemplateBinding Background}" />
                <Border x:Name="Header" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" Padding="3,0,3,0">
                    <Grid SnapsToDevicePixels="False" Background="Transparent" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <ToggleButton Grid.Column="0" MinHeight="0" MinWidth="0"
                            Name="HeaderToggle"
                            IsChecked="{Binding Path=IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" >
                            <ToggleButton.Template>
                                <ControlTemplate TargetType="{x:Type ToggleButton}">
                                    <Grid SnapsToDevicePixels="False" Background="Transparent">
                                        <Ellipse HorizontalAlignment="Center" x:Name="circle" VerticalAlignment="Center" Width="15" Height="15" Fill="{DynamicResource ButtonNormalBackgroundFill}" Stroke="DarkGray"/>
                                        <Ellipse Visibility="Hidden" HorizontalAlignment="Center" x:Name="shadow" VerticalAlignment="Center" Width="13" Height="13" Fill="{DynamicResource ExpanderShadowFill}"/>
                                        <Path SnapsToDevicePixels="false" x:Name="arrow" VerticalAlignment="Center" HorizontalAlignment="Center" Stroke="#666" StrokeThickness="2" Data="M1,1 L4,4 7,1" />
                                    </Grid>

                                    <ControlTemplate.Triggers>
                                        <Trigger Property="IsChecked" Value="true">
                                            <Setter Property="Data" TargetName="arrow" Value="M 1,4  L 4,1  L 7,4"/>
                                        </Trigger>
                                        <Trigger Property="IsMouseOver" Value="true">
                                            <Setter Property="Stroke" TargetName="circle" Value="#666"/>
                                            <Setter Property="Stroke" TargetName="arrow" Value="#222"/>
                                            <Setter Property="Visibility" TargetName="shadow" Value="Visible"/>
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </ToggleButton.Template>
                        </ToggleButton>
                        <ContentPresenter ContentSource="Header" RecognizesAccessKey="true"
                            TextElement.Foreground="{StaticResource GroupBoxHeaderBrush}"
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="3,0,0,0" />
                    </Grid>
                </Border>

                <ContentPresenter x:Name="ExpandSite" Visibility="Collapsed" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />


                <Border Grid.Row="1" Grid.ColumnSpan="4" BorderThickness="0,1,0,0" BorderBrush="{TemplateBinding BorderBrush}" >
                    <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>

            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="IsExpanded" Value="true">
                    <Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
                </Trigger>
                <Trigger Property="IsEnabled" Value="false">
                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                </Trigger>

            </ControlTemplate.Triggers>
        </ControlTemplate>

EDIT: By underneath I actually mean behind

Ok so I've photo-shopped an example of what I want to see visually, and now with the added complexity of a button in the far right corner.

example screenshot

I am happy to create a complicated mask to deal with additional controls but I just don't know how and I'm sure I dont actually need a border as just a single line would do.

Any help would be gratefully appreciated and please remember I want the background of the togglebutton and the header text to be TRANSPARENT so as to replicate the background of the parent control.

Thanks

هل كانت مفيدة؟

المحلول

Update:

Ok I could get your screenshot translated to something like

enter image description here

Xaml:

<ControlTemplate TargetType="{x:Type Expander}">
  <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="*" />
    </Grid.RowDefinitions>
    <Rectangle Grid.Row="0"
                Grid.Column="0"
                Height="1"
                Margin="0 4 0 0"
                VerticalAlignment="Center"
                Fill="{TemplateBinding BorderBrush}" />
    <Rectangle Grid.Row="0"
                Grid.Column="2"
                Grid.ColumnSpan="2"
                Height="1"
                Margin="0 4 0 0"
                VerticalAlignment="Center"
                Fill="{TemplateBinding BorderBrush}" />
    <Border x:Name="Header"
            Grid.Row="0"
            Grid.Column="1"
            Padding="3,0,3,0">
      <Grid Background="Transparent"
            SnapsToDevicePixels="False">
        <Grid.ColumnDefinitions>
          <ColumnDefinition />
          <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ToggleButton Name="HeaderToggle"
                      Grid.Column="0"
                      MinWidth="0"
                      MinHeight="0"
                      IsChecked="{Binding Path=IsExpanded,
                                          Mode=TwoWay,
                                          RelativeSource={RelativeSource TemplatedParent}}">
          <ToggleButton.Template>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
              <Grid Background="Transparent"
                    SnapsToDevicePixels="False">
                <Ellipse x:Name="circle"
                          Width="15"
                          Height="15"
                          HorizontalAlignment="Center"
                          VerticalAlignment="Center"
                          Fill="{DynamicResource ButtonNormalBackgroundFill}"
                          Stroke="DarkGray" />
                <Ellipse x:Name="shadow"
                          Width="13"
                          Height="13"
                          HorizontalAlignment="Center"
                          VerticalAlignment="Center"
                          Fill="{DynamicResource ExpanderShadowFill}"
                          Visibility="Hidden" />
                <Path x:Name="arrow"
                      HorizontalAlignment="Center"
                      VerticalAlignment="Center"
                      Data="M1,1 L4,4 7,1"
                      SnapsToDevicePixels="false"
                      Stroke="#666"
                      StrokeThickness="2" />
              </Grid>
              <ControlTemplate.Triggers>
                <Trigger Property="IsChecked"
                          Value="true">
                  <Setter TargetName="arrow"
                          Property="Data"
                          Value="M 1,4  L 4,1  L 7,4" />
                </Trigger>
                <Trigger Property="IsMouseOver"
                          Value="true">
                  <Setter TargetName="circle"
                          Property="Stroke"
                          Value="#666" />
                  <Setter TargetName="arrow"
                          Property="Stroke"
                          Value="#222" />
                  <Setter TargetName="shadow"
                          Property="Visibility"
                          Value="Visible" />
                </Trigger>
              </ControlTemplate.Triggers>
            </ControlTemplate>
          </ToggleButton.Template>
        </ToggleButton>
        <ContentPresenter Grid.Column="1"
                          Margin="3,0,0,0"
                          HorizontalAlignment="Left"
                          VerticalAlignment="Center"
                          ContentSource="Header"
                          RecognizesAccessKey="true"
                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                          TextElement.Foreground="Black" />
      </Grid>
    </Border>
    <ContentPresenter x:Name="ExpandSite"
                      Grid.Row="1"
                      Grid.Column="1"
                      Grid.ColumnSpan="2"
                      Margin="{TemplateBinding Padding}"
                      SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                      Visibility="Collapsed" />
  </Grid>
  <ControlTemplate.Triggers>
    <Trigger Property="IsExpanded"
              Value="true">
      <Setter TargetName="ExpandSite"
              Property="Visibility"
              Value="Visible" />
    </Trigger>
    <Trigger Property="IsEnabled"
              Value="false">
      <Setter Property="Foreground"
              Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
    </Trigger>
  </ControlTemplate.Triggers>
</ControlTemplate>

I also did remove a few Grid Rows that were not being used and removed some Grid Attached properties that were being set of items that were not even children of Grid.

The main change is pretty much remove the Border and OpacityMask and just render Rectangle's with a Height of 1 in the appropriate Grid Column. This way you are actually not rendering anything behind the header at all.

You can extend this concept ofc when you add the Button to the right. Just put it in it's own Grid Column and don't render a Rectangle in that Column

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top