Archive for fevereiro \22\UTC 2012

ListBox com Header no WPF

fevereiro 22, 2012

Há algum tempo precisei usar ListBox em uma aplicação WPF, porém precisava que o mesmo tivesse um Header, então decidi criar um estilo alterando a Template do ListBox para que o mesmo passasse a ter um Header.
Para começar criei uma classe chamada MeuListBox herdando da classe ListBox, depois registrei uma DependencyProperty chamada Header:


public class MeuListBox : ListBox
{
    public FrameworkElement Header
    {
        get { return (FrameworkElement)GetValue(HeaderProperty); }
        set { SetValue(HeaderProperty, value); }
    }

    public static readonly DependencyProperty HeaderProperty =
        DependencyProperty.Register("Header", typeof(FrameworkElement), typeof(Grid));
}

Feito isso, criei um estilo para que pudesse alterar a Template do ListBox:

<Style x:Key="ListBoxComHeader"
       TargetType="{x:Type ctl:MeuListBox}">
    <Setter Property="HorizontalContentAlignment"
            Value="Stretch" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ctl:MeuListBox}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Border Grid.Column="0"
                            Width="{Binding ElementName=Items, Path=ActualWidth}">
                        <ContentPresenter Margin="2,0,0,0"
                                          Content="{Binding Path=Header, RelativeSource={RelativeSource TemplatedParent}}" />
                    </Border>
                    <ScrollViewer Grid.Row="1"
                                  CanContentScroll="False"
                                  Name="scroll"
                                  Focusable="false"
                                  Padding="{TemplateBinding Padding}">
                        <ItemsPresenter Name="Items"
                                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                    </ScrollViewer>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Como podemos ver no código acima, coloquei um Grid, esse Grid possui duas linhas, na primeira linha coloquei uma borda definindo a largura de acordo com a propriedade ActualWidth do controle Items (ItemsPresenter – itens do ListBox) e, dentro dessa, um ContentPresenter, sendo que seu conteúdo está fazendo Binding com a propriedade Header (DependencyProperty) que criei na minha classe MeuListBox. Nesse ContentPresenter defini a margem como 2,0,0,0 para que os itens ficassem alinhados ao Header.
Já na segunda linha, temos um ScrollViewer e dentro dele temos o ItemsPresenter.
Depois disso feito, devemos agora aplicar o estilo no nosso ListBox e definir a propriedade Header:

<ctl:MeuListBox x:Name="listBox1"
                Margin="5"
                Style="{StaticResource ListBoxComHeader}">
    <ctl:MeuListBox.Header>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="100" />
                <ColumnDefinition Width="300" />
                <ColumnDefinition Width="100" />
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0"
                       Text="Nome"
                       FontWeight="Bold"
                       HorizontalAlignment="Left"
                       Foreground="Black" />
            <TextBlock Grid.Column="1"
                       Text="Idade"
                       FontWeight="Bold"
                       HorizontalAlignment="Center"
                       Foreground="Black" />
            <TextBlock Grid.Column="2"
                       Text="Cidade"
                       FontWeight="Bold"
                       HorizontalAlignment="Center"
                       Foreground="Black" />
            <TextBlock Grid.Column="3"
                       Text="Estado"
                       FontWeight="Bold"
                       HorizontalAlignment="Center"
                       Foreground="Black" />
        </Grid>
    </ctl:MeuListBox.Header>
    <ctl:MeuListBox.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="100" />
                    <ColumnDefinition Width="300" />
                    <ColumnDefinition Width="100" />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0"
                           Text="{Binding Nome}"
                           HorizontalAlignment="Left"
                           Foreground="Black" />
                <TextBlock Grid.Column="1"
                           Text="{Binding Idade}"
                           HorizontalAlignment="Center"
                           Foreground="Black" />
                <TextBlock Grid.Column="2"
                           Text="{Binding Cidade}"
                           HorizontalAlignment="Center"
                           Foreground="Black" />
                <TextBlock Grid.Column="3"
                           Text="{Binding Estado}"
                           HorizontalAlignment="Center"
                           Foreground="Black" />
            </Grid>
        </DataTemplate>
    </ctl:MeuListBox.ItemTemplate>
</ctl:MeuListBox>

Se observarmos, no Header temos um Grid contendo quatro colunas, e claro, no ItemTemplate também temos um Grid com as mesmas quatro colunas, ou seja, a estrutura do Header deve ser exatamente a mesma do ItemTemplate para que as colunas fiquem alinhadas.

Abaixo, podemos ver o resultado final:

 

Como pudemos ver no exemplo acima, para que todos os nossos ListBoxes tenham Header basta definirmos o estilo e também a propriedade Header.

Como no Header podemos colocar qualquer controle, podemos colocar botões, checkbox, etc.

Abaixo, segue projeto, o mesmo está nomeado como ListBoxSample.doc, favor renomear para .zip, pois não consigo anexar arquivos .zip no WordPress.
ListBox Sample