WPF - Vinculando imagens de um banco de dados em um ListBox


O controle ListBox da WPF é diferente do controle ListBox Windows Forms e possui muitas habilidades que podem ser estendidas. Usando apenas o controle ListBox podemos desenhar a maioria dos controles disponíveis na seção de controles Data da ASP .NET.

Neste artigo eu vou realizar a vinculação de dados entre o banco de dados Northwind.mdf e o controle ListBox da WPF mostrando como vincular imagens armazenadas no banco de dados no ListBox.

Vou usar LINQ to SQL para obter os dados e realizar a vinculação no ListBox, sendo que, através do mapeamento do tipo de dados Image do SQL Server para o tipo System.Data.Linq.Binary e posteriormente, quando da vinculação para o tipo Image, vamos converter de System.Data.Linq.Binary para o formato BitmapSource.

Desde que precisamos converter o System.Data.Linq.Binary para BitmapSource, temos definir um conversor enquanto estamos atribuindo a origem da imagem na tag. Isso pode ser feito usando um StaticResource. Sem a criação de um recurso estático a marcação não sabe como criar uma instância da classe de conversor.

Geralmente precisamos formatar ou converter os dados exibidos quando usamos o databinding pois na vinculação de dados comum a informação vem da fonte para a origem sem qualquer mudança. Se você estiver usando a vinculação two-way vai ter que fazer um conversor de forma a converter os dados informados pelo usuário em um formato apropriado ao objeto de origem.

A WPF permite que você realize estas duas tarefas através da criação de uma classe conversora de forma que ela formate a exibição no destino e , em caso de two-way binding, converta o novo valor informado no destino antes do mesmo ser aplicado a fonte.

Os conversores de valores (Value Converter) podem então ser considerados como uma peça muito importante na vinculação de dados no WPF podendo ser usados de diversas formas:

  • Formatar dados para uma representação no formato string. Ex: Converter um número para um valor monetário;
  • Criar um tipo de objeto WPF específico. Ex: Converter um bloco de dados binários em um objeto BitmapImage;
  • Alterar condicionalmente a propriedade de um elemento baseado no dado vinculado. Ex: Alterar a cor de um elemento para destacar valores em uma faixa especifica;

Para criar um conversor de valores, você precisa seguir 4 etapas:

  • Criar um classe que implementa a interface IValueConverter;
  • Incluir o atributo ValueConversion na declaração da classe e especificar os tipos de dados de destino e de origem;
  • Implementar o método Convert() que altera os dados a partir do formato original para o formato de exibição;
  • Implementar o método ConvertBack() que reverte as alterações e altera o valor a partir do formato de exibição para o formato original;

Então, precisamos criar um recurso estático e mapear o arquivo de classe respectivo na seção de recursos. Na seção de recursos no âmbito de Window.Resources, podemos criar o recurso estático com uma referência ao objeto de classe. Esta classe de objeto pode ser especificada na tag window como um atributo xmlns. Podemos especificar o nosso próprio namespace. Neste caso, chamei-o como local e voltado para a classe ImageConverter.

O nosso conversor de imagem deve implementar a interface IValueConverter. A interface IValueConverter tem que definir dois métodos:

Criando o projeto WPF

Abra o Visual Basic 2010 Express Edition e crie um novo projeto do tipo WPF Application com o nome ListBox_Imagem;

No menu Project clique em Add New Item e a seguir selecione o template LINQ to SQL Classes, informe o nome Northwind.dbml e clique no botão Add;

A seguir abra a janela DataBase Explorer e selecionando o banco de dados Northwind.mdf arraste e solte as tabelas Categories e Products para o descritor LINQ to SQL conforme a figura abaixo:

Obs: Se a conexão com o banco de dados ainda não existir você deverá criá-la ou adicioná-la;

Com isso temos a nossa fonte de dados definida através do LINQ to SQL.

Definindo a interface WPF

Selecione o arquivo MainWindow.xaml e defina o seguinte leiaute usando o código XAML:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ListBox_Imagem"
    Title="Exibindo imagens do banco de dados" Height="500" Width="550">
   
<Window.Resources>
        <local:ImageConverter x:Key="ImageConverter"/>
        <DataTemplate  x:Key="ProductTemplate" >
            <TextBlock  Text="{Binding ProductName}"
                        FontFamily="Calibri" FontSize="14"  />
        </DataTemplate>
        <DataTemplate  x:Key="CategoryTemplate">
            <StackPanel Orientation="Horizontal" VerticalAlignment="Top"  >
                <Border BorderThickness="1"
                            BorderBrush="silver" CornerRadius="10"
                            Padding="5" Margin="15px" Background="#E6E6E6"  >
                    <StackPanel>
                        <Image Source="{Binding Picture,
                            Converter={StaticResource ImageConverter}}"  />
                        <ItemsControl ItemsSource="{Binding Products}"
                            ItemTemplate="{StaticResource ProductTemplate}" >
                        </ItemsControl>
                    </StackPanel>
                </Border>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>

    <Grid>
      
 <ListBox Margin="10,10,10,0" Name="lstCategories"
                 ItemTemplate="{StaticResource CategoryTemplate}" 
                 VerticalAlignment="Top" Height="439">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel IsItemsHost="True" />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
        </ListBox>

    </Grid>
