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

Anúncios

ASP.NET Menu no Safari

janeiro 30, 2011

Há alguns dias precisava que uma aplicação ASP.NET funcionasse no Safari (IPad) mas me deparei com um problema, o menu não funcionava.
Procurando pela internet encontrei uma discussão que mostrava o seguinte código:

if (Request.ServerVariables["http_user_agent"].IndexOf("Safari", StringComparison.CurrentCultureIgnoreCase) != -1)
{
    ClientTarget = "uplevel";
}

Com o código acima verificamos se o browser é o Safari, depois atribuimos à propriedade ClientTarget o valor “uplevel” (especifica compatibilidades equivalentes ao Internet Explorer 6.0).

A propriedade ClientTarget nos permite sobrescrever a detecção automática das compatibilidades do browser e especificar como a página deve ser renderizada.

O código deve ser colocado no método OnPreInit da página. Como a MasterPage não possui esse método é necessário que seja colocado em todas as páginas que possuem o menu.
Para facilitar podemos criar uma classe herdando da classe Page e sobrescrever o método OnPreInit:

public class PaginaPadrao : Page
{
    protected override void OnPreInit(EventArgs e)
    {
        if (Request.ServerVariables["http_user_agent"].IndexOf("Safari", StringComparison.CurrentCultureIgnoreCase) != -1)
        {
            ClientTarget = "uplevel";
        }
        base.OnPreInit(e);
    }
}

Agora nossas páginas deverão herdar da classe PaginaPadrao:

public partial class Default : PaginaPadrao
{
    protected void Page_Load(object sender, EventArgs e)
    {
    }
}

Feito isso toda página quando aberta executará o método OnPreInit da classe PaginaPadrao fazendo com que o menu funcione sem problemas no Safari.

Artigos no MSDN

janeiro 29, 2011

Tive a honra de escrever seis artigos para o MSDN (Microsoft Developer Network), são eles:

Agradeço ao João Paulo Clementi pela oportunidade e à todos que me apoiaram.

Usando Lazy Loading e Eager Loading no Entity Framework 4.0

agosto 15, 2010

  

Lazy Loading e Eager Loading são formas de carregamento de dados. 

Lazy Loading 

Com o Lazy Loading habilitado as entidades relacionadas são carregadas quando as mesmas são acessadas pela Navigation Property

Vejamos a estrutura abaixo: 

  

Temos três entidades, Cliente, Telefone e Extrato. Se o Lazy Loading estiver habilitado, quando carregarmos a entidade Cliente todas as entidades relacionadas (Telefone e Extrato) não serão carregadas. 

Se usarmos o Entity Framework para gerar a camada de objetos, o Lazy Loading vem habilitado por default

Para habilitar ou desabilitar o Lazy Loading basta configurarmos a propriedade ContextOptions.LazyLoadingEnable do contexto. 

Para ficar mais claro, vamos carregar a entidade Cliente com o Lazy Loading habilitado e desabilitado. 

using (DbTesteContainer context = new DbTesteContainer())
{
    context.ContextOptions.LazyLoadingEnabled = true;

    var clientes = context.Cliente;

    foreach (var cliente in clientes)
    {
        Console.WriteLine("Cliente: {0}", cliente.Nome);

        foreach (var telefone in cliente.Telefone)
        {
            Console.WriteLine("Telefone: {0} - {1}",
                telefone.DDD, telefone.Numero);
        }
    }
}

Com o Lazy Loading habilitado conseguimos percorrer por todas as entidades relacionadas sem problemas. 

 

Desabilitando o Lazy Loading, quando acessarmos a entidade Telefone através da Navigation Property não teremos sucesso, pois a propriedade Telefone está nula. 

  

Abaixo outro exemplo. 

