Ontem (29/07/2008), apresentei um WebCast no MSDN, sobre DataBinding com Silverlight. Nele apresentei os conceitos de ligação de dados com a interface de usuário, mostrando o novo compoenente DataGrid, ligando a dados locais e também a dados remotos, usando um serviço WCF que obtém os dados de um banco de dados usando LINQ to SQL.

Além disso, mostrei as interfaces INototyPropertyChanged and como usar um Converter para fazer a conversão no DataBinding. Se você se interessou, pode fazer o download da gravação em https://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?culture=pt-BR&EventID=1032382850&CountryCode=BR (você se inscreve no evento para fazer o download).

O código fonte do que foi apresentado lá está no arquivo anexo.

Ontem, no Mix/08 foi lançado o Beta 1 do Silverlight 2.0. Esta tem muitas novidades sobre a versão anterior: novos controles (inclusive uma DataGrid), Data Binding, controles de layout, entre outros.


Vale a pena instalar o runtime da versão 2.0 em http://go.microsoft.com/fwlink/?LinkId=112012 para brincar um pouco com ele. Se quiser ver um pouco das novidades, veja o demo.


Gostou, quer mais? Instale o SDK em http://www.microsoft.com/downloads/details.aspx?FamilyId=E0BAE58E-9C0B-4090-A1DB-F134D9F095FD&displaylang=en e o Blend 2.5 em http://www.microsoft.com/expression/products/download.aspx?key=blend2dot5 e dê uma olhada nos tutoriais, vídeos e hands-on labs em http://silverlight.net e cheque os blogs do ScottGu aqui e aqui

Acaba de ser lançado o meu mais novo utilitário para a PCMag.com, o FontViewer 3. Todo escrito em C# com WPF, ele tem alguns recursos bem interessantes, que vale a pena ver:

  • Uma nova interface para visualização rápida dos fontes instalados
  • A possibilidade de criar coleções de fontes, facilitando a instalação e desinstalação de grupos de fontes
  • Uma caixa de pesquisa paraprocurar os fontes por nome
  • Filtros para visualizar os fontes por tipo
  • Detalhes dos fontes
  • Possibilidade de ver textos personalizados nos diversos fontes
moz-screenshot 
 
Se você se interessou, veja mais em http://www.pcmag.com/article2/0,2817,2270853,00.asp

Uma das grandes mudanças no Office 2007 está nos arquivos que são criados: o padrão de arquivos mudou radicalmente. Os novos arquivos usam tecnologia aberta, sendo arquivos zip contendo múltiplos arquivos XML. Para demonstrar que não é necessário usar APIs proprietárias nem componentes especiais, escrevi um artigo que mostra como acessar os arquivos OpenXML com Delphi. Neste artigo, mostro como é possível ler, modificar ou mesmo criar arquivos OpenXML usando componentes gratuitos. Vale a pena conferir em http://www.microsoft.com/brasil/msdn/tecnologias/Office/Open_XML_Delphi.mspx

Se você ainda não conhece o New York Times Reader vale a pena conhecê-lo, pois é um ótimo exemplo de aplicação WPF.

Se você gostou mesmo dele e quer desenvolver algo semelhante, chegou a hora: a Microsoft lançou o reader toolkit, com o nome de Syndicated Client Experiences Starter Kit para que qualquer um possa desenvolver seus readers. Além diss, ela disponibilizou um reader muito interessante do MSDN Magazine com código fonte. Vale a pena dar uma olhada!

Muitas vezes, queremos fazer um zoom em componentes WPF. Como todo o desenho é vetorial, não há perda de resolução neste processo.

Uma maneira de fazer o zoom é usar uma transformação, do tipo ScaleTransform. O ScaleTransform aumenta ou diminui o tamanho do componente, segundo suas propriedades ScaleX e ScaleY. Por exemplo, se quisermos aumentar ou diminuir o zoom no canvas do post anterior, podemos fazer o seguinte: no arquivo xaml, criamos um DockPanel para armazenar os botões e o canvas:

  <DockPanel>
    <StackPanel Orientation="Horizontal" Height="40" DockPanel.Dock="Top">
      <Button x:Name="maisZoom" Click="maisZoom_Click" Margin="5" Content="Mais zoom"/>
      <Button x:Name="menosZoom" Click="menosZoom_Click" Margin="5" Content="Menos zoom"/>
      <TextBlock x:Name="textZoom" Text="Zoom: 100%" VerticalAlignment="Center" Margin="5"/>
    </StackPanel>
    <Canvas....>
  </Canvas>
  </DockPanel>

