Аккордеон


Недавно мне пришла в голову мысль, что такой контрол, как Аккордеон(Accordion) в своём классическом варианте с одним активным pane-ом, функционально является полным аналогом TabControl-а, просто в другом визуальном оформлении. И я решил реализовать стиль для TabControl-а, который бы превращал его в аккордеон.
Аккордеон

Итак, сначала я создал в главном окне TabControl с несколькими табами содержащими разноветные Rectangle-ы:
<TabControl Width="Auto" Height="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
  <TabItem Header="1">
    <Rectangle Height="150" Fill="Red"/>
  </TabItem>
  <TabItem Header="2">
    <Rectangle Height="150" Fill="Orange"/>
  </TabItem>
  <TabItem Header="3">
    <Rectangle Height="150" Fill="Yellow"/>
  </TabItem>
  <TabItem Header="4">
    <Rectangle Height="150" Fill="Green"/>
  </TabItem>
  <TabItem Header="5">
    <Rectangle Height="150" Fill="LightBlue"/>
  </TabItem>
  <TabItem Header="6">
    <Rectangle Height="150" Fill="Blue"/>
  </TabItem>
  <TabItem Header="7">
    <Rectangle Height="150" Fill="Purple"/>
  </TabItem>
</TabControl>
Теперь я создам стиль TabControl-а:
<Style TargetType="TabControl">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="TabControl">
        <StackPanel IsItemsHost="True" Orientation="Vertical"/>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
Этот стиль представляет собой простую вертикальную StackPanel, которая будет являться ItemsHost-ом для содержимого аккордеона. Для большей универсальности стиля можно добавить изменение ориентации StackPanel-и используя, например, свойство TabStripPlacement у TabControl-а, но я не буду этого делать, чтобы не усложнять пример.
После я создал стиль для TabItem-ов:
<Style TargetType="TabItem">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="TabItem">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition Height="25"/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Border Margin="0,1,0,0" x:Name="outterHeaderBorder" BorderThickness="1" CornerRadius="2" BorderBrush="#AACCCCCC">
            <Border x:Name="innerHeaderBorder" BorderThickness="1" CornerRadius="1">
              <ContentPresenter ContentSource="Header" Margin="2"/>
            </Border>
          </Border>
          <Border x:Name="contentBorder" Visibility="Collapsed" Grid.Row="1" BorderBrush="#FFA0A0A0" BorderThickness="1,0,1,1" CornerRadius="0,0,2,2">
            <ContentPresenter ContentSource="Content" Margin="0"/>
          </Border>
        </Grid>
        <ControlTemplate.Triggers>              
          <Trigger Property="IsSelected" Value="True">
            <Setter Property="Visibility" Value="Visible" TargetName="contentBorder"/>
            <Setter Property="Background" TargetName="innerHeaderBorder">
              <Setter.Value>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                  <GradientStop Color="#FFCACACA" Offset="0"/>
                  <GradientStop Color="#FFEEEEEE" Offset="0.352"/>
                  <GradientStop Color="#FFC1C1C1" Offset="1"/>
                </LinearGradientBrush>
              </Setter.Value>
            </Setter>
            <Setter Property="BorderBrush" TargetName="innerHeaderBorder" Value="#FFDDDADA"/>
            <Setter Property="BorderBrush" TargetName="outterHeaderBorder" Value="#FFA0A0A0"/>
            <Setter Property="CornerRadius" TargetName="outterHeaderBorder" Value="2,2,0,0"/>
          </Trigger>
          <Trigger Property="IsMouseOver" Value="True" SourceName="outterHeaderBorder">
            <Setter Property="Background" TargetName="innerHeaderBorder">
              <Setter.Value>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                  <GradientStop Color="#FFF4F4F4" Offset="0"/>
                  <GradientStop Color="White" Offset="0.352"/>
                  <GradientStop Color="#FFCFCFCF" Offset="1"/>
                </LinearGradientBrush>
              </Setter.Value>
            </Setter>
            <Setter Property="BorderBrush" TargetName="innerHeaderBorder" Value="Gainsboro"/>
            <Setter Property="BorderBrush" TargetName="outterHeaderBorder" Value="#FFB7B7B7"/>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
Этот стиль представляет собой Grid с двумя строками: в верхней находится заголовок, а в нижней - содержимое pane-а. В невыделенном состоянии содержимое pane-а не отображается, а при выделении появляется, с помощью триггера на IsSelected. Так же сделан триггер на IsMouseOver для большей визуальной привлекательности стиля.
Таким образом стилизуя TabControl я смог получить аккордеон, не написав не строчки кода. Конечно такой аккордеон по функциональности уступает тому, что есть в WPF Toolkit-е, но если вам нужен простой аккордеон с одним активным pane-ом, то стилизированный TabControl будет вполне эффективным решением.

Скачать исходники:

КОММЕНТАРИИ


НОВЫЙ КОММЕНТАРИЙ


*жирный*
_курсив_
+подчеркнутый+
! заголовок 1
!! заголовок 2
* список
** список 2
# нумерованый список
## нумерованый список 2
[url:http://www.example.com]
{"без форматирования"}
Полное описание синтаксиса