Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este tópico fornece um tour guiado pela hierarquia de classes do WPF (Windows Presentation Foundation). Ele abrange a maioria dos principais subsistemas do WPF e descreve como eles interagem. Ele também detalha algumas das opções feitas pelos arquitetos do WPF.
System.Object
O modelo de programação do WPF primário é exposto por meio do código gerenciado. No início da fase de design do WPF, havia uma série de debates sobre onde a linha deve ser desenhada entre os componentes gerenciados do sistema e os não gerenciados. O CLR fornece uma série de recursos que tornam o desenvolvimento mais produtivo e robusto (incluindo gerenciamento de memória, tratamento de erros, sistema de tipo comum etc.), mas eles têm um custo.
Os principais componentes do WPF são ilustrados na figura abaixo. As seções vermelhas do diagrama (PresentationFramework, PresentationCore e milcore) são as principais partes de código do WPF. Destes, apenas um é um componente não gerenciado – milcore. O Milcore é escrito em código não gerenciado para habilitar uma integração apertada com o DirectX. Toda a exibição no WPF é feita por meio do mecanismo DirectX, permitindo uma renderização eficiente de hardware e software. O WPF também exigia controle fino sobre memória e execução. O mecanismo de composição no milcore é extremamente sensível ao desempenho e exigiu abandonar muitas vantagens do CLR para melhorar o desempenho.
A comunicação entre as partes gerenciadas e não gerenciadas do WPF é discutida posteriormente neste tópico. O restante do modelo de programação gerenciada é descrito abaixo.
System.Threading.DispatcherObject
A maioria dos objetos no WPF deriva de DispatcherObject, que fornece os constructos básicos para lidar com simultaneidade e threading. O WPF é baseado em um sistema de mensagens implementado pelo dispatcher. Isso funciona muito parecido com a bomba de mensagem win32 familiar; na verdade, o dispatcher do WPF usa mensagens User32 para executar chamadas entre threads.
Há realmente dois conceitos fundamentais a serem entendidos ao discutir a simultaneidade no WPF: o dispatcher e a afinidade de thread.
Durante a fase de design do WPF, o objetivo era mover para um único thread de execução, mas um modelo não afinizado a threads. A afinidade de thread ocorre quando um componente usa a identidade do thread em execução para armazenar algum tipo de estado. A forma mais comum disso é usar o TLS (repositório local de thread) para armazenar o estado. A afinidade de thread requer que cada thread lógico de execução seja de propriedade de apenas um thread físico no sistema operacional, o que pode tornar-se com uso intensivo de memória. No final, o modelo de threading do WPF foi mantido sincronizado com o modelo de threading User32 existente de execução em um único thread com afinidade ao thread. O principal motivo para isso foi a interoperabilidade – sistemas como o OLE 2.0, a área de transferência e o Internet Explorer exigem execução com afinidade de thread único (STA).
Considerando que você tem objetos com threading STA, você precisa de uma maneira de se comunicar entre threads e validar que você está na thread correta. Aqui está o papel do dispatcher. O dispatcher é um sistema de expedição de mensagens básico, com várias filas priorizadas. Exemplos de mensagens incluem notificações de entrada brutas (movidas pelo mouse), funções de estrutura (layout) ou comandos de usuário (execute este método). Ao derivar de DispatcherObject, você cria um objeto CLR que tem comportamento STA e receberá um ponteiro para um dispatcher no momento da criação.
System.Windows.DependencyObject
Uma das principais filosofias arquitetônicas usadas na criação do WPF era a preferência por propriedades em vez de métodos ou eventos. As propriedades são declarativas e permitem que você especifique mais facilmente a intenção em vez de ação. Isso também dá suporte a um sistema controlado por modelos ou controlados por dados para exibir o conteúdo da interface do usuário. Essa filosofia teve o efeito pretendido de criar mais propriedades às quais você poderia associar, a fim de controlar melhor o comportamento de um aplicativo.
Para ter mais do sistema orientado por propriedades, era necessário um sistema de propriedades mais avançado do que o que o CLR fornece. Um exemplo simples dessa riqueza são as notificações de alteração. Para habilitar a associação bidirecional, você precisa dos dois lados da associação para dar suporte à notificação de alteração. Para ter o comportamento vinculado aos valores da propriedade, você precisa ser notificado quando o valor da propriedade for alterado. O Microsoft .NET Framework tem uma interface, INotifyPropertyChange, que permite que um objeto publique notificações de alteração, porém é opcional.
O WPF fornece um sistema de propriedades mais avançado, derivado do DependencyObject tipo. O sistema de propriedades é realmente um sistema de propriedades de "dependência", pois controla as dependências entre expressões de propriedade e revalida valores de propriedade automaticamente quando as dependências são alteradas. Por exemplo, se você tiver uma propriedade que herda (como FontSize), o sistema será atualizado automaticamente se a propriedade for alterada em um pai de um elemento que herda o valor.
A base do sistema de propriedades do WPF é o conceito de uma expressão de propriedade. Nesta primeira versão do WPF, o sistema de expressão de propriedade é fechado e as expressões são todas fornecidas como parte da estrutura. Expressões são por isso que o sistema de propriedades não tem associação de dados, estilo ou herança codificada, mas sim fornecida por camadas posteriores dentro da estrutura.
O sistema de propriedades também prevê armazenamento disperso de valores de propriedade. Como os objetos podem ter dezenas (se não centenas) de propriedades, e a maioria dos valores está em seu estado padrão (herdado, definido por estilos etc.), nem todas as instâncias de um objeto precisam ter o peso total de cada propriedade definida nele.
O novo recurso final do sistema de propriedades é a noção de propriedades anexadas. Os elementos do WPF são criados com base no princípio da composição e da reutilização de componentes. Geralmente, algum elemento contêiner (como um elemento de layout Grid) precisa de dados adicionais sobre os elementos filho para controlar seu comportamento (como as informações de linha/coluna). Em vez de associar todas essas propriedades a cada elemento, qualquer objeto tem permissão para fornecer definições de propriedade para qualquer outro objeto. Isso é semelhante aos recursos "expando" do JavaScript.
System.Windows.Media.Visual
Com um sistema definido, a próxima etapa é renderizar pixels na tela. A Visual classe fornece a criação de uma árvore de objetos visuais, cada uma opcionalmente contendo instruções de desenho e metadados sobre como renderizar essas instruções (recorte, transformação etc.). Visual foi projetado para ser extremamente leve e flexível, portanto, a maioria dos recursos não tem exposição à API pública e dependem muito de funções de retorno de chamada protegidas.
Visual é realmente o ponto de entrada para o sistema de composição do WPF. Visual é o ponto de conexão entre esses dois subsistemas, a API gerenciada e o milcore não gerenciado.
O WPF exibe dados percorrendo as estruturas de dados não gerenciadas gerenciadas pelo milcore. Essas estruturas, chamadas de nós de composição, representam uma árvore de exibição hierárquica com instruções de renderização em cada nó. Esta árvore, ilustrada no lado direito da figura abaixo, só pode ser acessada por meio de um protocolo de mensagens.
Ao programar o WPF, você cria Visual elementos e tipos derivados, que se comunicam internamente com a árvore de composição por meio desse protocolo de mensagens. Cada Visual no WPF pode criar um, nenhum ou vários nós de composição.
Há um detalhe de arquitetura muito importante a ser notado aqui – toda a árvore de visuais e instruções de desenho é armazenada em cache. Em termos gráficos, o WPF usa um sistema de renderização retido. Isso permite que o sistema repinte com altas taxas de atualização evitando bloqueios nas chamadas de retorno do código do usuário. Isso ajuda a impedir a aparência de um aplicativo sem resposta.
Outro detalhe importante que não é realmente perceptível no diagrama é como o sistema realmente executa a composição.
No User32 e GDI, o sistema opera em um sistema de recortagem de modo imediato. Quando um componente precisa ser renderizado, o sistema estabelece um limite de recorte fora do qual o componente não tem permissão para tocar nos pixels e, em seguida, o componente é solicitado a pintar pixels nessa caixa. Esse sistema funciona muito bem em sistemas com restrição de memória porque quando algo muda, você só precisa tocar no componente afetado– nenhum componente contribui para a cor de um único pixel.
O WPF usa um modelo de pintura "algoritmo do pintor". Isso significa que, em vez de cortar cada componente, cada componente é solicitado a renderizar de trás para a frente da tela. Isso permite que cada componente pinte sobre a tela do componente anterior. A vantagem desse modelo é que você pode ter formas complexas e parcialmente transparentes. Com o hardware gráfico moderno de hoje, esse modelo é relativamente rápido (o que não ocorreu quando o User32/GDI foi criado).
Conforme mencionado anteriormente, uma filosofia central do WPF é migrar para um modelo de programação mais declarativo e "centrado na propriedade". No sistema visual, isso aparece em alguns lugares interessantes.
Primeiro, se você considerar o sistema gráfico em modo retido, isso realmente está se afastando de um modelo imperativo do tipo DrawLine/DrawLine para um modelo orientado a dados – new Line()/new Line(). Essa mudança para a renderização controlada por dados permite que operações complexas nas instruções de desenho sejam expressas usando propriedades. Os tipos derivados de Drawing são efetivamente o modelo de objeto para renderização.
Em segundo lugar, se você avaliar o sistema de animação, verá que ele é quase completamente declarativo. Em vez de exigir que um desenvolvedor compute o próximo local ou a próxima cor, você pode expressar animações como um conjunto de propriedades em um objeto de animação. Essas animações podem expressar a intenção do desenvolvedor ou designer (mover esse botão daqui para lá em 5 segundos) e o sistema pode determinar a maneira mais eficiente de fazer isso.
System.Windows.UIElement
UIElement define os principais subsistemas, incluindo Layout, Entrada e Eventos.
Layout é um conceito principal no WPF. Em muitos sistemas, há um conjunto fixo de modelos de layout (HTML dá suporte a três modelos para layout; fluxo, absoluto e tabelas) ou nenhum modelo para layout (o User32 realmente dá suporte apenas ao posicionamento absoluto). O WPF começou com a suposição de que desenvolvedores e designers queriam um modelo de layout flexível e extensível, que poderia ser controlado por valores de propriedade em vez de lógica imperativa. UIElement No nível, o contrato básico para layout é introduzido – um modelo de duas fases com Measure e Arrange passa.
Measure permite que um componente determine quanto tamanho ele gostaria de ter. Essa é uma fase separada de Arrange porque há muitas situações em que um elemento pai solicitará que um filho meça várias vezes para determinar sua posição e tamanho ideais. O fato de os elementos pai solicitarem aos elementos filho que meçam demonstra outra filosofia chave do WPF – dimensionamento conforme o conteúdo. Todos os controles no WPF dão suporte à capacidade de dimensionar para o tamanho natural de seu conteúdo. Isso facilita muito a localização e permite o layout dinâmico de elementos conforme as coisas são redimensionada. A Arrange fase permite que um pai posicione e determine o tamanho final de cada filho.
Muitas vezes é gasto muito tempo falando sobre o lado de saída do WPF – Visual e objetos relacionados. No entanto, há uma enorme quantidade de inovação no aspecto do input também. Provavelmente, a alteração mais fundamental no modelo de entrada do WPF é o modelo consistente pelo qual os eventos de entrada são roteado por meio do sistema.
A entrada se origina como um sinal em um driver de dispositivo no modo kernel e é roteada para o processo e thread corretos por meio de um processo intrincado envolvendo o kernel do Windows e o User32. Depois que a mensagem User32 correspondente à entrada é roteada para o WPF, ela é convertida em uma mensagem de entrada bruta do WPF e enviada ao dispatcher. O WPF permite que eventos de entrada brutos sejam convertidos em vários eventos reais, permitindo que recursos como "MouseEnter" sejam implementados em um nível baixo do sistema com entrega garantida.
Cada evento de entrada é convertido em pelo menos dois eventos – um evento de "visualização" e o evento real. Todos os eventos no WPF têm uma noção de roteamento por meio da árvore de elementos. Diz-se que os eventos "fazem bolha" se eles atravessam do alvo até a raiz, e "fazem túnel" se eles começam na raiz e descem até um alvo. Túnel de eventos de visualização de entrada, permitindo a qualquer elemento na árvore uma oportunidade de filtrar ou tomar medidas no evento. Os eventos regulares, em seguida, propagam-se do destino até a raiz.
Essa divisão entre o túnel e a fase de bolha faz com que a implementação de recursos como aceleradores de teclado funcione de forma consistente em um mundo composto. No User32, você implementaria aceleradores de teclado com uma única tabela global contendo todos os aceleradores que você queria dar suporte (mapeamento Ctrl+N para "Novo"). No dispatcher do seu aplicativo, você chamaria TranslateAccelerator , que detectaria as mensagens de entrada no User32 e determinaria se algum deles correspondeu a um acelerador registrado. No WPF, isso não funcionaria porque o sistema é totalmente "componível" – qualquer elemento pode manipular e usar qualquer acelerador de teclado. Ter esse modelo de duas fases para entrada permite que os componentes implementem seu próprio "TranslateAccelerator".
Para levar este passo adiante, UIElement também apresenta a noção de CommandBindings. O sistema de comandos do WPF permite que os desenvolvedores definam a funcionalidade em termos de um ponto de extremidade de comando – algo que implementa ICommand. As associações de comando permitem que um elemento defina um mapeamento entre um gesto de entrada (Ctrl+N) e um comando (Novo). Os gestos de entrada e as definições de comando são extensíveis e podem ser conectados durante o uso. Isso torna trivial, por exemplo, permitir que um usuário final personalize as associações de chave que deseja usar em um aplicativo.
Até este ponto do tópico, os recursos fundamentais do WPF – aqueles implementados no assembly PresentationCore, têm sido o foco principal. Ao criar o WPF, uma separação limpa entre peças fundamentais (como o contrato de layout com Medida e Organização) e peças de estrutura (como a implementação de um layout específico como Grid) foi o resultado desejado. O objetivo era fornecer um ponto de extensibilidade baixo na pilha que permitiria aos desenvolvedores externos criar suas próprias estruturas, se necessário.
System.Windows.FrameworkElement
FrameworkElement pode ser analisado de duas maneiras diferentes. Ele apresenta um conjunto de políticas e personalizações nos subsistemas introduzidos em camadas inferiores do WPF. Ele também apresenta um conjunto de novos subsistemas.
A política primária introduzida por FrameworkElement envolve o layout do aplicativo. FrameworkElement baseia-se no contrato de layout básico introduzido por UIElement e adiciona a noção de um "slot" de layout que facilita para os autores de layout manterem um conjunto consistente de semântica de layout baseada em propriedades. Propriedades como HorizontalAlignment, VerticalAlignmente MinWidthMargin (para citar alguns) fornecem todos os componentes derivados de FrameworkElement um comportamento consistente dentro de contêineres de layout.
FrameworkElement também fornece uma exposição de API mais fácil a muitos recursos encontrados nas camadas principais do WPF. Por exemplo, FrameworkElement fornece acesso direto à animação por meio do BeginStoryboard método. Um Storyboard fornece uma maneira de escrever várias animações sobre um conjunto de propriedades.
As duas coisas mais críticas que FrameworkElement introduzem são associação de dados e estilos.
O subsistema de associação de dados no WPF deve ser relativamente familiar para qualquer pessoa que tenha usado o Windows Forms ou ASP.NET para criar uma interface do usuário (interface do usuário) do aplicativo. Em cada um desses sistemas, há uma maneira simples de expressar que você deseja que uma ou mais propriedades de um determinado elemento sejam associadas a um pedaço de dados. O WPF tem suporte completo para associação de propriedade, transformação e associação de lista.
Um dos recursos mais interessantes da associação de dados no WPF é a introdução de modelos de dados. Os modelos de dados permitem que você especifique declarativamente como um pedaço de dados deve ser visualizado. Em vez de criar uma interface do usuário personalizada que pode ser associada a dados, você pode, em vez disso, reverter o problema e permitir que os dados determinem a exibição que será criada.
O estilo é realmente uma forma leve de associação de dados. Usando o estilo, você pode associar um conjunto de propriedades de uma definição compartilhada a uma ou mais instâncias de um elemento. Os estilos são aplicados a um elemento por referência explícita (definindo a Style propriedade) ou implicitamente associando um estilo ao tipo CLR do elemento.
System.Windows.Controls.Control
O recurso mais significativo do controle é o uso de templates. Se você pensar no sistema de composição do WPF como um sistema de renderização de modo retido, a modelagem permitirá que um controle descreva sua renderização de maneira declarativa e parametrizada. Um ControlTemplate não é nada mais do que um script para criar um conjunto de elementos filho, com associações a propriedades oferecidas pelo controle.
Control fornece um conjunto de propriedades de estoque, Foreground, Background, Padding, para citar alguns, que os autores de templates podem usar para personalizar a exibição de um controle. A implementação de um controle fornece um modelo de dados e um modelo de interação. O modelo de interação define um conjunto de comandos (como Fechar para uma janela) e associações a gestos de entrada (como clicar no X vermelho no canto superior da janela). O modelo de dados fornece um conjunto de propriedades para personalizar o modelo de interação ou personalizar a exibição (determinada pelo modelo).
Essa divisão entre o modelo de dados (propriedades), o modelo de interação (comandos e eventos) e o modelo de exibição (modelos) permite a personalização completa da aparência e do comportamento de um controle.
Um aspecto comum do modelo de dados de controles é o modelo de conteúdo. Se você olhar para um controle como Button, verá que ele tem uma propriedade chamada "Conteúdo" do tipo Object. Nos Windows Forms e ASP.NET, essa propriedade normalmente seria uma cadeia de caracteres , no entanto, isso limita o tipo de conteúdo que você pode colocar em um botão. O conteúdo de um botão pode ser uma cadeia de caracteres simples, um objeto de dados complexo ou uma árvore de elementos inteira. No caso de um objeto de dados, o modelo de dados é usado para construir uma exibição.
Resumo
O WPF foi projetado para permitir que você crie sistemas de apresentação dinâmicos e controlados por dados. Cada parte do sistema é projetada para criar objetos por meio de conjuntos de propriedades que conduzem o comportamento. A associação de dados é uma parte fundamental do sistema e é integrada a cada camada.
Os aplicativos tradicionais criam uma exibição e, em seguida, associam-se a alguns dados. No WPF, tudo sobre o controle, todos os aspectos da exibição, é gerado por algum tipo de associação de dados. O texto encontrado dentro de um botão é exibido criando um controle composto dentro do botão e associando sua exibição à propriedade de conteúdo do botão.
Quando você começa a desenvolver aplicativos baseados em WPF, isso deve parecer bastante familiar. Você pode definir propriedades, usar objetos e associação de dados da mesma maneira que pode usar o Windows Forms ou ASP.NET. Com uma investigação mais profunda sobre a arquitetura do WPF, você descobrirá que existe a possibilidade de criar aplicativos muito mais avançados que tratam fundamentalmente os dados como o driver principal do aplicativo.
Consulte também
- Visual
- UIElement
- ICommand
- FrameworkElement
- DispatcherObject
- CommandBinding
- Control
- Visão Geral do Data Binding
- Layout
- Visão geral da animação
.NET Desktop feedback