WPF - Apresentando o controle Calendar


Quando o Windows Presentation Foundation - WPF foi lançado a comparação com o Windows Forms era inevitável, e, logo foi sentida a falta de controles para calendário, afinal o Windows Forms tinha os controles MonthCalendar e DateTimePicker e o WPF ???

Mas no início de 2009 (já faz um bom tempo) foram lançados os controles Calendar e DatePicker no WPF Toolkit.

Desse modo não há mais o que reclamar e neste artigo eu vou mostrar as propriedades e recursos do controle Calendar e DatePicker.

As classes Calendar e DatePicker estão no namespace Microsoft.Windows.Controls além disso ainda nesse namespace encontramos as classes subsidiárias como CalendarItem, CalendarButton, CalendarDayButton, e DatePickerTextBox, e como , esse namespace não é o padrão para controles WPF você terá que incluí-lo em seu projeto para usar os recursos dos novos controles.

Esses controles incluem muitas características que ajudam a criar uma experiência rica como usuário final para visualização e seleção de datas que são sempre exigidas em aplicações comerciais. Dessa forma Datas não-válidas (blackout dates), seleção de múltiplas datas, navegação pelo teclado, dentre outras, são recursos que tornam a aplicação mais fácil de usar e interagir.

Vamos então mostrar alguns recursos do controle Calendar criando uma aplicação WPF no Visual Basic 2010 Expess Edition.

Abra o VB 2010 Express Edition e no menu File selecione New Project e a seguir selecione o template WPF Application informando o nome WPF_Calendar e clicando em OK;

A forma mais simples de usar o controle Calendar e selecioná-lo a partir da ToolBox e arrastá-lo em uma janela MainWindow.xaml conforme mostrado na figura abaixo onde vemos a exibição do mês atual para o ano corrente;

A seguir vemos a declaração XAML criada :

<Calendar Height="170" HorizontalAlignment="Left" Margin="121,68,0,0" Name="Calendar1" VerticalAlignment="Top" Width="180" />

Diferentemente do controle MonthCalendar do Windows Forms o controle Calendar exibe somente um único mês.

Cada mês exibe seis semanas e ao clicar no cabeçalho do mês o modo de exibição será alterado para exibir os meses do ano:

Para retornar ao modo de exibição anterior basta clicar no mês desejado.

Se você clicar novamente no cabeçalho do controle o modo de exibição será alterado novamente para exibir os anos:

Para retornar ao modo anterior basta clicar no ano desejado.

Vejamos a seguir alguns como tratar e usar alguns recursos deste controle:

1- Modos de exibição

A propriedade DisplayMode da classe Calendar representa o formato de exibição do calendário que pode ser:

DisplayMode="Month" DisplayMode="Year" DisplayMode="Decade"

2- Modos de seleção e seleção de datas

A propriedade SelectedDate representa a data atual selecionada e se múltiplas datas estiverem selecionadas esta propriedade representará uma coleção das datas selecionadas: SelectedDates

Para definir o modo de seleção de datas usamos o SelectionMode da enumeração CalendarSelectionMode. A tabela a seguir descreve os valores possíveis para enumeração:

SelectionMode

Descrição

None

Nenhuma seleção é permitida

SingleDate

Somente uma única data pode ser selecionada ou pela definição de SelectedDate ou pelo primeiro valor de SelectedDates.

SingleRange

Um único intervalo de datas contínuo pode ser selecionado. Definindo SelectedDate, incluindo uma data individualmente em SelectedDates ou usando AddRange.

MultipleRange

Permite a seleção de múltiplos intervalos de dados não contínuos.

- Adicionando uma data para SelectedDates individualmente ou através de AddRange não vai limpar SelectedDates.
- Definindo SelectedDate vai limpar SelectedDates, mas datas adicionais ou um intervalo de datas pode ser adicionado.
- Adicionando um intervalo que inclui algumas datas que já estão selecionados ou sobreposições com outra faixa de de datas resulta na união dos intervalos.

 No exemplo a seguir temos a exibição do controle com múltiplas datas selecionadas. A seleção é feita pressionando a tecla CTR e o dia desejado:

O código que define o modo de seleção no XAML é visto abaixo:

<Calendar Height="170" HorizontalAlignment="Left" Margin="121,68,0,0" Name="Calendar1" VerticalAlignment="Top" Width="180" SelectionMode="MultipleRange">
</
Calendar>

 

