Información general sobre la personalización y creación de formularios con la herramienta de creación de Service Manager
Un formulario es una ventana que permite a los usuarios interactuar con los objetos de la base de datos. Los usuarios pueden usar un formulario para ver y editar las propiedades de los objetos. Cada formulario está asociado a una clase específica y muestra información solo para las instancias de la clase de destino. Un formulario contiene campos. Por lo general, cada campo está enlazado a una propiedad específica de la clase de destino del formulario. El formulario de incidente, por ejemplo, está vinculado al objeto incidente. Por lo tanto, el formulario de incidente muestra información sobre los objetos incidente en la base de datos.
Un formulario de Service Manager consta de la implementación de formularios de Windows Presentation Foundation (WPF) en un ensamblado de Microsoft .NET Framework y una definición de formulario en un módulo de administración de Service Manager. La definición del formulario especifica la clase que representa el formulario, junto con las demás propiedades del formulario.
Conceptos clave sobre los formularios
Antes de personalizar los formularios, debes familiarizarte con los siguientes conceptos de formulario.
Cómo se usan los formularios
Cuando el módulo de administración que contiene las definiciones del formulario se importa en Service Manager, las definiciones del formulario se almacenan en la base de datos. Más adelante, cuando el usuario inicia una tarea de consola de Service Manager que requiere la presentación de un objeto, Service Manager debe encontrar un formulario para mostrar el objeto solicitado. Service Manager accede a la base de datos y busca un formulario definido para ese objeto. Si no se define ningún formulario para el objeto, Service Manager busca un formulario definido para el objeto primario del objeto. Service Manager continúa buscando en toda la jerarquía de herencia del objeto hasta encontrar un formulario definido.
Formularios genéricos
Si Service Manager no encuentra ningún formulario para el objeto o para ninguno de sus objetos primarios, Service Manager compila dinámicamente un formulario genérico predeterminado para ese objeto. El formulario genérico es un formulario generado por el sistema que es suficiente para un uso sencillo. El formulario genérico representa una manera rápida y sencilla de crear un formulario para objetos sin definiciones de formulario.
De forma predeterminada, el formulario genérico muestra todas las propiedades del formulario en un diseño simple que no se puede cambiar. El formulario genérico muestra las propiedades de todos los objetos primarios de la jerarquía de herencia del formulario y no puedes cambiar ese comportamiento. Las personalizaciones del formulario genérico están limitadas. Por ejemplo, puedes especificar las propiedades que deseas que se muestren en el formulario genérico; sin embargo, el formulario genérico no se puede usar como base para la personalización. Si más adelante defines un formulario personalizado para ese objeto, el formulario personalizado sobrescribe el formulario genérico del objeto.
Para obtener información sobre cómo ocultar las propiedades en un formulario genérico y otras formas de personalizar un formulario genérico, consulta la entrada del blog Overview of the Forms Infrastructure and the Generic Form.
Clases de combinación en los formularios
A veces, necesitas un formulario para mostrar la información derivada de más de una clase. Para ello, crea una clase de combinación y, a continuación, enlaza un campo en el formulario a la clase de combinación. Para obtener más información sobre las clases de combinación, consulta Cambios en el esquema común de System Center.
Aspectos funcionales de un formulario
Un formulario tiene los siguientes aspectos funcionales:
Inicialización
Tamaño y ubicación
Actualizar
Enviar cambios
Dichos aspectos se describen en las secciones siguientes.
Inicialización
Durante la inicialización, se analiza el lenguaje de marcado extensible de aplicación (XAML) de un formulario y se crean instancias y se cargan todos los controles del formulario. El evento Loaded del formulario indica cuándo se han cargado el formulario y todos los elementos contenidos. Las operaciones de carga de los datos son asincrónicas. Por lo tanto, es posible que la instancia de destino no esté disponible cuando se genere el evento Loaded. En su lugar, el evento DataContextChanged debe usarse para la notificación cuando se establece la instancia de destino para el formulario. El evento PropertyChanged de la propiedad DataContext se puede usar en lugar del evento DataContextChanged.
Se recomienda usar el evento Loaded para la inicialización personalizada relacionada con el control y, a continuación, usar los eventos DataContextChanged o PropertyChanged en la propiedad DataContext para la inicialización personalizada relacionada con la instancia de destino.
Tamaño y ubicación
Cuando se muestra un formulario en una ventana emergente, su tamaño inicial se determina en función de las propiedades Width, Height, MinWidth y MinHeight. Si estas propiedades no se establecen para el formulario, el tamaño inicial del formulario se calcula en función de su contenido.
Se recomienda establecer estas propiedades de la siguiente manera:
Establece las propiedades Width y Height del formulario para especificar explícitamente el tamaño ideal. Considera la posibilidad de establecer el valor de estas propiedades en Automático. Así, se establece el ancho y el alto del formulario en función del tamaño de su contenido.
Establece las propiedades MinWidth y MinHeight del formulario para especificar la ventana más pequeña aceptable para el formulario. Si un usuario reduce el tamaño de la ventana a un valor menor que el especificado, aparecen barras de desplazamiento para desplazarse al contenido del formulario oculto.
Cuando el formulario se hospeda en el host de formularios de Service Manager, el tamaño y la ubicación usados se conservan para la presentación posterior de ese formulario por el mismo usuario en una misma sesión de ejecución.
Actualizar
La instancia de destino de un formulario puede cambiar como resultado de ejecutar un comando Refresh en el formulario. El controlador de este comando captura los datos nuevos de la base de datos. Cuando llegan los datos, el valor de la propiedad DataContext del formulario se establece en la nueva instancia de destino y se genera el evento DataContextChanged.
Para diferenciar entre el evento DataContextChanged que se generó cuando se cargó por primera vez el formulario y el evento que se generó para controlar un comando Refresh, comprueba la propiedad OldValue de los argumentos de evento que se pasan con el evento. Esta propiedad es null si el formulario se acaba de inicializar.
Enviar cambios
La ventana emergente del host de formulario en Service Manager proporciona botones para enviar los cambios realizados en el formulario y para cerrar la ventana emergente.
Cuando un usuario selecciona el botón Aplicar para un formulario, la instancia de destino del formulario se envía para el almacenamiento. Esta operación es sincrónica; por lo tanto, el usuario no puede editar el formulario hasta que se complete la operación de envío. Si se produce un error durante el envío del formulario, aparece un mensaje de error. El formulario permanece abierto para realizar cambios adicionales. Se recomienda que los usuarios apliquen sus cambios con frecuencia para evitar colisiones si se edita otra instancia del formulario al mismo tiempo.
Si el usuario selecciona el botón Aceptar, el comportamiento es similar a Aplicar, salvo que, si la operación de envío del formulario se realiza correctamente, se cierran el formulario y su ventana del host.
Si el usuario selecciona el botón Cancelar, aparece un cuadro de diálogo que pide al usuario que confirme la operación. El usuario puede ora seleccionar Sí y perder cambios, ora seleccionar No y volver al formulario.
Instrucciones generales y procedimientos recomendados en materia de formularios
Puedes ampliar las características de Service Manager agregando o modificando los formularios. En esta sección, se describen algunas recomendaciones en materia de los procedimientos recomendados para crear y usar formularios de Service Manager mediante diversas herramientas y definiciones de formulario de scripting directamente.
Esta sección está dirigida principalmente a los asociados y clientes que tienen experiencia en crear sus propios formularios personalizados mediante Windows Presentation Foundation (WPF) y Microsoft Visual Studio Team System o Microsoft Expression Blend.
Las directrices generales para crear un nuevo formulario son las siguientes.
- Usa los controles estándar.
- Sigue las directrices generales de diseño de formularios.
- Evita el código subyacente.
- Incluye el control de excepciones.
- Considera la posibilidad de personalizar y actualizar los formularios.
- Asigna un nombre a todos los controles personalizables.
- Enlaza el formulario a los orígenes de datos.
- Usa las reglas de validación de la infraestructura de los formularios de service Manager, convertidores de valores y plantillas de error.
- Usa los eventos y comandos de infraestructura de formularios.
Para obtener más información acerca de estas directrices, consulta las siguientes secciones:
Uso de los controles estándar
Los controles que se usan en un formulario pueden ser:
- Controles estándar. Esto incluye los controles de la biblioteca de .NET, por ejemplo, el cuadro combinado y el cuadro de lista.
- Controles personalizados. Esto incluye controles adicionales creados por el autor del formulario o por un tercero.
Sugerencia
Cuando usas controles estándar siempre que sea posible y evitas la creación de controles personalizados, favoreces la coherencia con respecto a la experiencia del usuario con los formularios. Si debes crear un control personalizado, separa la apariencia visual, el comportamiento y el comportamiento lógico mediante plantillas de control para definir la apariencia del control. Lo ideal es que haya una plantilla de control independiente para cada tema de Windows.
Sigue las directrices generales de diseño de formularios
Al diseñar un formulario, sigue directrices de diseño públicas para asegurarte de que el formulario sea fácil de usar y que cumpla los paradigmas comunes de interacción del usuario.
Para obtener más información sobre el diseño general de Windows, consulta las Directrices de interacción de la experiencia del usuario de Windows.
Además:
- Divide la información entre varias pestañas para que el formulario sea más sencillo y fácil de leer. Incluye la información más usada en la primera pestaña y la información de menor importancia en las pestañas siguientes.
- Diseña los controles del formulario con los paneles de diseño. Esto garantiza el comportamiento correcto del formulario al cambiarle el tamaño y localizarlo.
- Evita establecer propiedades visuales de control individuales y usa estilos en su lugar. Esto permite cambiar la apariencia de todos los controles de una serie de formularios modificando el estilo y facilita que los formularios relacionados tengan una apariencia coherente.
Evita el código subyacente
El código subyacente es el código que se une con objetos definidos por marcado cuando se compila una página XAML mediante marcado. Limita el uso de código subyacente en un formulario tanto como sea posible. Es preferible insertar el código para un formulario en el propio control, ya que después es más fácil cambiar ese código. En su lugar, usa las funcionalidades declarativas admitidas por la infraestructura de formularios de Service Manager para definir conversiones de valor y reglas de validación en el formulario.
Como guía general, debes limitar el uso de código subyacente a situaciones en las que no sea posible proporcionar la funcionalidad necesaria mediante las funcionalidades declarativas de XAML, con clases definidas en WPF y la biblioteca de infraestructura de formularios. Incluso entonces, piensa en la posibilidad de mover la funcionalidad que se implementa en código subyacente a una biblioteca auxiliar y, después, hacer referencia a ella desde el XAML.
Incluye un control de excepciones
Asegúrate de que el código del formulario contiene el control de excepciones para que se pueda cargar en la fase de diseño de Authoring Tool y en la consola de Service Manager durante su ejecución.
Plantéate personalizar y actualizar los formularios
Al diseñar un nuevo formulario, debes tener en cuenta las futuras personalizaciones y actualizaciones del formulario. Para asegurarte de que se puede personalizar y actualizar un formulario, conservando las personalizaciones, sigue las instrucciones y sugerencias que se proporcionan anteriormente en esta sección, junto con las siguientes directrices:
Ten en cuenta las futuras personalizaciones y actualizaciones en las fases tempranas del diseño del formulario. Es probable que los formularios evolucionen en versiones futuras y es importante tener en cuenta cómo podrán actualizar los usuarios a las nuevas versiones sin perder sus personalizaciones del formulario original. Por ejemplo, puedes proporcionar un formulario actualizado después de que los usuarios ya hayan invertido mucho en personalizar el formulario original. Los usuarios esperan que sus personalizaciones sobrevivan a la actualización de la versión.
Dale un nombre único a cada control del formulario para permitir que las personalizaciones se apliquen a los controles. Las personalizaciones del formulario se almacenan como un conjunto de acciones destinadas a un control específico o a un conjunto de controles. Al control de destino se hace referencia por nombre, por eso es importante conservar los nombres de los controles entre las versiones del formulario. Si un control no tiene un nombre, el Editor de personalización de formularios lo generará, pero este nombre no se conserva a través de las diferentes versiones del formulario.
Asegúrate de que los nombres de los controles permanecen inmutables en las diferentes versiones del formulario. Esto garantiza que las personalizaciones de un control determinado de una versión anterior se puedan aplicar al mismo control en una nueva versión del formulario.
Si es posible, evita mover controles a otra ubicación de la misma pestaña al actualizar un formulario. Mover controles del formulario a una ubicación diferente es una personalización de usuario común. Si cambias la ubicación de un control en una nueva versión del formulario, existe el riesgo de que la nueva ubicación del control se superponga con un control que el usuario haya reubicado.
Si es posible, evita mover controles entre pestañas al diseñar una actualización de un formulario existente. Los controles se identifican por nombre y por la pestaña en la que se encuentran. Al mover un control de una pestaña a otra en una nueva versión del formulario se pueden interrumpir las personalizaciones que realiza el usuario en ese control, ya que las personalizaciones no podrán identificar el control de destino.
Cuando la actualización de un formulario incluya nuevos controles, piensa en la posibilidad de agregar los nuevos controles a una nueva pestaña. Esa es la forma más segura de evitar interferir con las personalizaciones de usuario a las pestañas y controles existentes.
Ten en cuenta cómo están enlazados los controles. Los controles de solo lectura deben usar enlaces unidireccionales.
Asignar un nombre a todos los controles personalizables
Asegúrate de que los nombres de los controles describen los datos a los que están enlazados los controles o lo que hacen los controles.
Enlaza el formulario a los orígenes de los datos
El propósito principal de un formulario es visualizar un único objeto de la base de datos de Service Manager. Este objeto es una instancia de destino y siempre se especifica mediante la propiedad DataContext de un formulario (que se hereda de la clase FrameworkElement).
Importante
No modifiques la propiedad DataContext del formulario. El entorno de hospedaje de formularios identifica la instancia de destino del formulario con esta propiedad.
En el modelo de datos de Service Manager, una instancia de destino se representa como un objeto BindableDataItem. Esta clase agrega el objeto del kit de desarrollo de software (SDK) subyacente y expone sus propiedades a través de un indizador, el cual toma un nombre de propiedad como parámetro.
La clase BindableDataItem también implementa ICustomTypeDescriptor, lo que permite usar la clase BindableDataItem como origen de datos para el enlace WPF. A continuación se muestra un ejemplo de enlace de una propiedad de instancia de destino a la propiedad Texto de un control TextBox:
<TextBox Name="textBoxDescription" Text="{Binding Path=Summary}"/>
No es necesario especificar el Origen del enlace porque las instancias de destino se establecen como DataContext del formulario, que actúa como Origen predeterminado para todos los controles del formulario.
Los controles del formulario se pueden enlazar a orígenes de datos distintos de la instancia de destino y la biblioteca de infraestructura de formularios contiene muchos controles que realizan el enlace implícitamente. Por ejemplo, el control selector de instancias está enlazado al origen de datos, que contiene la colección de instancias que se van a elegir. También es posible definir orígenes de datos adicionales de forma declarativa mediante las clases ObjectDataProvider y XmlDataProvider.
La infraestructura de formularios considera la instancia de destino como el único origen de datos de lectura y escritura del formulario. Por lo tanto, la implementación del comando Enviar solo almacenará los cambios realizados en la instancia de destino. Otros orígenes de datos del formulario se tratan como de solo lectura.
Uso de reglas de validación de infraestructura de formularios de Service Manager, convertidores de valores y plantillas de error
Se recomienda usar reglas de validación de infraestructura de formularios en formularios para designar entradas de datos que no sean válidas. La infraestructura de enlace de WPF admite la validación de las propiedades de control enlazadas a un origen de datos con enlaces unidireccionales o bidireccionales. El objeto de enlace tiene una colección ValidationRules que puede contener cualquier número de objetos ValidationRule. Cada vez que los datos se insertan desde el control al origen de datos, se llama a los objetos ValidationRule para validar el valor.
La biblioteca de infraestructura de formularios contiene muchas reglas de validación que controlan los casos más habituales. La infraestructura de formularios aprovecha las reglas de validación para determinar si se puede enviar el contenido del formulario para almacenarlo. Por ejemplo, el botón Enviar de un formulario se puede deshabilitar si hay un control que tiene un error de validación en el formulario.
Se recomienda usar la plantilla de errores personalizada que viene con la biblioteca de infraestructura de formularios. Si un control tiene un error de validación, aparece de forma predeterminada con un borde rojo alrededor de este. WPF permite definir un indicador de error personalizado a través de la propiedad Validation.ErrorTemplate, que se puede establecer en cualquier control. La biblioteca de infraestructura de formularios de Service Manager contiene una plantilla de errores personalizada, que muestra un icono de error en lugar del borde rojo de WPF. Además, cuando un ratón apunta al icono de error, aparece una información sobre herramientas con un mensaje de error. El mensaje de error debe indicar el motivo por el que los datos del control no pudieron validarse.
En el ejemplo siguiente se muestra cómo hacer referencia a la plantilla de errores en XAML:
<TextBox Text="{Binding SomeProperty}"
scwpf:Validation.ValueRequired="True"
Validation.ErrorTemplate="{DynamicResource {ComponentResourceKey {x:Type scwpf:Validation}, InvalidDataErrorTemplate}}"/>
Si las reglas de validación integradas no proporcionan la lógica de validación necesaria, se recomienda crear reglas de validación personalizadas para representar esa lógica. Esto hará posible que la lógica de validación estándar y personalizada coexista dentro del mecanismo de control de validación común.
Si el mecanismo de reglas de validación no es adecuado para un escenario determinado, debes controlar FormEvents.PreviewSubmitEvent y ejecutar la validación desde allí.
En el ejemplo de código siguiente se proporciona un ejemplo del patrón que se puede usar para ejecutar la validación 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
}
Uso de comandos y eventos de infraestructura de formularios
La infraestructura de formularios contiene muchos comandos que se pueden ejecutar en un formulario. Entre estos comandos se incluyen:
FormsCommand.Submit, que guarda la instancia de destino del formulario.
FormsCommand.SubmitAndClose, que guarda la instancia de destino del formulario y lo cierra.
FormsCommand.Refresh, que repite la consulta para la instancia de destino del formulario.
FormCommands.Cancel, que descarta todos los cambios y cierra el formulario.
Cada uno de estos comandos va entre corchetes por eventos, que se generan antes y después de que se ejecute el comando.
Antes del comando, se generan los siguientes eventos:
El evento FormEvents.PreviewSubmit se genera antes del comando FormCommand.Submit y el evento FormEvents.Submitted se genera después del comando FormCommand.Submit.
El evento FormEvents.PreviewRefresh se genera antes del comando FormCommands.Refresh y el comando FormCommand.Refreshed se genera después del comando FormCommand.Submit.
El evento FormEvents.PreviewCancel se genera antes del comando FormCommands.Cancel y el evento FormCommand.Canceled se genera después del comando FormCommand.Cancel.
Los eventos de vista previa pasan a un objeto PreviewFormCommandEventArgs. Este objeto contiene una propiedad Cancelar mutable que impedirá que se ejecute el comando correspondiente cuando la propiedad esté establecida en verdadero.
Los eventos posteriores al comando pasan un objeto FormCommandExecutedEventArgs. Este objeto contiene una propiedad Resultado que indica si la ejecución del comando se realizó correctamente, se canceló o se produjo un error. En caso de error, la propiedad Error del objeto FormCommandExecutedEventArgs hace referencia a la excepción que proporciona información sobre el error.
Es posible habilitar, deshabilitar y ejecutar comandos de formulario mediante programación y mediante declaración.
Para habilitar los comandos de formulario mediante programación, establece un CommandBinding entre el formulario y el comando relacionado.
En el ejemplo siguiente, se establece un enlace de comandos entre el formulario y un comando Actualizar y se definen dos controladores para este comando. El primer controlador devuelve si el comando Actualizar se puede ejecutar o no, y el segundo controlador contiene realmente la implementación del comando Actualizar:
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
}
}
También puedes definir controladores para comandos de formulario mediante declaración. Para ello, usa un objeto Regla que use RoutedCommandTrigger. En el ejemplo de código siguiente se muestra cómo definir controladores mediante declaración:
<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>