Este artigo foi traduzido por máquina.

Fronteiras da interface do usuário

Pensando fora da grade

Charles Petzold

Baixe o código de exemplo

A tela é uma das várias opções de layout disponíveis no Silverlight e no Windows Presentation Foundation (WPF) e é a mais firmemente enraizada no tradição. Ao preencher a tela com filhos, posição cada filho, especificando coordenadas usando a Canvas.Left e Canvas.Top anexado propriedades. Isso é bastante um paradigma diferente de outros painéis, que organiza elementos filho baseados em algoritmos simples sem a necessidade para o programador descobrir os locais reais.

Quando você ouvir a palavra “ tela ”, provavelmente pensa sobre pintura e desenho. Por esse motivo, talvez, os programadores que usam o WPF e Silverlight tendem a dedicada a tela para a exibição dos gráficos vetoriais. Ainda assim, quando você usa a tela para exibir elementos line, Polyline, Polygon e Path, os elementos próprios incluem pontos de coordenadas posição-los dentro da tela. Como resultado, você Don precisa se preocupar com o Canvas.Left e Canvas.Top propriedades anexadas.

Então por que usar Canvas se Don precisar as propriedades anexadas fornece? Existe uma abordagem melhor?

Tela vs. Grid

Com o passar dos anos, eu ter tendia cada vez mais rejeitar a tela para a exibição de gráficos vetoriais, gravitating em vez disso, para o uso de um Grid de célula única. Um Grid de célula única é como uma grade normal, exceto sem nenhuma definição de linha ou coluna. Se a grade tem apenas uma célula, você pode colocar vários elementos para a grade da célula e você Don usar a grade do anexado propriedades para indicar linhas ou colunas.

Inicialmente, usando uma tela ou em uma única célula grade parece muito semelhante. Independentemente disso qual deles você usar para elementos gráficos, line, Polyline, Polygon e Path elementos serão posicionados em relação ao canto superior esquerdo do contêiner com base em seus pontos de coordenadas de vetor.

A diferença entre a tela e a grade de célula única é como o contêiner é exibido para o resto do sistema de layout. O WPF e Silverlight incorporam um layout de duas passagens, de cima para baixo, onde cada elemento interroga o tamanho de seus filhos e, em seguida, é responsável por organizando seus filhos em relação a mesmo. No sistema de layout, a tela e a grade de célula única são muito diferentes:

  • Para seus filhos, a grade tem as mesmas dimensões como dimensões de seu próprio pai. Esses são geralmente finitos dimensões, mas sempre a tela parece ter dimensões infinitas para seus filhos.
  • A grade reporta o tamanho composto de seus filhos a seu pai. No entanto, a tela sempre tem um tamanho aparente do zero, independentemente dos filhos que nele contidos.

Suponha que você tenha um monte de elementos de polígono que formam a algum tipo de imagem de elementos gráficos como desenhos vetoriais. Se você colocar todos esses elementos de polígono na grade de uma única célula, o tamanho da grade de baseia-se nas coordenadas horizontal e verticais máxima dos polígonos. A grade, em seguida, pode ser tratada como um elemento finito em tamanho normal dentro do sistema de layout porque seu tamanho corretamente reflete o tamanho da imagem composta. (Na verdade, isso funciona corretamente somente se o canto superior esquerdo da imagem estiver no ponto (0, 0) e há coordenadas não negativas.)

Colocar todos os os polígonos em uma tela, no entanto, e a tela para relatórios para o sistema de layout que ela tem um tamanho de zero. Em geral, a integração de uma imagem de gráficos de vetor compostos em seu aplicativo, você quer certamente o comportamento da grade de célula única em vez da tela.

Então, a tela é totalmente inútil? De forma alguma. O truque é usar as peculiaridades da tela a seu favor. Em um sentido bastante real, a tela não participar de layout. Portanto, você pode usá-lo sempre que precisar Transcendente layout — para exibir elementos gráficos quebrar os limites do sistema de layout e float fora dela. Por padrão a tela não clip seus filhos, mesmo que se for muito pequeno, ele ainda pode hospedar filhos fora de seus limites. A tela é mais um ponto de referência para a exibição de elementos ou elementos gráficos que um recipiente.