</Window>

No código acima temos:

1- A declaração do namespace local: xmlns:local="clr-namespace:ListBox_Imagem"

2- A definição do recurso estático - Window.Resources - que usa o conversor de Imagens - ImageConverter - e o recurso DataTemplate para exibir a imagem da categoria e os nomes dos produtos relacionados:

<Window.Resources>
        <local:ImageConverter x:Key="ImageConverter"/>
        <DataTemplate  x:Key="ProductTemplate" >
            <TextBlock  Text="{Binding ProductName}"
                        FontFamily="Calibri" FontSize="14"  />
        </DataTemplate>
       
<DataTemplate  x:Key="CategoryTemplate">
            <StackPanel Orientation="Horizontal" VerticalAlignment="Top"  >
                <Border BorderThickness="1"
                            BorderBrush="silver" CornerRadius="10"
                            Padding="5" Margin="15px" Background="#E6E6E6"  >
                    <StackPanel>
                        <Image Source="{Binding Picture,
                            Converter={StaticResource ImageConverter}}"  />
                        <ItemsControl ItemsSource="{Binding Products}"
                            ItemTemplate="{StaticResource ProductTemplate}" >
                        </ItemsControl>
                    </StackPanel>
                </Border>
            </StackPanel>
        </DataTemplate>

    </Window.Resources>

3- O controle ListBox usando o recurso estático para exibir a imagem:

 <Grid>
      
 <ListBox Margin="10,10,10,0" Name="lstCategories"
                 ItemTemplate="{StaticResource CategoryTemplate}" 
                 VerticalAlignment="Top" Height="439">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel IsItemsHost="True" />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
        </ListBox>

    </Grid>

Vamos definir agora a classe conversora chamada ImageConverter. No menu Project clique em Add Class, selecione o template Class e  informe o nome ImageConverter.vb;

Após isso inclua o código a seguir nesta classe:

Imports System.IO
Imports System.Globalization

Public Class ImageConverter
    Implements IValueConverter

    Public Function Convert(ByVal value As Object, _
                            ByVal targetType As Type, _
                            ByVal parameter As Object, _
                            ByVal culture As CultureInfo) _
                            As Object Implements IValueConverter.Convert

        Dim BmpImage As BitmapSource
        Dim BmpBytes = DirectCast(value, System.Data.Linq.Binary).ToArray
        Using BmpStream = New MemoryStream()

            Dim NwOffset = 78 'para tratar imagens do tipo ole armazenadas no Northwind 
            BmpStream.Write(BmpBytes, NwOffset, BmpBytes.Length - NwOffset)
            BmpImage = BitmapFrame.Create(BmpStream, _
                                          BitmapCreateOptions.IgnoreImageCache, _
                                          BitmapCacheOption.OnLoad)
        End Using
        Return BmpImage
    End Function

    Public Function ConvertBack(ByVal value As Object, _
                           ByVal targetType As Type, _
                           ByVal parameter As Object, _
                           ByVal culture As CultureInfo) _
                           As Object Implements IValueConverter.ConvertBack
        Return Nothing
    End Function
End Class

Esta classe implementa a interface IValueConverter e os métodos: Convert e ConvertBack;

Para concluímos basta definir no arquivo code-behind MainWindow.xaml.vb o código abaixo onde estamos usando o contexto definido pelo LINQ to SQL onde foi realizado o mapeamento das tabelas Products e Categories para as classes Product e Category:

Class MainWindow 

    Public NorthwindDados As New NorthwindDataContext

    Private Sub Window1_Loaded(ByVal sender As System.Object, _
                ByVal e As RoutedEventArgs) Handles MyBase.Loaded
        lstCategories.ItemsSource = NorthwindDados.Categories
    End Sub

End Class

Executando o projeto teremos o seguinte resultado:

Você pode fazer o mesmo em aplicações Windows Forms mas vai dar um pouco mais de trabalho.

Pegue o projeto completo aqui: ListBox_Imagem.zip

"Deus é Espírito,e importa que os que o adoram o adorem em espírito e em verdade." (João 4:24)

Referências:


José Carlos Macoratti