2010-08-12 8 views
5

Descargo de responsabilidad: En esta pestaña de preguntas, página, un diálogo realmente significa lo mismo, lo siento. Mi excusa: no estoy seguro de cómo debería ser el producto final: un montón de ventanas separadas o todo en uno.Buscando un gran ejemplo de asistente implementado con WinForms y/o un diseño de aviso de registro

Estoy buscando mejorar un Wizard existente y difícil de mantener preparado con WinForms. Necesito tratar de mantener el aspecto y la sensación sobre lo mismo, pero necesito limpiar la lógica interna. Hay 5 cuadros de diálogo en total, todos los cuales se muestran uno tras otro (después de que se hace clic en el botón Siguiente, por supuesto) dentro de un método gigante. La forma de saltar hacia adelante y hacia atrás es con ... ¡5 o 6 etiquetas y GOTO!

Ahora, este asistente es lineal, no es un árbol. Desde cualquier diálogo/página, debería poder ir a, como máximo, otros dos. De alguna manera, la lista doblemente enlazada viene a la mente. En este momento hay 5 * 4 = 20 transiciones de estado potencial, mientras que solo 2*1 + 3*2 = 8 de ellas son válidas. No tengo que usar goto s. Por lo general son malvados, y en este caso lo son, es difícil mantener esto ya ... y estoy pensando en agregar otra, 6ª página. La razón por la cual goto están ahí es muy probable porque A) la presión del tiempo cuando se estaba realizando la v. 1.0, B) Fue hace 5 años, por lo que los mejores ejemplos/tutoriales sobre asistentes disponibles en ese momento pueden no haber sido excelentes.

Ahora, la mayoría de las páginas del asistente solicitan la entrada del usuario. Las páginas siguientes se representan según lo que haya ingresado el usuario. Si el usuario dice en la página 3 y decidió volver a poner un botón de retroceso en 1, y no ha cambiado nada, y pulsa Siguiente dos veces, el estado no debería cambiar. Sin embargo, cambiar las cosas en la página x generalmente invalidará las cosas en las páginas x + 1 y posteriores. Sin embargo, existen excepciones, ya que algunas o todas las configuraciones en una página x pueden depender de la página x-1, x-2, etc., pero las páginas x + 1, x + 2, etc. no dependen de esa x para algo x.

Espero que las cosas estén claras hasta ahora. Tratamos de ayudar al usuario por defecto algunas cosas para ellos. La forma en que se almacenan las cosas tampoco es genial. El cuadro de diálogo tiene propiedades de lectura/escritura, desde/hasta qué elementos se copian a/desde los controles reales. Luego, en el método principal, hay un "super-almacenamiento" que almacena almacenamientos para cada página. Entonces, cuando el usuario finaliza con la página xy los hits a continuación, primero se copian las cosas de los controles en el almacenamiento que es local para la clase, y luego esas cosas se guardan en el miembro apropiado del super-almacenamiento.