A tela é excelente para as técnicas que passaram a considerar como “ pensamento fora da grade. ” Embora eu irá mostrar os exemplos de código no Silverlight, você pode usar as mesmas técnicas no WPF. O código-fonte para download que acompanha este artigo é uma solução do Visual Studio chamada ThinkingOutsideTheGrid e você pode jogar com os programas em charlespetzold.com/Silverlight/ThinkingOutsideTheGrid.

Vinculação Visual de controles

Suponha que você tiver diversos controles em seu Silverlight ou aplicativo do WPF e você precisará fornecer algum tipo de vínculo visual entre dois ou mais controles. Talvez você deseja desenhar uma linha de um controle para outro e, talvez essa linha irá cruzar outros controles entre.

Certamente esta linha deve reagir às mudanças no layout, talvez como a janela ou página é redimensionada pelo usuário. Que está sendo informado quando um layout é atualizado é um aplicativo excelente do evento LayoutUpdated — um evento nunca tive ocasião para usar antes de explorando os problemas descrito neste artigo. LayoutUpdated é definido pelo UIElement no WPF e por FrameworkElement no Silverlight. Como o nome sugere, o evento é acionado depois que uma passagem de layout possui reorganizados elementos na tela.

Ao processar o evento LayoutUpdated, você Don quer fazer tudo o que irá fazer com que outra passagem de layout e você embroiled em uma recursão infinita. Que é onde a tela se torna útil: Porque ele reporta o tamanho zero sempre ao seu pai, você pode alterar os elementos da tela sem afetar o layout.

O arquivo XAML do programa ConnectTheElements é estruturado como este:

<UserControl ... >
  <Grid ... >
    <local:SimpleUniformGrid ... >
      <Button ... />
      <Button ... />
      ...
    </local:SimpleUniformGrid>

    <Canvas>
      <Path ... /> 
      <Path ... />
      <Path ... />
    </Canvas>
  </Grid>
</UserControl>

A grade contém uma SimpleUniformGrid calcula o número de linhas e colunas para exibir seus filhos com base em seu tamanho geral e taxa de proporção. Conforme você altera o tamanho da janela, alterará o número de linhas e colunas e células mudará ao redor. Dos botões de 32 esse SimpleUniformGrid, dois botões têm nomes de btnA e btnB. A tela ocupa a mesma área que o SimpleUniformGrid, mas fica na parte superior de. Esta tela contém elementos de caminho que o programa usa para desenhar elipses em torno de dois botões nomeados e uma linha entre eles.

O arquivo code-behind faz todo o seu trabalho durante o evento LayoutUpdated. Ele precisa encontrar o local dos dois botões nomeados em relação à Canvas, convenientemente também é alinhada com a SimpleUniformGrid, a grade e MainPage propriamente dito.

Para encontrar um local de qualquer elemento em relação à qualquer outro elemento na mesma árvore visual, use o método TransformToVisual. Esse método é definido pela classe Visual no WPF e pelo UIElement no Silverlight, mas funciona da mesma forma nos dois ambientes. Suponha que el1 elemento seja em algum lugar dentro da área ocupada por el2. (Em ConnectTheElements, el1 é um botão e el2 é MainPage.) Essa chamada de método retorna um objeto do tipo GeneralTransform, que é a classe abstrata pai para todas as outras classes da transformação gráficos:

el1.TransformToVisual(el2)

Você não pode realmente fazer nada com GeneralTransform exceto chamada seu método de transformação, que transforma um ponto de um espaço de coordenadas para outro.

Suponha que você deseja localizar o Centro de el1 mas em espaço de coordenadas do el2. Eis aqui o código:

Point el1Center = new Point(
  el1.ActualWidth / 2, el1.ActualHeight / 2); 