Podemos definir as datas selecionadas via código XAML da seguinte forma:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="324" Width="407">
    <Grid>
        <StackPanel>
            <Button Content="Definir Datas Inválidas" Height="27" Name="Button1" Width="159" />
            <Calendar Height="170" HorizontalAlignment="Left" Margin="114,72,0,0" 
            Name="Calendar1" VerticalAlignment="Top" Width="195" 
                 SelectionMode="MultipleRange"
                 xmlns:sys="clr-namespace:System;assembly=mscorlib">
                <Calendar.SelectedDates>
                      <sys:DateTime>2/5/2011</sys:DateTime>
                      <sys:DateTime>2/15/2011</sys:DateTime>
                      <sys:DateTime>2/25/2011</sys:DateTime>
                </Calendar.SelectedDates>
            </Calendar>
        </StackPanel>
    </Grid>
</Window>

 

Executando o projeto iremos obter:

3- Datas Inválidas (BlockoutDates)

A propriedade BlackoutDates da classe Calendar representa uma coleção de datas que não estão disponíveis para a seleção. Todas as datas de seleção não válidas estão marcadas por uma cruz.

Para definir as datas inválidas ou não selecionáveis usando código XAML usamos a classe CalendarDataRange que define um intervalo de datas conforme o código abaixo:

Obs: Observe que a data esta no formato mm/dd/yyyy.

Podemos obter o mesmo resultado via código. Vamos incluir um controle Button no StackPanel e definir o seu conteúdo como Definir Datas Inválidas, conforme mostra o código XAML abaixo:

A seguir no arquivo code-behind MainWindow.xaml.vb vamos definir no evento Click do botão o seguinte código:

 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click


        Calendar1.BlackoutDates.Add(New CalendarDateRange(

           New DateTime(2011, 2, 1),

           New DateTime(2011, 2, 4)

          ))

        Calendar1.BlackoutDates.Add(New CalendarDateRange(

            New DateTime(2011, 2, 18),

            New DateTime(2011, 2, 21)

            ))
 

        Calendar1.BlackoutDates.Add(New CalendarDateRange(

           New DateTime(2011, 2, 28),

           New DateTime(2011, 2, 28)

          ))

 End Sub

O código acima usa o método BlackoutDates.Add que toma um objeto CalendarDateRange o qual é uma coleção de objetos DateTime e definem a data inicial e data final do intervalo das datas inválidas:

Executando o projeto e clicando no botão iremos obter:

4- Datas de início de fim e de exibição do calendário

O controle Calendar permite que você defina as datas de início e fim de exibição usando as propriedades DisplayDateStart e DisplayDateEnd. Podemos assim usar as propriedades DisplayStartDate e DisplayEndDate para controlar as datas de início e fim de um mês que desejamos exibir.

Para exibir uma data desejada usamos a propriedade DisplayDate que representa a data a ser exibida.

No código XAML abaixo vemos como definir as datas de início de fim e exibir uma data diferente da data atual do calendário:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="324" Width="407">
    <Grid>
        <StackPanel>
            <Button Content="Definir Datas Inválidas" Height="27" Name="Button1" Width="159" />
            <Calendar Height="170" HorizontalAlignment="Left" Margin="114,72,0,0" 
            Name="Calendar1" VerticalAlignment="Top" Width="195" 
                 SelectionMode="MultipleRange"
                 DisplayDate="2/5/2011"
                 DisplayDateStart="2/1/2011"
                 DisplayDateEnd="2/28/2011">
            </Calendar>
        </StackPanel>
    </Grid>
</Window>

 

Executando o projeto iremos obter:

Observe que agora temos a exibição apenas do dia 1 ao dia 28 de fevereiro no calendário. Para obter o mesmo resultado via código basta definir no code-behind do arquivo MainWindow.xaml.vb o seguinte código:

Calendar1.DisplayDate = new DateTime(2011, 2, 5)
Calendar1.DisplayDateStart = new DateTime(2011, 2, 1)
Calendar1.DisplayDateEnd = new DateTime(2011, 2, 28)

5- Definindo o primeiro dia da semana e destacando a data atual

Por padrão Domingo (Sunday) é o primeiro dia da semana e para alterar este comportamento basta definir outro dia na propriedade FirstDayOfWeek.

Para não destacar data atual basta definir a propriedade IsTodayHightLighted como false.(O padrão é true)

No código XAML abaixo estamos definindo o primeiro dia semana como sendo a segunda-feira(Monday) e não destacando o dia atual:

<Window x:Class="MainWindow"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="MainWindow" Height="324" Width="407">

    <Grid>

        <StackPanel>

            <Button Content="Definir Datas Inválidas" Height="27" Name="Button1" Width="159" />

            <Calendar Height="170" HorizontalAlignment="Left" Margin="114,72,0,0"

            Name="Calendar1" VerticalAlignment="Top" Width="195"

                 SelectionMode="MultipleRange"

                 DisplayDateStart="2/1/2011"

                 DisplayDateEnd="2/28/2011"

                 DisplayDate="2/10/2011"

                 FirstDayOfWeek="Monday"

                 IsTodayHighlighted="false">

            </Calendar>

        </StackPanel>

    </Grid>