Na parte superior do DockPanel colocamos um StackPanel. Nesse StackPanel colocamos dois botões e um textblock que irá mostrar o zoom atual. 

Em seguida, criamos uma transformação (LayoutTransform) para o canvas, inicializando a escala para 1:

      <Canvas...>
      <Canvas.LayoutTransform>
        <ScaleTransform x:Name="canvasZoom" ScaleX="1" ScaleY="1" />
      </Canvas.LayoutTransform>
 

 Finalmente, colocamos o código para o evento Click dos botões:

        private void maisZoom_Click(object sender, RoutedEventArgs e)
        {
            canvasZoom.ScaleX += 0.1;
            canvasZoom.ScaleY += 0.1;
            textZoom.Text = String.Format("Zoom: {0}%", canvasZoom.ScaleX * 100);
        }

        private void menosZoom_Click(object sender, RoutedEventArgs e)
        {
            canvasZoom.ScaleX -= 0.1;
            canvasZoom.ScaleY -= 0.1;
            textZoom.Text = String.Format("Zoom: {0}%", canvasZoom.ScaleX * 100);
        }
 

Executando o programa, vemos que os botões aumentam ou diminuem o zoom do canvas, sem nenhuma perda de resolução.

Quando aumentamos o zoom, o Canvas aumenta de tamanho, mas não há nenhuma indicação disso: devemos aumentar nossa janela para ver o restante da imagem. Podemos usar barras de rolagem para ver o restante da imagem quando o zoom é maior que 100%. Para isso, colocamos o Canvas dentro de um ScrollViewer:

     <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" >
      <Canvas ...>
         ....     
      </Canvas>
    </ScrollViewer>

O ScrollViewer apresenta barras de rolagem que são mostradas quando o tamanho de seu conteúdo é maior que seu tamanho. Assim, se dermos um zoom no Canvas que faça que ele fique maior que a área útil do ScrollViewer, as barras de rolagem aparecerão.

O projeto completo pode ser baixado aqui 

 

Em WPF, uma Shape (retângulos, elipses, linhas,..) é um FrameworkElement e recebe os eventos de mouse e teclado, como qualquer outro FrameworkElement (botões, por exemplo). Isto é muito diferente de WinForms, onde as formas desenhadas usando GDI+ são apenas desenhos na tela e não recebem nenhum evento. 

Podemos usar isso para movimentar nossas shapes com o mouse usando os eventos PreviewMouseLeftButtonDown, PreviewMouseLeftButtonUp e PreviewMouseMove.

Inicialmente, colocamos algumas shapes no Canvas:

    <Canvas x:Name="canvas1" PreviewMouseLeftButtonDown="canvas1_PreviewMouseDown"
            PreviewMouseMove="canvas1_PreviewMouseMove"
            PreviewMouseLeftButtonUp="canvas1_PreviewMouseUp">
      <Rectangle Canvas.Top="10" Canvas.Left="10" Width="20" Height="40" Fill="Fuchsia" />
      <Ellipse Canvas.Top="25" Canvas.Left="50" Width="40" Height="40" Fill="DarkSeaGreen" />
      <Line Canvas.Top="120" Canvas.Left="50" X1="10" Y1="10" X2="50" Y2="50" Stroke="Navy" StrokeThickness="3"/>
    <Polygon Canvas.Top="50" Canvas.Left="120" Points="30,20 80,24 80,54 30,20" Fill="Red"/>
  </Canvas>
 

Como podemos ver, colocamos os manipuladores de eventos no Canvas. Devido ao recurso de Bubbling e Tunneling, os eventos são propagados por toda a árvore de elementos e, quando clicamos em qualquer uma das shapes, o Canvas recebe o evento também. Assim, podemos processar os eventos em um único local, não nos preocupando de atribuir os manipuladores para cada shape do desenho.

 No código fonte, definimos alguns campos auxiliares:

        Point start; // Ponto base para a movimentação
        int currentZ = 0; // Z-Index atual
        bool isDragging = false; // Está movendo?
        Shape movedElement; // Elemento sendo movido