Point centerInEl2 = 
  el1.TransformToVisual(el2).Transform(el1Center);

Se el2 é a tela para qualquer um dos ou alinhada com a tela, em seguida, você pode usar esse ponto centerInEl2 para definir um elemento gráfico na tela que aparentemente será posicionada no centro da el1.

ConnectTheElements realiza essa transformação em seu método WrapEllipseAroundElement para desenha elipses ao redor dos dois botões nomeados e, em seguida, calcula as coordenadas de linha entre as elipses, com base em uma interseção de linha entre os centros dos botões. A Figura 1 mostra o resultado.

image: The ConnectTheElements Display

Figura 1 Exibir ConnectTheElements

Se você tentar este programa no WPF, altere o SimpleUniformGrid para um WrapPanel para que uma alteração no layout mais dinâmica ao redimensionar a janela do programa.

Controle deslizantes

Alterar elementos gráficos e outros elementos visuais em resposta às alterações em uma barra de rolagem ou o controle deslizante é muito básico e no WPF e Silverlight, você pode fazê-lo em código ou uma ligação XAML. Mas e se você deseja alinhar elementos gráficos com exatidão thumb deslizante real?

Essa é a idéia por trás do projeto TriangleAngles, que eu concebido como um tipo de demonstração interativa trigonometria. Eu organizados dois controles deslizantes, um vertical e outra na horizontal, ângulos retos entre si. Os dois polegares controle deslizante definir dois vértices de um triângulo, conforme mostrado no Figura 2.

image: The TriangleAngles Display

Figura 2 Exibir TriangleAngles

Observe como o triângulo semi-transparente fica acima dos dois controles deslizantes. À medida que você move os controle deslizante polegares, os lados do triângulo alterar tamanho e a proporção, conforme indicado pelo ângulos inscrito e os rótulos em segmentos verticais e horizontais.

Isso é, obviamente, outro trabalho para uma sobreposição de tela, mas com uma camada adicional de complexidade, pois o programa precisa obter acesso para o controle deslizante do polegar. Essa prática controle deslizante é parte de um modelo de controle: os polegares são atribuídos nomes dentro do modelo, mas infelizmente esses nomes não podem ser acessados fora do modelo.

Em vez disso, a classe estática VisualTreeHelper freqüentemente essencial entra em ação. Essa classe permite qualquer árvore visual em WPF ou Silverlight através dos métodos GetParent, GetChildenCount e GetChild orientá-lo (ou em vez disso, suba). Ao generalizar o processo de localização de um filho de tipo específico, escrevi um pequeno método genérico recursiva:

T FindChild<T>(DependencyObject parent) 
  where T : DependencyObject

Posso chamá-lo como este:

Thumb vertThumb = FindChild<Thumb>(vertSlider);
Thumb horzThumb = FindChild<Thumb>(horzSlider);

Neste ponto, eu poderia usar TransformToVisual em dois polegares para obter as coordenadas em relação a sobreposição de tela.

Bem, funcionou para um controle deslizante, mas não no outro e demorei um pouco se lembrar de que o modelo de controle para o controle deslizante contém dois polegares — um para a orientação horizontal e um para vertical. Dependendo da orientação definida para o controle deslizante, metade o modelo tem sua propriedade Visibility definido como recolhido. Adicionei um segundo argumento para o método FindChild chamado mustBeVisible e usada para abandonar a pesquisa para baixo qualquer ramificação filho onde um elemento não estiver visível.

Definindo HitTestVisible para false no polígono que forma o triângulo ajudou a impedir que ele interferindo na entrada do mouse para o controle deslizante Slider.

Rolagem fora o ItemsControl

Suponha que você estiver usando um ItemsControl ou um ListBox com um DataTemplate para exibir os objetos na coleção do controle. Pode você incluir uma tela em que DataTemplate para que as informações relacionadas a um item específico podem ser exibidas fora do controle, mas parece para rastrear o item como o controle é rolada?