Las matrices (de diálogos/almacenamientos) y los índices no se están utilizando. Existe una lógica "crear & populate" separada pero similar para cada destino goto (etiqueta). Los objetos de diálogo se descartan cuando la página ya no se muestra (no se eliminan, pero cada vez que se muestran, se vuelven a crear y se vuelven a llenar. No creo que sea necesario, ya que solo un Se necesita un único identificador, y después de que se haya mostrado y cerrado, creo que se puede volver a mostrar en el mismo estado, sin tener que volver a llenar los controles. Si desperdiciar la memoria fuera el único problema, probablemente dejaría que las cosas se desmoronaran. pero la cosa no es muy fácil de mantener, por lo que también podría arreglarlo todo para arriba

Pienso:.

  1. diálogos de almacén en una colección, como por ejemplo una matriz, pero preferiblemente DLL porque sólo puede avanzar 1 o retroceder 1, o solo una de las dos opciones Enumeré (para el primer y último diálogo).
  2. En realidad, mis pestañas/páginas extienden una clase abstracta común (porque los botones "siguiente", "posterior", "salir" y su comportamiento son comunes a todos).
  3. Cada pestaña/página/diálogo (lo mismo a los fines de esta pregunta, disculpe la confusión) tendrá propiedades de solo lectura visibles para la clase "conductor".Estas propiedades se derivarán de los valores en los controles (la verdadera fuente de información), a veces las propiedades darán masajes a estos valores un poco. Será responsabilidad del "conductor" tomarlos y guardarlos. Cuando el conductor desea completar el diálogo con un único método (llamémoslo "semilla"). Tengo un poco de dificultad aquí, ya que los parámetros para cada método de semilla serán diferentes. Quiero poder aprovechar la capacidad de tipeo fuerte, así como mantener las cosas genéricas. Sospecho que algo debería dar. Podría pasar un diccionario a cada método de semilla, pero eso se siente demasiado pitónico, como el tipado de patos. No sabría hasta el tiempo de ejecución si lo arruiné. Además, el embalaje y el desempaquetado del diccionario siempre coinciden, para cualquier página determinada. Aquí es donde vendría.
  4. El almacenamiento global puede ser un diccionario gigante. Puedo ser lo suficientemente disciplinado como para mantener todas las claves diferentes, o prefijo sus nombres con "p1_" a "p5_" dependiendo de una página, solo para estar seguro. Estoy seguro de que existen otros esquemas también. Tener en el diccionario gigante puede ser conveniente al final - allí no importará el orden en que se ensambló la entrada del usuario, siempre que se haga correctamente. También puedo tener una máquina de estado ... más o menos. Aquí es donde también me pierdo en el diseño. Si guardo las cosas en un diccionario, tendría que realizar una gran cantidad de lógica condicional, como: si estoy en la página 2, y hago un cambio, entonces, por lo general (puede haber excepciones), necesito realizar antiguos valores predeterminados si cualquiera para las páginas 3,4,5 no válido. Dependiendo de lo feo que sea, puede que no sea mucho mejor que el diseño actual basado en goto. Sin embargo, creo que puedo hacerlo mejor, ya que puedo implementar mi lógica específica de transición estatal o estatal con un grupo de delegados, manejadores que están almacenados en dos diccionarios (uno para el próximo, uno para el reverso), donde el estado actual es el llave.

Como puede ver, hay algunos desafíos. Tengo la esperanza, sin embargo, porque pensar a través de un buen diseño de mago es una rueda que seguramente fue [re] inventada antes. Quizás puedas recomendar una aplicación C#/mono de fuente abierta que viene con un asistente lineal, pero no trivial, para que pueda echar un vistazo a la implementación. Diablos, tal vez incluso Java/Swing probablemente me convenga, siempre y cuando el mago sea similar en naturaleza. WPF sería otro desafío para mí, no quiero tener 2 problemas en lugar de 1.

Déjame saber lo que se te ocurre. ¿Debería guardar los gotos pero limpiar otras partes tanto como pueda? No dude en hacer preguntas. Gracias,

-HG

Respuesta

0

por qué no crear una clase que tiene propiedades de cada campo de entrada para cada página y hacer un seguimiento de ellos, mientras que el usuario hace clic con el asistente, de esa manera usted será capaz de saltar hacia atrás y avanzar entre varias páginas y aún mantener los datos que ha agregado el usuario en su sesión. incluso se podría tener la validación de entrada en estos modelos de páginas de modo que cuando un usuario hace clic al lado se podía hacer algo como

if(!page1model.IsValid) 
{ 
    List<RuleViolation> ruleViolations = page1model.GetRuleViolations(); 
} 

esto es si he entendido algunos de sus problemas.

(para hacer el seguimiento de las páginas que podría tener los modelos de páginas implementar la misma interfaz y crean un algo List<IPageModel> o y añadir los modelos de página a la misma)

0

Yo voto por la superestructura como un parámetro que recibe cada página como se muestra. El segundo parámetro sería la dirección con la que llegamos a la página (Siguiente o Atrás). Si está atrás, solo muestra los datos que ya existen. Si es Siguiente, utiliza datos de páginas anteriores (encontrados en superestructura) para mostrar los datos apropiados en la página actual. La página también debe tener la información si se muestra por primera vez. Si es así, debe proporcionar los datos predeterminados, y si no es así, puede reciclar los datos existentes atribuidos a esa página (siempre que no contradigan los datos de las páginas anteriores). Estado es solo un número, que se incrementa o disminuye después de cada página.El código principal es un pequeño ciclo while que solo muestra una página para el estado actual y actualiza un estado cuando el usuario finaliza con la página. Cuando el usuario abandona la última página con Siguiente, salga del ciclo.

Si hay páginas opcionales, el código principal se vuelve un poco complicado, porque necesita una lógica para decidir qué página siguiente (y cuáles eran todas las páginas anteriores), pero todo lo demás sigue igual.

+0

Hm ... de esta forma cada "página" debe saber cuáles son todas las demás. Estoy pensando en poder actualizar una "página" después de que se haya construido con cualquier nueva información importante, y hacer que la página devuelva la información importante que se solicitó. Luego, algún tipo de controlador desde el exterior ... –

+0

La página está en la mejor posición para "saber" qué necesita ver el usuario en esa página y para saber qué información necesita para lograrlo. Y la página requiere toda la información que requiere :-). No hay ninguna razón para ocultar fragmentos de información de la página, ya que no hay necesidad de hacer que todo sea demasiado modular. Es decir, a menos que desee reutilizar "módulos" en otro proyecto. En esta solución, aún puede actualizar la página y, de hecho, debe actualizar cada vez que se muestre la página. Pero, si tiene una solución en mente, entonces solo haga eso. – Dialecticus