using (DbTesteContainer context = new DbTesteContainer())
{
    context.ContextOptions.LazyLoadingEnabled =
        false;

    var cliente = context.Cliente.FirstOrDefault();

    try
    {
        Console.WriteLine(
            cliente.Telefone.FirstOrDefault().Numero);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

Qual o resultado?

Uma exceção do tipo NullReferenceException é disparada.

 

 

Eager Loading

Eager Loading é o oposto do Lazy Loading, ou seja, quando carregamos uma entidade podemos carregar todas (ou algumas) entidades relacionadas ao mesmo tempo através da Navigation Property. Existem duas formas de se fazer isso, a forma implícita e a forma explícita. 

Forma Implícita 

Na forma implícita usamos o método Include

using (DbTesteContainer context = new DbTesteContainer())
{
    context.ContextOptions.LazyLoadingEnabled =
        false;

    var cliente =
        context.Cliente.Include("Telefone").FirstOrDefault();

    try
    {
        Console.WriteLine(
            cliente.Telefone.FirstOrDefault().Numero);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.GetBaseException());
    }
}

  

Forma Explícita 

Na forma explícita usamos o método LoadProperty do contexto. 

using (DbTesteContainer context = new DbTesteContainer())
{
    context.ContextOptions.LazyLoadingEnabled =
        false;

    Cliente cliente = context.Cliente.FirstOrDefault();
    context.LoadProperty(cliente, "Telefone");

    try
    {
        Console.WriteLine(
            cliente.Telefone.FirstOrDefault().Numero);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.GetBaseException());
    }
}

  

O problema dos métodos Include e LoadProperty é o fato de não serem fortemente tipados. Mas uma solução é usarmos o método LoadProperty<TEntity>(TEntity entity, Expression<Func<TEntity, object>> selector)

Cliente cliente = context.Cliente.FirstOrDefault();
context.LoadProperty<Cliente>(cliente, c => c.Telefone);

Usando Model-First no Entity Framework 4.0

agosto 14, 2010

Um recurso muito interessante no Entity Framework Designer do Visual Studio 2010 é a capacidade de gerarmos o DDL (Data Definition Language) a partir de nossas entidades (classes).

Para começar adicionamos a template ADO.NET Entity Data Model ao projeto.

Adicionar ADO.NET Entity Data Model

Na janela Entity Data Model Wizard selecionamos Empty model.

Empty Model

Depois de feito isso, começamos a criação de nossas entidades.  Para adicionar uma nova entidade, podemos clicar com o botão direito do mouse na superfície do designer e selecionar Add –> Entity…

Adicionar Entidade

Ou pela Toolbox.

Adicionar Entidade

Adicionando pelo Designer, uma nova janela (Add Entity) é aberta  para que possamos configurá-la.

Adicionar Entidade

Para adicionar uma nova propriedade, basta clicarmos com o botão direito do mouse em cima da entidade e selecionar Add –> Scalar Property.

Scalar Property

Depois de nossa propriedade devidamente renomeada configuramos suas propriedades, para isso podemos clicar com o botão direito do mouse em cima da propriedade e selecionar Properties ou então pressionando a tecla F4.

Para criarmos os relacionamentos também temos duas opções, pela ToolBox e pelo Designer.

Adicionar Relacionamento

Feito isso, configuramos o relacionamento.

Configurar Relacionamento

A opção Navigation Property nos permite navegar nas entidades relacionadas. Podemos habilitar ou não e também podemos nomear da forma que nos convém.

Já a opção Add foreign key properties to the ‘Nome da Entidade’ Entity, como o próprio nome já diz adiciona uma chave estrangeira, nesse caso é adicionado a propriedade ClienteClienteId na entidade Telefone. Se optarmos por não adicionar a chave estrangeira temos que fazer isso manualmente, para isso configuramos Referential Constraint nas propriedades do relacionamento.

 

Depois de todas as entidades criadas vamos gerar o script, para isso clicamos com o botão direito do mouse na superfície do designer e selecionamos Generate Database from Model…

Gerar Database