O manipulador para o evento PreviewMouseLeftButtonDown é:

        private void canvas1_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            // Verifica se clicamos numa Shape
            if (e.Source is Shape)
            {
                // Pega posição atual do mouse
                start = e.GetPosition(canvas1);
                // Inicializa variáveis e configura opacidade da shape para 0.5
                isDragging = true;
                movedElement = (Shape)e.Source;
                ((Shape)e.Source).Opacity = 0.5;
                canvas1.CaptureMouse();
                e.Handled = true;
            }
        }

Aqui, inicializamos a variável start com a posição que o mouse foi clicado, configuramos isDragging para true, deixamos o elemento semi-transparente e capturamos o mouse. O manipulador para o evento PreviewMouseMove é:

        private void canvas1_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (isDragging)
            {
                Point Pt = e.GetPosition(canvas1);
                // Pega posição atual da shape
                double CurrentLeft = (double)movedElement.GetValue(Canvas.LeftProperty);
                double CurrentTop = (double)movedElement.GetValue(Canvas.TopProperty);

                // Calcula nova posição
                double newLeft = CurrentLeft + Pt.X - start.X;
                double newTop = CurrentTop + Pt.Y - start.Y;

                // Reposiciona elemento
                movedElement.SetValue(Canvas.LeftProperty, newLeft);
                movedElement.SetValue(Canvas.TopProperty, newTop);

                start = Pt;
                e.Handled = true;
            }
        }

Aqui verificamos se estamos arrastando um elemento. Se estivermos, pegamos a nova posição do mouse, recalculamos a posição da Shape e movemos. Como a posição é dada por attached properties, devemos usar SetValue e GetValue para obter e alterar as propriedades Canvas.Left e Canvas.Top. Finalmente, fechamos o movimento no evento PreviewMouseLeftButtonUp:

        private void canvas1_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            // Restaura valores
            movedElement.Opacity = 1;
            movedElement.SetValue(Canvas.ZIndexProperty, ++currentZ);
            isDragging = false;
            canvas1.ReleaseMouseCapture();
        }

Aqui apenas restauramos os valores. Uma coisa que deve ser notada é que estamos alterando a propriedade Canvas.ZIndex. Isto é devido ao fato que queremos que os elementos que movemos fiquem sempre por cima dos outros. Assim, usamos a variável currentZ para guardar o ZIndex, incrementando-o a cada movimento, de maneira que a cada vez que movemos um elemento, ele tenha um ZIndex maior que o anterior.

Desta maneira, podemos mover os elementos dentro de uma janela WPF. Note que este código pode ser estendido a qualquer FrameworkElement, não sendo apenas para Shapes.

 Uma pergunta muito comum que vejo por aí é como trocar o Enter pelo Tab. Normalmente, deve-se manipular o evento de tecla pressionada, verificar se é um Enter e mudar o foco para o elemento seguinte na lista de tabulação. Em WPF, podemos criar um manipulador para o evento PreviewKeyDown da janela (no WPF, os eventos são propagados por toda a árvore de elementos, assim, os eventos de tecla obrigatoriamente passam pela janela, independente de onde tenham sido gerados), como o seguinte:


private void Window_PreviewKeyDown(object sender, KeyEventArgs e)

{

  if (e.Key ==
Key.Return)

  {

    FocusNavigationDirection focusDirection = FocusNavigationDirection.Next;

    TraversalRequest
request =
new TraversalRequest(focusDirection);

    UIElement
elementWithFocus =
Keyboard.FocusedElement as
UIElement;

    if
(elementWithFocus !=
null)

      elementWithFocus.MoveFocus(request);

    e.Handled = true;

  }

}

 

Hoje postei um novo artigo no CodeProject sobre acesso a dados com WPF e LINQ, mostrando como você pode criar um visual bonito, filtrar, classificar e agrupar os dados sem código usando WPF. Isso ligado ao LINQ, que sem dúvida é uma tecnologia que veio para ficar. Se você quer saber mais, dê uma conferida em http://www.codeproject.com/KB/WPF/wpfviews.aspx. Se você gostar do artigo, cadastre-se e vote nele.

Meu filho Roberto também postou um artigo sobre 3D interativo, a tecnologia introduzida com o .net 3.5, vale a pena conferir em http://www.codeproject.com/KB/WPF/3D-BookWriter.aspx