0

Cree un winform que alojará un control de usuario que implemente una interfaz. haga que todos los controles de usuario de las páginas implementen esta interfaz y controle el flujo desde su formulario de nivel superior. si te gusta este enfoque, puedo desenterrar algunos códigos antiguos para dar más detalles.

+0

esto parece prometedor, y sí, necesito código antes de poder tomar una decisión. No votaré negativamente incluso si no funciona. Animo a los demás a no castigar los intentos de ayudar tampoco. –

1

que tienen algo de código asistente WinForms esta es la arquitectura general:

El asistente principal tienen StepList lista como (De baseWizardStep) .. lista de todas las medidas posibles que se crean cuando la forma principal asistente es.

También Siguiente Botones para retroceder y cancelar.

Como con Alex anterior, tenga un formulario principal de clase base (baseWizard) con un panel que aloje un control de usuario para el paso actual. Cada paso del asistente en sí es un control de usuario (Hereda de baseWizardStep que hereda de UserControl) baseWizardStep tiene algunos eventos integrados (ArriveAtStep, ValiateStep, LeaveStep etc.) También en un sub InitliaseStep que toma el asistente principal como parámetro y almacena una referencia a la forma del mago principal en la propiedad. Todos los pasos acceden al asistente principal para almacenar sus datos en propiedades/objetos/datasets, etc. (el almacenamiento de datos generalmente se realiza en el evento LeaveStep) Los pasos cargan datos en sus controles en el evento ArriveAtStep.

A veces necesito compartir pasos de asistente entre muchos asistentes, en cuyo caso el asistente principal implementa una interfaz y los pasos del asistente transfieren su propiedad de asistente principal a la interfaz para acceder/almacenar datos.

Tengo 2 eventos de flujo de control superior a través del asistente.

Si no maneja ninguno de estos 2 eventos, el asistente pasa del 1.º en StepList al último en orden, paso por paso.

Evento 1: Si maneja ShoudlStepOccur este evento permite al desarrollador decidir si un paso ocurre en la lista de pasos (para omitir pasos) Esto parece hacer frente a la mayoría de los asistentes en una metáfora fácil de entender. Evento le da el paso y la diurección (Adelante, Atrás)

Evento 2: (control avanzado) THEN hay otro evento en el formulario principal NavigateToStep, el evento le indica el paso al que tenía la intención de ir, pero usted puede cambiarlo para navegar a un paso completamente diferente. Usamos esto para dar vueltas repetidas veces (por ejemplo, en el matricular en un asistente de curso, avanzamos a través de algunos pasos del asistente muchas veces una vez para cada curso)

También tengo una grilla que enumera todos los pasos con el paso actual resaltado y que el usuario puede hacer clic para saltar a través del asistente. Uso el evento StepShouldOccur para saber qué pasos mostrar en la lista de pasos en cualquier momento.

Una vez que haya finalizado el asistente, debe deshacerse de los pasos que no están actualmente alojados en el panel y simplemente estar en la Lista de pasos, ya que de lo contrario no recuperarán sus controladores de ventanas.

No estoy seguro de cuánto sentido tiene eso, así que lo voy a dejar allí.

Tal vez uno de estos días voy a poner este código de asistente en el proyecto de código o algo así, estoy bastante contento con la forma en que funciona para nosotros.

+0

Gracias, ¿podrían compartir el código? –

Cuestiones relacionadas