A janela Generate Database Wizard é mostrada e nela selecionamos o banco de dados a ser usado.

Wizard Gerar Database

Clicando em Next, é gerado o script para que possamos executá-lo no SQL Server.

Script

E por último executamos o script no Banco de Dados, podemos copiar e colar o script no SQL Server, abrir o arquivo .sql gerado ou então pela própria IDE do Visual Studio 2010.

Executar Script

Pronto, agora podemos usar nosso banco de dados.

Usando Stored Procedures no Entity Framework 4.0

abril 18, 2010

Para usarmos Stored Procedures no Entity Framework, precisamos primeiro gerar o EDMX. 

 

Depois, selecionamos a base de dados que queremos usar. 

 

O próximo passo é selecionar as Tabelas e as Stored Procedures necessárias. 

 

Finalizado esta etapa o arquivo EDMX é gerado. 

 

Se depois de gerado o EDMX tentarmos usar uma Stored Procedure não vamos conseguir. 

 

Para que isso seja possível, devemos seguir os seguintes passos:

Devemos ir até o EDMX e na janela Model Browser (menu View, Other Windows, Entity Data Model Browser).  

Como podemos notar, a pasta Function Imports  do EDMX está vazia. Já a pasta Stored Procedures da Store contém todas as Stored Procedures que selecionamos anteriormente. 

  

  

  

Existem duas formas para adicionarmos as Stored Procedures para a pasta Function Imports. A primeira delas é clicando com o botão direito em cima de Function Imports e depois em Add Function Import…

 

 

 

Uma nova janela (Add Function Import) é aberta para selecionarmos a Stored Procedure e configurá-la.

 

 

 

Feito isso, podemos usar a Stored Procedure sem problemas.

 

 

 

A segunda forma de adicionarmos uma Stored Procedure e clicarmos em cima da mesma com o botão direito do mouse e depois em Add Function Import…

 

 

Observe que nesse caso, o campo Function Import Name é automaticamente preenchido com o nome da Stored Procedure. Observe também que há um botão, Get Column Information que nos mostra quais colunas são retornadas quando a Stored Procedure é executada. Podemos observar ainda que é possível selecionar o retorno, são quatro opções:

  • None – Nenhum tipo de retorno.
  • Scalars – Retorna um tipo específico (Binary, Boolean, Int32, etc.).
  • Complex – Retorna um tipo complexo com propriedades que correspondem às colunas retornadas pela Stored Procedure (Esse tipo é automaticamente gerado quando clicamos no botão Create New Complex Type).
  • Entities – Retorna uma entidade.

No nosso caso, como a Stored Procedure retorna todos os campos, selecionamos EntitiesStudentGrade. 

 

 

 

Por fim, executamos a Stored Procedure.

 

 

Task Dialog

janeiro 24, 2010

Presentes desde o Windows Vista, Task Dialogs são Message Box flexíveis onde é possível criar caixas de mensagem customizadas. Para fazer isso com código gerenciado basta usar o Windows API Code Pack for Microsoft .NET Framework descrito neste post.

Para começar vamos ver os detalhes da classe TaskDialog:

Vamos criar uma Task Dialog bem simples, contendo apenas Caption e Text:

if (TaskDialog.IsPlatformSupported)
{
    TaskDialog myDialog = new TaskDialog();
    myDialog.Caption = "Título";
    myDialog.Text = "Mensagem.";
    myDialog.StartupLocation =
        TaskDialogStartupLocation.CenterScreen;
    myDialog.Show();
}

Como podemos perceber, o primeiro passo é instanciar a classe TaskDialog, definir as propriedades necessárias e por fim chamar o método Show(). A propriedade Caption recebe uma string que determina o título e a propriedate Text também recebe uma string que determina a mensagem da Task Dialog. A propriedade IsPlatformSupported informa se Task Dialog é suportada no sistema operacional. A propriedade StartupLocation permite definir a posição da Task Dialog.