</Window>

 

O resultado obtido é o seguinte:

O mesmo resultado pode ser obtido via código da seguinte forma:

Calendar1.FirstDayOfWeek = DayOfWeek.Monday;
Calendar1.IsTodayHighlighted = false;

6 - Formatando um calendário

A propriedade BorderBrush do calendário define um pincel para desenhar a borda de um calendário. Você pode usar qualquer pincel para preencher a borda. O trecho de código a seguir usa um pincel de gradiente linear para desenhar a borda com uma combinação de cores vermelho e azul.

Além disso podemos usar as propriedades BackGround e Foreground do calendário para definir as cores de fundo e de frente usando um pincel para preencher a borda.

No código XAML abaixo vemos a aplicação dessas propriedades:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="324" Width="407">
    <Grid>
        <StackPanel>
            <Button Content="Definir Datas Inválidas" Height="27" Name="Button1" Width="159" />
            <Calendar Height="170" HorizontalAlignment="Left" Margin="114,72,0,0" 
            Name="Calendar1" VerticalAlignment="Top" Width="195" 
                 SelectionMode="MultipleRange"
                 DisplayDateStart="2/1/2011"
                 DisplayDateEnd="2/28/2011"
                 DisplayDate="2/10/2011"
                 FirstDayOfWeek="Monday"
                 IsTodayHighlighted="false">
                 <Calendar.BorderBrush>
                   <LinearGradientBrush StartPoint="0,0" EndPoint="1,1" >
                    <GradientStop Color="Blue" Offset="0" />
                    <GradientStop Color="Red" Offset="1.0" />
                </LinearGradientBrush>
                </Calendar.BorderBrush>
                <Calendar.Background>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="1,1" >
                        <GradientStop Color="Lavender" Offset="0.1" />
                        <GradientStop Color="Aquamarine" Offset="0.25" />
                        <GradientStop Color="LightGreen" Offset="0.75" />
                        <GradientStop Color="LightBlue" Offset="1.0" />
                    </LinearGradientBrush>
                </Calendar.Background>
                <Calendar.Foreground>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="1,1" >
                        <GradientStop Color="Black" Offset="0.25" />
                        <GradientStop Color="Green" Offset="1.0" />
                    </LinearGradientBrush>
                </Calendar.Foreground>
            </Calendar>
        </StackPanel>
    </Grid>
</Window>

O resultado obtido é o seguinte:

7 - Definindo uma imagem de fundo em um calendário

Para definir uma imagem de fundo em um calendário basta definir uma imagem usando a propriedade Background e definindo a imagem com ImageBrush.

Abaixo vemos o código XAML que define uma imagem de fundo para o nosso calendário. A imagem usada foi incluída no projeto.

<Window x:Class="MainWindow"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="MainWindow" Height="324" Width="407">

    <Grid>

        <StackPanel>

            <Button Content="Definir Datas Inválidas" Height="27" Name="Button1" Width="159" />

            <Calendar Height="170" HorizontalAlignment="Left" Margin="114,72,0,0"

            Name="Calendar1" VerticalAlignment="Top" Width="195"

                 SelectionMode="MultipleRange"

                 DisplayDateStart="2/1/2011"

                 DisplayDateEnd="2/28/2011"

                 DisplayDate="2/10/2011"

                 FirstDayOfWeek="Monday"

                 IsTodayHighlighted="false">

                <Calendar.Background>

                    <ImageBrush ImageSource="Penguins.jpg" Opacity="0.3"/>

                </Calendar.Background>

            </Calendar>

        </StackPanel>

    </Grid>

</Window>

 

O resultado pode ser visto na figura abaixo:

8 - Definindo a linguagem e a direção da exibição

Para definir a linguagem do calendário usamos a propriedade Language. Já direção de exibição do mesmo é definido pela propriedade FlowDirection conforme abaixo:

- FlowDirection: define se o calendário deve ser exibido da esquerda para a direita (padrão) ou da direita para a esquerda;
- Language: define a cultura que deve ser considerada nos textos exibidos no controle Calendar.

No seguinte código XAML estamos definindo a linguagem como Chinês e a direção da direita para a esquerda:

<Window x:Class="MainWindow"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="MainWindow" Height="324" Width="407">

    <Grid>

        <StackPanel>

            <Button Content="Definir Datas Inválidas" Height="27" Name="Button1" Width="159" />

            <Calendar Height="170" HorizontalAlignment="Left" Margin="114,72,0,0"

            Name="Calendar1" VerticalAlignment="Top" Width="195"

                 SelectionMode="MultipleRange"

                 DisplayDateStart="2/1/2011"

                 DisplayDateEnd="2/28/2011"

                 DisplayDate="2/10/2011"

                 FirstDayOfWeek="Monday"

                 IsTodayHighlighted="false"

                 FlowDirection="RightToLeft"

                 Language="zh-HK">

            </Calendar>

        </StackPanel>

    </Grid>