Ainda não encontrei uma boa maneira de fazer exatamente isso. O grande problema parece ser uma região de recorte imposta pelo ScrollViewer. Este ScrollViewer Corta qualquer tela dangle fora de seu limite e, consequentemente, nada na tela para que por acaso.

No entanto, com um pouco conhecimento adicional do funcionamento interno de ItemsControl, você pode fazer algo próximos para que você deseja.

Acho que esse recurso como um pop-out no que é algo que pertencem a um item em um ItemsControl, mas na verdade, ser exibido de ItemsControl propriamente dito. Projeto ItemsControlPopouts demonstra a técnica. Para oferecer algo para o ItemsControl exibir, criei um banco de dados pouco chamado ProduceItems.xml que reside no subdiretório Data da ClientBin. ProduceItems consiste em um número de elementos com o nome da marca do ProduceItem, cada qual contendo um atributo Name, um atributo de fotos fazendo referência a uma imagem de bitmap do item e uma mensagem opcional, que será exibido “ exibido-out ” de ItemsControl. (As fotos e outro trabalho artístico são Microsoft Office clip-arts.)

As classes ProduceItem e ProduceItems oferecem suporte a código para o arquivo XML e ProduceItemsPresenter lê o arquivo XML e desserializa-lo em um objeto ProduceItems. Isso é definido para a propriedade DataContext da árvore visual que contém o ScrollViewer e ItemsControl. O ItemsControl contém um DataTemplate simples para exibir os itens.

Agora você pode detectar um pequeno problema. O programa está efetivamente inserindo objetos comerciais do tipo ProduceItem no ItemsControl. Internamente, o ItemsControl está criando uma árvore visual de cada item com base no DataTemplate. Para controlar o movimento desses itens, você precisa ter acesso à árvore de visual interno do item para descobrir qual exatamente os itens devem em relação ao restante do programa.

Essas informações estão disponíveis. ItemsControl define uma propriedade somente obtenção chamada ItemContainerGenerator que retorna um objeto do tipo ItemContainerGenerator. Esta é a classe responsável por gerar as árvores visuais associadas a cada item em ItemsControl e contém métodos úteis, como ContainerFromItem, que fornece o recipiente (que é realmente um ContentPresenter) para cada objeto no controle.

Como os dois outros programas, o programa ItemsControlPopouts abrange toda a página com uma tela. Uma vez event LayoutUpdated permite que o programa verificar se algo na tela precisa ser alterado. O manipulador LayoutUpdated neste programa enumera através dos objetos ProduceItem em ItemsControl e verifica a existência de uma propriedade de mensagem não nulo e não-vazia. Cada uma dessas propriedades de mensagem deve corresponder a um objeto do tipo PopOut na Canvas. O PopOut é simplesmente uma classe pequena que derive de ContentControl com um modelo para exibir uma linha e o texto da mensagem. Se o PopOut não estiver presente, ele tem criado e adicionado da tela. Se estiver presente, simplesmente é reutilizada.

O PopOut, em seguida, deve ser posicionado dentro da tela. O programa obtém o recipiente que corresponde ao objeto de dados e transforma o seu local relativo à tela. Se esse local estiver entre as partes superior e inferior da ScrollViewer, o PopOut tem sua propriedade de visibilidade definida como Visible. Caso contrário o PopOut está oculto.

Quebra de célula

O WPF e Silverlight certamente tem dado o brinde grande facilidade no layout. A grade e outros painéis colocar elementos claramente em células e certifique-se de que é onde elas permanecem. Seria uma pena se, em seguida, assumido que conveniência era uma limitação necessária para a liberdade de colocar elementos sempre que você deseja que eles.

Charles Petzold é editor colaborador há muito tempo para MSDN Magazine*.*Seu livro mais recente é “ O Turing Annotated: A Guided Tour pelo papel histórico de Alan Turing sobre Computability e a máquina de Turing ” (Wiley, 2008). Blogs Petzold no seu site da charlespetzold.com.

Graças aos seguintes especialistas técnicos para revisar este artigo: Arathi Ramani e a equipe de layout do WPF