Com a propriedade InstructionText definimos a finalidade da Task Dialog e a propriedade Cancelable define se a Task Dialog pode ser cancelada ou não, definindo para true um botão “Close” é inserido no canto superior direito.

myDialog.InstructionText = "Finalidade da Task Dialog";
myDialog.Cancelable = true;

É possível adicionar uma mensagem que pode ficar oculta com botões para mostrar e esconder a mesma, para isso usamos as seguintes propriedades:

  • DetailsExpanded – Define se a mensagem fica ou não oculta, sendo que o padrão é false.
  • DetailsExpandedText – Define a mensagem.
  • DetailsCollapsedLabel – É o texto que fica no botão para mostrar a mensagem.
  • DetailsExpandedLabel – É o texto que fica no botão para ocultar a mensagem.
  • ExpansionMode – Determina o local da mensagem oculta, podendo ser acima ou abaixo dos botões de mostrar / ocultar a mensagem.
myDialog.DetailsExpanded = true;
myDialog.DetailsExpandedText = "Detalhes...";
myDialog.DetailsCollapsedLabel = "Mostrar";
myDialog.DetailsExpandedLabel = "Ocultar";
myDialog.ExpansionMode =
    TaskDialogExpandedDetailsLocation.ExpandContent;

Pode-se adicionar controles, sendo eles:

  • StandardButtons
    –> Cancel
    –> Close
    –> No
    –> Ok
    –> Retry
    –> Yes
  • Buttons (Customizados)
  • CommandLinks
  • RadioButtons

Usando StandardButtons, podemos usar a lógica OR para os botões adicionados e atribuir o método Show() da nossa Task Dialog à enumeração TaskDialogResult para capturarmos o resultado.

myDialog.StandardButtons =
    TaskDialogStandardButtons.Yes |
    TaskDialogStandardButtons.No |
    TaskDialogStandardButtons.Cancel;
    TaskDialogResult result = myDialog.Show();

Em certas situações os StandardsButtons não suprem nossas necessidades, nesses casos usamos os Buttons (Customizados). Instanciamos a classe TaskDialogButton e definimos as propriedades que se fizerem necessárias, feito isso, é preciso definir um Delegate para o evento Click dos respectivos botões e, por fim, adicionar os botões em nossa Task Dialog.

TaskDialogButton continuar = new TaskDialogButton();
continuar.Default = true;
continuar.Text = "Continuar";
continuar.Click += new EventHandler(continuar_Click);

TaskDialogButton parar = new TaskDialogButton();
parar.Default = false;
parar.Text = "Parar";
parar.Click += new EventHandler(parar_Click);

myDialog.Controls.Add(continuar);
myDialog.Controls.Add(parar);

Ao invés de Buttons podemos usar os CommandLinks, cuja configuração é praticamente a mesma dos Buttons.

TaskDialogCommandLink continuar = new TaskDialogCommandLink();
continuar.Default = true;
continuar.Text = "Continuar";
continuar.Instruction = "Continua a operação.";
continuar.Click +=new EventHandler(continuar_Click);

TaskDialogCommandLink parar = new TaskDialogCommandLink();
parar.Default = false;
parar.Text = "Parar";
parar.Instruction = "Para a operação.";
parar.Click += new EventHandler(parar_Click);

myDialog.Controls.Add(continuar);
myDialog.Controls.Add(parar);

Também podemos colocar RadioButtons que, semelhante aos CommandLinks, a configuração é praticamente a mesma dos Buttons.

TaskDialogRadioButton continuar = new TaskDialogRadioButton();
continuar.Default = true;
continuar.Text = "Continuar";

TaskDialogRadioButton parar = new TaskDialogRadioButton();
parar.Default = false;
parar.Text = "Parar";

myDialog.Controls.Add(continuar);
myDialog.Controls.Add(parar);