</Window>

 

O resultado pode ser visto a seguir:

9 - Redimensionando o controle Calendar

Percebeu que em todos os exemplos o tamanho do controle Calendar não estava adequado.

Para que seu controle Calendar seja redimensionado para ocupar todo o espaço disponível na célula do Grid, basta colocá-lo dentro de uma Viewbox:

O controle Viewbox é um controle WPF do namespace System.Windows.Controls usado para esticar e dimensionar um determinado controle.

No código XAML a seguir vemos o controle Calendar redimensionado após ser inserido em um controle Viewbox:

10  - Como criar um calendário dinamicamente ?

O código a seguir cria um controle de calendário via código. Primeiro, ele cria um objeto do calendário e define sua DisplayMode e SelectedMode e outras propriedades e, posteriormente, o Calendário é adicionado ao LayoutRoot.

Primeiro vamos definir na aplicação WPF o seguinte leiaute:

Agora no code-behind do arquivo MainWindow.xaml.vb vamos definir o código abaixo no evento Click do botão de comando:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click

        Dim Calendar1 As Calendar = New Calendar

        Calendar1.Name = "Calendar1"

        Calendar1.Width = 400

        Calendar1.Height = 400

        Calendar1.Background = Brushes.LightBlue

        Calendar1.DisplayMode = CalendarMode.Month

        Calendar1.SelectionMode = CalendarSelectionMode.SingleRange

        Calendar1.DisplayDateStart = New DateTime(2011, 2, 1)

        Calendar1.DisplayDateEnd = New DateTime(2011, 2, 28)

        Calendar1.SelectedDates.Add(New DateTime(2011, 2, 5))

        Calendar1.SelectedDates.Add(New DateTime(2011, 2, 15))

        Calendar1.SelectedDates.Add(New DateTime(2011, 2, 25))

        Calendar1.FirstDayOfWeek = DayOfWeek.Monday

        Calendar1.IsTodayHighlighted = True

        Calendar1.DisplayDate = New DateTime(2011, 2, 5)

        Calendar1.DisplayDateStart = New DateTime(2011, 2, 1)

        Calendar1.DisplayDateEnd = New DateTime(2011, 2, 28)

        Grid1.Children.Add(Calendar1)

    End Sub

 

Executando o projeto e clicando no botão de comando iremos obter:

11  - Tratando os eventos do objeto Calendar

Além dos eventos normais de controle o controle Calendar possui 3 eventos de calendário relacionados. São eles:

  1. DisplayDateChanged - é disparado quando a propriedade DisplayDate é alterada;

  2. DisplayModeChanged - é disparado quando a propriedade DisplayMode é alterada;

  3. SelectedDatesChanged - é disparado quando a propriedade SelectedDate ou SelectedDates são alteradas.

Abaixo vemos o código XAML que define os atributos para esses 3 eventos:

<Calendar Height="170" HorizontalAlignment="Left" Margin="160,26,0,0" Name="Calendar1" VerticalAlignment="Top" Width="180"

          SelectionMode="MultipleRange"

       SelectedDatesChanged="Calendar1_SelectedDatesChanged"

          DisplayDateChanged="Calendar1_DisplayDateChanged"

      DisplayModeChanged="Calendar1_DisplayModeChanged"

/>

O code-behind do arquivo MainWindow.xaml.vb para o tratamento dos eventos é mostrado a seguir:

Private Sub MonthlyCalendar_SelectedDatesChanged(sender As Object, e As SelectionChangedEventArgs)
End Sub

Private Sub MonthlyCalendar_DisplayDateChanged(sender As Object, e As CalendarDateChangedEventArgs)
End Sub

Private Sub MonthlyCalendar_DisplayModeChanged(sender As Object, e As CalendarModeChangedEventArgs)
End Sub

Em um exemplo de uso suponha que você queira, em uma seleção de data, capturar o evento para saber qual a data atual esta selecionada.

Defina o arquivo MainWindow.xaml o atributo para o evento e inclua um controle TextBox conforme o código abaixo:

A seguir no arquivo code-behind defina o código para o evento desejado:

Private Sub Calendar1_SelectedDatesChanged(ByVal sender As Object, ByVal e As SelectionChangedEventArgs)

   TextBox1.Text = Calendar1.SelectedDate.ToString()

End Sub

Execute o projeto e selecione uma data para exibi-la no controle TextBox:

E dessa forma creio que apresentei os principais recursos do controle Calendar da WPF.

Aguarde em breve mais artigos sobre WPF...

Eu sei é apenas WPF  , mas eu gosto...

Referências:


José Carlos Macoratti