Visão geral da personalização e criação de formulários com a Ferramenta de Criação do Service Manager
Um formulário é uma janela que possibilita aos usuários interagirem com objetos do banco de dados. Os usuários podem usar um formulário para exibir e editar as propriedades dos objetos. Cada formulário está vinculado a uma classe específica e exibe informações exclusivamente das instâncias da classe de destino. Um formulário contém campos. Normalmente, cada campo é associado a uma propriedade específica da classe de destino do formulário. O formulário de incidente, por exemplo, está vinculado ao objeto "incidente". Portanto, o formulário de incidente exibe informações sobre objetos de incidentes no banco de dados.
Um formulário do Service Manager consiste na implementação do formulário do Windows Presentation Foundation (WPF) em um assembly do Microsoft .NET Framework e uma definição de formulário em um pacote de gerenciamento do Service Manager. A definição de formulário especifica a classe que o formulário representa, junto com outras propriedades do formulário.
Conceitos-chave sobre formulários
Antes de personalizar formulários, você deve estar familiarizado com os conceitos de formulário a seguir.
Como os formulários são usados
Quando o pacote de gerenciamento que contém as definições de formulário é importado para Service Manager, as definições de formulário são armazenadas no banco de dados. Posteriormente, quando o usuário iniciar uma tarefa de console do Service Manager que requer a exibição de um objeto, Service Manager deverá encontrar um formulário para exibir o objeto solicitado. Service Manager acessa o banco de dados e procura um formulário que foi definido para esse objeto. Se nenhum formulário for definido para o objeto, Service Manager procurará um formulário definido para o objeto pai do objeto. Service Manager continua a pesquisar toda a hierarquia de herança do objeto até encontrar um formulário definido.
Formulários genéricos
Se Service Manager não conseguir encontrar nenhum formulário para o objeto ou para qualquer um de seus objetos pai, Service Manager criará dinamicamente um formulário genérico padrão para esse objeto. O formulário genérico é um formulário gerado pelo sistema que é suficiente para o uso de formulário simples. O formulário genérico representa uma maneira rápida e fácil de criar um formulário para objetos sem definições de formulário.
Por padrão, o formulário genérico exibe todas as propriedades do formulário em um layout simples que você não pode alterar. O formulário genérico exibe as propriedades de todos os objetos pai na hierarquia de herança do formulário e você não pode alterar esse comportamento. As personalizações para o formulário genérico são limitadas. Por exemplo, você pode especificar as propriedades que deseja que o formulário genérico exiba; no entanto, o formulário genérico não pode ser usado como base para personalização. Se você definir posteriormente um formulário personalizado para esse objeto, seu formulário personalizado substituirá o formulário genérico do objeto.
Para obter informações sobre como ocultar propriedades em um formulário genérico e outras maneiras de personalizar um formulário genérico, consulte a postagem no blog Overview of the Forms Infrastructure and the Generic Form (Visão geral da infraestrutura de formulário e do formulário genérico).
Classes de combinação em formulários
Às vezes, você precisa de um formulário para exibir informações que são derivadas de mais de uma classe. Para fazer isso, você cria uma classe de combinação e associa um campo no formulário a ela. Para obter mais informações sobre classes de combinação, consulte Alterações no esquema comum do System Center.
Aspectos funcionais de um formulário
Um formulário tem os seguintes aspectos funcionais:
Inicialização
Tamanho e local
Atualizar
Enviar alterações
Esses aspectos são descritos nas seções a seguir.
Inicialização
Durante a inicialização, a XAML (Extensible Application Markup Language) de um formulário é analisada e todos os controles no formulário são instanciados e carregados. O evento Loaded do formulário indica quando o formulário e todos os elementos contidos foram carregados. As operações de carregamento de dados são assíncronas. Portanto, a instância de destino pode não estar disponível quando o evento Loaded é gerado. Em vez disso, o evento DataContextChanged deve ser usado para notificação quando a instância de destino está definida para o formulário. O evento PropertyChanged da propriedade DataContext pode ser usado no lugar do evento DataContextChanged .
Recomendamos usar o evento Loaded para inicialização personalizada relacionada ao controle e, em seguida, usar os eventos DataContextChanged ou PropertyChanged na propriedade DataContext para inicialização personalizada relacionada à instância de destino.
Tamanho e local
Quando um formulário é exibido em uma janela pop-up, seu tamanho inicial é determinado com base nas propriedades Width, Height, MinWidth e MinHeight do formulário. Se essas propriedades não forem definidas para o formulário, o tamanho inicial do formulário será calculado com base em seu conteúdo.
Recomendamos definir essas propriedades da seguinte maneira:
Defina as propriedades Width e Height do formulário para especificar explicitamente o tamanho ideal. Considere definir essas propriedades para o valor Auto . Isso define a largura e a altura do formulário com base no tamanho do conteúdo.
Defina as propriedades MinWidth e MinHeight a fim de especificar a menor janela aceitável para o formulário. Se um usuário redimensionar a janela para um tamanho menor do que foi especificado, serão exibidas barras de rolagem para rolar até o conteúdo oculto do formulário.
Quando o formulário é hospedado dentro do host de formulários do Service Manager, o último tamanho e local usados são preservados para exibição subsequente desse formulário pelo mesmo usuário na mesma sessão de execução.
Atualizar
A instância de destino de um formulário pode ser alterada como resultado da execução de um comando Refresh no formulário. O manipulador desse comando busca novos dados do banco de dados. Quando os dados chegam, o valor da propriedade DataContext do formulário é definido como a nova instância de destino e o evento DataContextChanged é gerado.
Para diferenciar entre o evento DataContextChanged que foi gerado quando o formulário foi carregado pela primeira vez e o evento que foi gerado para manipular um comando Refresh , verifique a propriedade OldValue dos argumentos do evento que são transmitidas com o evento. Essa propriedade será null se o formulário já tiver sido inicializado.
Enviar alterações
A janela pop-up do host do formulário no Service Manager fornece botões para enviar alterações feitas no formulário e para fechar a janela pop-up.
Quando um usuário seleciona o botão Aplicar para um formulário, a instância de destino do formulário é enviada para armazenamento. Esta operação é síncrona; portanto, o usuário não pode editar o formulário até que a operação de envio seja concluída. Se ocorrer uma falha durante o envio do formulário, será exibida uma mensagem de erro. O formulário permanece aberto para outras alterações. Recomendamos que os usuários apliquem suas alterações com frequência para evitar colisões de outra instância do formulário estiver sendo editada ao mesmo tempo.
Se o usuário selecionar o botão OK , o comportamento será semelhante a Aplicar, exceto que, se a operação de envio do formulário for bem-sucedida, o formulário e sua janela host serão fechados.
Se o usuário selecionar o botão Cancelar , será exibida uma caixa de diálogo solicitando que o usuário confirme a operação. O usuário pode selecionar Sim e perder as alterações ou selecionar Não e retornar ao formulário.
Diretrizes gerais e práticas recomendadas para formulários
Você pode estender os recursos do Service Manager adicionando ou modificando formulários. Esta seção descreve algumas recomendações de práticas recomendadas para criar e usar formulários do Service Manager, usando várias ferramentas e definições de formulário de script diretamente.
Esta seção destina-se principalmente a parceiros e clientes com experiência na criação de seus próprios formulários personalizados usando o Windows Presentation Foundation (WPF) e o Microsoft Visual Studio Team System ou o Microsoft Expression Blend.
As diretrizes gerais para a criação de um novo formulário são as seguintes.
- Use controles padrão.
- Siga as diretrizes de design de forma geral.
- Evite o code-behind.
- Inclua tratamento de exceção.
- Considere a possibilidade de personalização e atualizações de formulários.
- Nomeie todos os controles personalizáveis.
- Associe o formulário a Fontes de Dados.
- Use Service Manager regras de validação de infraestrutura, convertores de valor e modelos de erro.
- Use os comandos de infraestrutura de formulários e eventos.
Para obter informações sobre essas diretrizes, consulte as seções a seguir.
Usar controles padrão
Controles que são usados em um formulário podem ser:
- Controles padrão. Isso inclui controles da biblioteca .NET, como caixa de combinação e caixa de listagem.
- Controles personalizados. Isso inclui controles adicionais que são criados pelo autor do formulário ou por terceiros.
Dica
Quando controles padrão são usados sempre que possível e a criação de controles personalizados é evitada, a consistência ocorre relacionada à experiência do usuário de formulários. Se for necessário criar um controle personalizado, separe a aparência e o comportamento visual do comportamento lógico usando modelos de controle para definir a aparência do mesmo. Preferencialmente, deve haver um modelo de controle separado para cada tema do Windows.
Siga as diretrizes gerais de design de formulário
Quando um formulário for planejado, as diretrizes de design públicas deverão ser usadas para assegurar que o formulário seja de fácil utilização e se una aos paradigmas de interação de usuários comuns.
Para mais informações sobre o design geral do Windows, acesse Windows User Experience Interaction Guidelines (Diretrizes de interação de experiência do usuário do Windows).
Além disso:
- Divida as informações entre várias guias para tornar o formulário mais simples e fácil de ler. Inclua as informações mais usadas na primeira guia e as informações de menor importância nas guias subsequentes.
- Use painéis de layout para dispor controles no formulário. Isso garante que o formulário se comporte corretamente quando for redimensionado e localizado.
- Evite configurar propriedades visuais de controle individual e use os estilos. Isso possibilita que você altere a aparência de todos os controles em uma série de formulários modificando o estilo e promove uma aparência consistente em formulários relacionados.
Evite code-behind
Code-behind é um termo que descreve o código unido a objetos definidos com marcação quando uma página XAML é compilada com marcação. Limite o uso do code-behind em um formulário tanto quanto possível. É preferível que você insira o código de um formulário no próprio controle, porque mais tarde é mais fácil alterar esse código. Em vez disso, use os recursos declarativos compatíveis com a infraestrutura de formulários do Service Manager para definir conversões de valor e regras de validação no formulário.
Como diretriz geral, você deve limitar o uso do code-behind a situações em que não é possível fornecer a funcionalidade necessária usando os recursos declarativos do XAML, com classes definidas no WPF e na biblioteca de infraestrutura de formulários. Mesmo então, considere mover a funcionalidade que está implementada no code-behind em uma biblioteca de ajuda e a confirme do XAML.
Incluir tratamento de exceções
Verifique se o código no formulário contém tratamento de exceção para que o formulário possa ser carregado durante a fase de design na Ferramenta de Criação e no console do Service Manager em tempo de execução.
Considere a personalização e as atualizações de formulários
Ao criar um novo formulário, você deve considerar personalizações e atualizações futuras para esse formulário. Para garantir que seja possível personalizar e atualizar um formulário preservando as personalizações, siga as diretrizes e dicas fornecidas anteriormente nesta seção, juntamente com as seguintes diretrizes:
Considere personalizações e atualizações futuras antecipadamente enquanto estiver projetando o formulário. É provável que os formulários evoluam em versões futuras, e é importante considerar como os usuários poderão atualizar para novas versões do formulário, preservando suas personalizações para o formulário original. Por exemplo, é preciso fornecer um formulário de atualização após os usuários já tiverem feito grande investimento em personalizar o seu formulário original. Os usuários esperam que suas personalizações sobrevivam à atualização de versão.
Providencie um nome único para cada controle no formulário, para que seja possível que personalizações sejam aplicadas aos controles. Personalizações de formulários são armazenadas como um conjunto de ações que são destinadas a um controle específico ou a um conjunto de controles. O controle de destino é referenciado por nome, e é por isso que é importante preservar os nomes de controle entre as versões do formulário. Se um controle não tiver um nome, o Editor de Personalização de Formulário gerará um nome, mas o nome gerado não será preservado em diferentes versões do formulário.
Verifique se os nomes de controle permanecem imutáveis em diferentes versões do formulário. Isso assegura que as personalizações para um dado controle em uma versão anterior pode ser aplicada ao mesmo controle em uma nova versão do formulário.
Se possível, evite mover os controles para um local diferente na mesma guia quando atualizar um formulário. Uma personalização do usuário comum é mover os controles no formulário para um local diferente. Se você alterar o local de um controle em uma nova versão do formulário, haverá o risco de que o novo local de controle possa se sobrepor a um controle que o usuário realocou.
Se possível, evite mover controles entre guias ao criar uma atualização para um formulário existente. Os controles são identificados pelo nome e pela guia na qual estão localizados. Mover um controle de uma guia para outra em uma nova versão do formulário pode danificar as personalizações que o usuário fez para aquele controle, porque a personalização irá falhar em identificar o controle de destino.
Quando a atualização de um formulário incluir novos controles, considere adicionar os novos controles a uma nova guia. Essa é a maneira mais segura de evitar interferir em qualquer personalização do usuário nas guias e controles existentes.
Lembre-se de como os controles são limitados. Controles somente leitura devem usar somente a associação unidirecional.
Nomeie todos os controles personalizáveis
Assegure que todos os nomes de controle descrevem a qual dado o controle está limitado, ou descreva qual a função do controle.
Vincular o formulário a fontes de dados
A principal finalidade de um formulário é visualizar um único objeto do banco de dados do Service Manager. Esse objeto é chamado de um target instance, que é sempre especificado pelas propriedades de DataContext de um formulário (que é herdado da classe FrameworkElement ).
Importante
Não modifique a propriedade DataContext do formulário. Os formulários que hospedam o ambiente usam esta propriedade para identificar a instância de destino do formulário.
No modelo de dados do Service Manager, uma instância de destino é representada como um objeto BindableDataItem . Essa classe agrega o objeto do kit de desenvolvimento de software sublinhado (SDK) e expõe suas propriedades por um indexador, que toma um nome apropriado como parâmetro.
A classe BindableDataItem também implementa ICustomTypeDescriptor, o que possibilita usar a classe BindableDataItem como fonte de dados para a associação do WPF. A seguir está um exemplo de associação de uma instância de destino para a propriedade de Text de um controle de TextBox :
<TextBox Name="textBoxDescription" Text="{Binding Path=Summary}"/>
Não é necessário especificar a Origem da associação porque as instâncias de destino são definidas como o DataContext do formulário, que serve como a Origem padrão para todos os controles no formulário.
Os controles no formulário podem ser associados a fontes de dados diferentes da instância de destino, e a biblioteca de infraestrutura de formulários contém muitos controles que executam a associação implicitamente. Por exemplo, o controle seletor de instância está limitado à fonte de dados, que fornece a coleção de instâncias de escolha. Também é possível definir fontes de dados adicionais declarativamente usando as classes ObjectDataProvider e XmlDataProvider .
A infraestrutura de formulários considera a instância de destino como a única fonte de dados de leitura/gravação no formulário. Então, a implementação do comando de Submit armazenará as mudanças que são feitas para a instância de destino. Outras fontes de dados para o formulário são tratadas como somente leitura.
Usar regras de validação de infraestrutura de formulários do Service Manager, conversores de valor e modelos de erro
Recomendamos que você use regras de validação de infraestrutura de formulários em formulários para designar a entrada de dados que não é válida. A validação de suporte de infraestrutura de associação do WPF para propriedade de controle que estão limitadas a fonte de dados com suas associações de uma via ou duas vias. O objeto de associação possui uma coleção de ValidationRules que pode conter qualquer número de objetos de ValidationRule . Sempre que dados forem retirados do controle para a fonte de dados, os objetos de ValidationRule são chamados para validar os valores.
A biblioteca de infraestrutura de formulários contém muitas regras de validação que lidam com os casos mais comuns. A infraestrutura de formulários toma vantagem das regras de validação sempre que os conteúdos de formulário podem ser enviados para armazenagem. Por exemplo, o botão Enviar de um formulário pode ser desabilitado se houver um controle que tenha um erro de validação no formulário.
Recomendamos que você use o modelo de erro personalizado que é fornecido com a biblioteca de infraestrutura de formulários. Se um controle tiver um erro de validação, ele será exibido por padrão com uma borda vermelha ao redor dele. O WPF torna possível definir um indicador de erro personalizado pela propriedade do Validation.ErrorTemplate , que pode ser configurado em qualquer controle. A biblioteca de infraestrutura de formulários do Service Manager contém um modelo de erro personalizado, que exibe um ícone de erro em vez da borda vermelha do WPF. Além disso, quando o mouse aponta para o ícone de erro, uma dica de ferramenta surge com uma mensagem de erro. A mensagem de erro deve indicar o motivo pela qual os dados no controle falharam na validação.
O exemplo a seguir mostra como referenciar o modelo de erro em XAML:
<TextBox Text="{Binding SomeProperty}"
scwpf:Validation.ValueRequired="True"
Validation.ErrorTemplate="{DynamicResource {ComponentResourceKey {x:Type scwpf:Validation}, InvalidDataErrorTemplate}}"/>
Se as regras de validação internas não fornecerem a lógica de validação necessária, recomendamos que você crie regras de validação personalizadas para representar essa lógica. Isso tornará possível para a lógica de validação personalizada coexistir com os mecanismos de manuseio de validação comum.
Se o mecanismo de regras de validação não for adequado para um cenário específico, você deverá manipular FormEvents.PreviewSubmitEvent e executar a validação a partir daí.
O seguinte exemplo de código fornece um exemplo do padrão que pode ser usado para executar a validação personalizada:
void MyForm_Loaded(object sender, RoutedEventArgs e)
{
// hook to handle form events
this.AddHandler(
FormEvents.PreviewSubmitEvent,
new EventHandler<PreviewFormCommandEventArgs>(this.OnPreviewSubmit));
}
private void OnPreviewSubmit(object sender, PreviewFormCommandEventArgs e)
{
string errorMessage;
bool result = this.DoVerify(out errorMessage);
if (!result)
{
// cancel Submit operation
e.Cancel = true;
// display error message
MessageBox.Show(errorMessage);
}
}
internal bool DoVerify(out string errorMessage)
{
// Do custom verification and return true to indicate that
// validation check has passed; otherwise return false and
// populate errorMessage argument
}
Usar comandos e eventos de infraestrutura de formulário
A infraestrutura de formulário expõe muitos comandos que podem ser executados em um formulário. Esses comandos incluem:
FormsCommand.Submit, que salva a instância de destino do formulário.
FormsCommand.SubmitAndClose, que salva a instância de destino do formulário e fecha o formulário.
FormsCommand.Refresh, que repete a consulta para a instância de destino do formulário.
FormCommands.Cancel, que descarta todas as alterações e fecha o formulário.
Cada um desses comandos é agrupado por eventos que são disparados antes e após o comando ser executado.
Antes do comando, os seguintes eventos são gerados:
O evento de FormEvents.PreviewSubmit é gerado antes do comando de FormCommand.Submit , e o evento de FormEvents.Submitted é gerado após o comando de FormCommand.Submit .
O evento de FormEvents.PreviewRefresh é gerado antes do comando de FormCommands.Refresh , e o evento de FormCommand.Refreshed é gerado após o comando de FormCommand.Submit .
O evento de FormEvents.PreviewCancel é gerado antes do comando de FormCommands.Cancel , e o evento de FormCommand.Canceled é gerado após o comando de FormCommand.Cancel .
Os eventos anteriores transmitem um objeto de PreviewFormCommandEventArgs . O objeto contém uma propriedade mutável Cancel que irá prevenir o comando correspondente de ser executado quando a propriedade estiver configurada para true.
Os eventos pós-comando transmitem um objeto de FormCommandExecutedEventArgs . Esse objeto contém propriedades de Result que indica se a execução do comando foi bem sucedida, cancelada ou causou um erro. No caso de erro, a propriedade de Error do objeto de FormCommandExecutedEventArgs refere-se à exceção que fornece informação sobre o erro.
É possível habilitar, desabilitar e executar comandos de formulário programaticamente e declarativamente.
Para habilitar os comandos de formulários programáticos, estabeleça um CommandBinding entre o formulário e o comando relacionado.
No seguinte exemplo, um comando associado está estabelecido entre o formulário e um comando de Refresh , e dois manipuladores são definidos para esse comando. O primeiro manipulador retorna se ou não o comando de Refresh pode ser executado, e o segundo manipulador contém a implementação do comando de Refresh :
public class MyForm : UserControl
{
public MyForm()
{
// do standard initialization
// establish CommandBinding for Refresh command
this.CommandBindings.Add(
new CommandBinding(FormCommands.Refresh, this.ExecuteRefresh, this.CanExecuteRefresh));
}
private void CanExecuteRefresh(
object sender,
CanExecuteRoutedEventArgs e)
{
// put your logic that determines whether Refresh
// can be executed here
bool canExecute = true;
BindableDataItem dataItem = this.DataContext as BindableDataItem;
if (dataItem)
{
canExecute = dataItem["Status"] != "New";
}
e.CanExecute = canExecute;
}
private void ExecuteRefresh(
object sender,
ExecutedRoutedEventArgs e)
{
// here is placeholder for the code that has do be
// executed upon running Refresh command
}
}
Também é possível definir manipuladores de comandos de formulário declarativamente. Isso é possível empregando um objeto de Regra que usa um RoutedCommandTrigger. O exemplo de código a seguir mostra como definir manipuladores declarativamente:
<scwpf:BusinessLogic.Rules>
<scwpf:RuleCollection>
<scwpf:Rule>
<scwpf:Rule.Triggers>
<scwpf:RoutedCommandTrigger
RoutedCommand="{x:Static scwpf:FormCommands.Refresh}"/>
</scwpf:Rule.Triggers>
<scwpf:Rule.Conditions>
<scwpf:PropertyMatchCondition
Binding="{Binding Status}"
Value="New"
Operation="NotEquals" />
</scwpf:Rule.Conditions>
<!-- Use RuleAction objects to define the logic that executed
upon running Refresh command; this can be left empty -->
</scwpf:Rule>
</scwpf:RuleCollection>
</scwpf:BusinessLogic.Rules>
Próximas etapas
- Personalize e crie formulários com a Ferramenta de Criação do Service Manager.
- Personalize o formulário de incidente padrão para o cenário de exemplo da Ferramenta de Criação.