Como na Message Box, conseguimos adicionar icone na Task Dialog utilizando a propriedade Icon.
Podemos escolher entre os seguintes icones:

  • Error
  • Information
  • Shield
  • Warning

Podemos adicionar controles ao rodapé usando as propriedades:

  • FooterCheckBoxCheccked – Determina se o checkbox no rodapé inicia selecionado ou não.
  • FooterCheckBoxText – Texto do checkbox no rodapé.
  • FooterIcon – Icone no rodapé.
  • FooterText – Texto no rodapé.
myDialog.FooterCheckBoxChecked = false;
myDialog.FooterCheckBoxText = "Tomar essa decisão para todos.";
myDialog.FooterIcon = TaskDialogStandardIcon.Warning;
myDialog.FooterText = "Texto de rodapé";

Conseguimos adicionar hyperlinks ao rodapé, o que é preciso fazer é definir a propriedade HyperlinksEnable como true, adicionar o link dentro da tag <a href> , definir um delegate para o evento HyperlinkClick e codificá-lo.

myDialog.HyperlinksEnabled = true;
myDialog.FooterText =
    "Para maiores detalhes visite nosso site:
    <a href=\"http://www.microsoft.com.br\">Microsoft</a>";
myDialog.HyperlinkClick += new
    EventHandler<TaskDialogHyperlinkClickedEventArgs>
    (myDialog_HyperlinkClick);

void myDialog_HyperlinkClick(object sender,
    TaskDialogHyperlinkClickedEventArgs e)
{
    Process.Start(e.LinkText);
}

É possível adicionar ProgressBar para mostrar ao usuário o andamento de uma operação. É preciso apenas instanciar a classe TaskDialogProgressBar e atribuí-la à propriedade ProgressBar. É possível também definir o estado do andamento da operação, são eles:

  • Error
  • Marquee
  • Normal
  • Paused
TaskDialogProgressBar progressBar =
    new TaskDialogProgressBar();
progressBar.Minimum = 0;
progressBar.Maximum = 100;
progressBar.Value = 25;
progressBar.State = TaskDialogProgressBarState.Normal;
myDialog.ProgressBar = progressBar;

Desenvolvendo aplicações .NET usando recursos do Windows 7

janeiro 13, 2010

Foi disponibilizado pela Microsoft o Windows API Code Pack for Microsoft .NET Framework que fornece uma biblioteca de códigos para desenvolvermos aplicações .NET usufruindo de alguns recursos do Windows 7 com código gerenciado.
Na versão 1.0.1 são suportados os seguintes recursos:

          • Windows 7 Taskbar
                   –>    Jump Lists, Icon Overlay, Progress Bar, Tabbed Thumbnails, and Thumbnail Toolbars
          • Windows Shell
                    –>   Windows 7 Libraries
                    –>   Windows Shell Search API support
                    –>   Explorer Browser Control
                    –>   A hierarchy of Shell Namespace entities
                    –>   Windows Shell property system
                    –>   Drag and Drop for Shell Objects
                    –>   Windows Vista and Windows 7 Common File Dialogs, including custom controls
                    –>   Known Folders and non-file system containers
          • DirectX
                    –>   Direct3D 11.0, Direct3D 10.1/10.0, DXGI 1.0/1.1, Direct2D 1.0, DirectWrite, Windows Imaging Component (WIC) APIs
         • Windows Vista and Windows 7 Task Dialogs
         • Sensor Platform APIs
         • Extended Linguistic Services APIs
         • Power Management APIs
         • Application Restart and Recovery APIs
         • Network List Manager APIs
         • Command Link control and System defined Shell icons

Para iniciarmos o desenvolvimento primeiro precisamos extrair o conteúdo do arquivo WindowsAPICodePack.zip, depois adicionar os projetos Core e Shell à nossa solução e por fim referenciar as DLLs Core.dll e Shell.dll ao projeto.
Após feito isso é só começar a desenvolver suas aplicações.