PDA

Ver la Versión Completa : al Cambiar Preferencias informar a todas las ventanas afectadas


Lepe
20-11-2006, 13:53:04
Vamos al grano: En todo programa se tiene la ventana de preferencias, y al modificar una opción puede afectar a varias ventanas que están abiertas (entorno MDI), ¿qué filosofía usan para reflejar ese cambio?

PD: No me sirve eso de: "ahhh, tienes que cerrar la ventana y volverla a abrir para que los cambios surtan efecto" ;).

Por ejemplo, una opción en las preferencias dice: "Desplegar XX elementos en las listas desplegables". Ya he pensado algunas soluciones, pero siempre le encuentro "pegas", expongo a continuación "mis sobresaltos mentales" :D para explicarme mejor:

- Comprobar qué ventanas se encuentran abiertas y ejecutar un procedimiento concreto es bastante engorroso.

- Realizar una Forma base (de la cual hereden todas las ventanas) con un método predefinido puede ser útil, pero si queremos recibir esa modificación desde un .pas que no tiene asociado una ventana, hemos pinchado ;). Además pueden estar abiertas 5 ventanas y el cambio solo afecta a 1 de ellas, no es muy eficiente que digamos.

- He pensado implementar una clase TEventAlerter (al estilo de interbase), así podría centralizar todas las alertas, e incluso reutilizar el código. La pega viene cuando se tenga un número elevado de alertas, podría ser una jungla de eventos que se lanzan en las más insospechadas ocasiones.

¿Qué opinan de todo esto?

PD: Me extraña que este tema no se haya tratado anteriormente en los foros; al parecer, no he sabido buscar por los términos correctos.

Gracias por vuestra paciencia y dedicación.

Saludos

dec
21-11-2006, 08:30:49
Hola,

Seguramente no te sirva lo que diga Lepe, o sea algo que ya tuvieras en cuenta. Me extrañaría que no fuera así. Alguna vez hize alguna aplicación con su correspondiente ventana de opciones. Dichas opciones podían aplicarse o "aceptarse".

En el formulario principal de la aplicación se contaba con dos métodos: uno para guardar las opciones "actuales" y otro para recuperar las opciones. Dichas opciones se guardaban en el Registro de Windows. Así, cuando se habría el formulario de opciones (desde el formulario principal), al cerrarse el primero "aceptando las opciones" (no cancelando) se llevaba a cabo la ejecución del procedimiento para guardar las opciones del formulario principal.

Al cerrarse el formulario de opciones, acto seguido, quiero decir, se ejecutaba el procedimiento para recuperar las opciones, de manera que estas se aplicaban en el formulario principal, naturalmente. Ya con más formularios implicados... no sé yo cómo sería el caso.

Pero, ya digo: contaba con un procedimiento para guardar las opciones y otro para recuperarlas. Cuando se abría el formulario de opciones se mostraban las actuales al usuario. Cuando el usuario cambiaba dichas opciones, primero se guardaban, utilizando el procedimiento adecuado, y luego se leían, de manera que los cambios surtieran efecto.

Me parece que no he dicho nada... o que he liado la cosa más, pero, en fin, es lo que puedo decir. Ahora, que esto pueda mejorarse... hacerse mejor... no me cabe la menor duda de que así es.

seoane
21-11-2006, 12:47:28
Le he estado dando vueltas al asunto, y se me ha ocurrido algo sencillo, aunque no se si te servirá. Defines un nuevo mensaje WM_ACTUALIZAR y en todas las ventanas interceptas ese mensaje. Dentro del procedimiento que trata ese mensaje pones todo lo necesario para actualizar la ventana, de este modo cuando quieras actualizar solo tienes que mandar ese mensaje a todas las ventanas abiertas. Solo necesitaras la API SendMessage, así que no tendrás problema en llamarla desde cualquier unit.

Es solo una idea, ahora quedaría saber el handle de todas las ventanas, aunque no creo que sea difícil averiguar el handle de las ventanas hija, o en ultimo caso llevar una lista.

EDITO:

Añado un ejemplo de lo que quiero decir. En este ejemplo todos los hijos interceptan el mensaje de la misma forma, pero cada uno podría interpretarlo como quisiera, además si algún formulario no lo interceptara no pasaría nada, simplemente seria ignorado.

EDITO 2:
En el ejemplo para mandar el mensaje a todos los formularios hago esto:

for i:= 0 to frmMain.MDIChildCount - 1 do
SendMessage(frmMain.MDIChildren[i].Handle,WM_ACTUALIZAR,0,0);


Pero se me ocurre otra forma que todavía hace mas independiente cada formulario del resto:

for i:= 0 to Screen.FormCount - 1 do
SendMessage(Screen.Forms[i].Handle,WM_ACTUALIZAR,0,0);



PD: Como Lepe no responda rápido, seguro que va a haber un "EDITO 3" :D

Lepe
21-11-2006, 20:48:38
Gracias dec, uso algo parecido, aunque la mayoría de las opciones las guardo en la propia base de datos (tabla config), en un entorno multiusuario es imprescindible, ya que el usuario puede cambiar de máquina y al hacer el login, tiene toda su configuración.

seoane: Gracias por el ejemplo , no hace falta que te expliques más porque ya he captado perfectamente la idea.

Es más en los parámetros wmParams podría enviar un entero indicando qué opciones se han modificado, algo así como definir constantes del tipo:


Valor Significado
1 modificado opción de desplegar
2 " " una
4 " " dos
8 " "
16


Si se envía un 6 ya se sabe que hay que "recargar" las opciones "una" y "dos"

Tengo que implementarlo para ver las posibles "incomodidades" que produzca; de momento es el método que más me gusta.

Tengo poco tiempo ahora mismo... pero volveré ;)

roman
21-11-2006, 21:24:27
A esto, en OOP se le conoce como el patrón del observador (http://en.wikipedia.org/wiki/Observer_pattern). La VCL lo utiliza en muchas partes, por ejemplo, cuando el DataSource comunica a todos los controles conectados (suscritos) a él, acerca de un cambio.

Con el método de seone recuerden que también pueden pasar como parámetros de los mensajes, apuntadores a un objeto o registro, disfrazados de enteros. Digo esto porque así puede pasársel una estructura de datos mucho más rica que indique qué cambios y cuáles hubo.

// Saludos

Lepe
22-11-2006, 10:37:09
Muchas gracias roman por el enlace, de hecho eso mismo he implementado con el TEventAlerter, pero como empezaba a ver "incomodidades" (pueden ser muchos mensajes registrados en la aplicación, muchos callbacks esperando dicha notificación, saltos de eventos en ocasiones inesperadas) ... vamos ... como la propia VCL en TDatasets (tú lo has comentado en otras ocasiones y estoy totalmente de acuerdo, es un jungla de eventos), quería saber si hay otra opción.

A ver, no pido milagros, sino poder reutilizar código y que sea algo "elegante". Si no puede ser ambas cosas al mismotiempo, usaré la que mejor se adapte.

Lo que si me gusta del TIBEventAlert es que puedes saber todos los mensajes que están registrados en un momento determinado y al menos saber cuantos observadores lo esperan. Ayuda un poco cuando se está depurando una aplicación.

Por razones que no vienen al caso, tengo que posponer mi trabajo unos días, así que no entraré a menudo en los foros, pero esto, no quita importancia a todos vuestros esfuerzos y consejos, Gracias.

Saludos

Lepe
30-11-2006, 20:41:06
Pues al final me he decidido por el patrón del observador, pero nada complejo:

Un TStringlist almacena el nombre de cada "alerta", cada alerta lleva asociado un TObjectList.

Cada elemento del TObjectList es un objeto en el que se puede almacenar:
- Puntero hacia un procedure of object
- Puntero hacia un procedure.
- Tipo de Puntero (define cual de los dos anteriores se ha usado).
- Nombre del observador (para depurar).

Todo queda encapsulado en un TObject con los métodos:
- AddAlert - > se registra un observador
- DeleteAlert-> se elimina un observador
- ExecuteAlert-> se lanza la notificación de una alerta determinada a todos sus observadores
- AlertsNamesAndObservers -> Depuración: muestra todos los nombres de las alertas, el total de observadores y el nombre de cada uno.

Los 3 métodos iniciales están sobrecargados para llamarlos con un "procedure of object" o con un simple "procedure"

Le veo una pequeña lista de futuros cambios (de momento se queda como está ;)):
TO DO: - ¿Derivar de TComponent y añadir el FreeNotification?
Razon: Si el usuario del módulo no Elimina la alerta al hacer un .Free de la
ventana donde se encuentra el Callback, al llamar a Execute se
producirá un access violation.

- Añadir CriticalSection ¿?

Saludos y muchas gracias.

Paoti
30-11-2006, 23:27:55
:eek:


:eek:


:eek:


Otro nivel con ustudes... cada día se aprende más.


Gracias.

Lepe
02-12-2006, 17:29:29
Como es algo que pueda interesar (al menos para echar un vistazo), aquí os lo dejo. Tiene una mala práctica de programación, el que avisa no es traidor.

Como ya dije hay dos clases una es TEventAlerter (a partir de ahora EA) y otra TEAItem, la relación entre ambas es la misma de un TreeView (TTreeNodesss) y un TTreeNode, pero he desechado hacerlo como hace la VCL. La razón (que no es de peso) es que me gusta usar una sola línea de código para añadir la alerta, en lugar de teclear 4 o 5 líneas.

A quien le guste que lo use, y al que no... ¡¡ que lo modifique !! :D

Otros Comentarios:
- No se instala nada en la paleta de delphi, sólo son 2 unidades independientes.
- Para usarlo, se da un ejemplo, básicamente es asignar un evento Onclick de por código delphi (sin usar el Inspector de objetos).
- Implementado en BDS2006, pero se podrá ejecutar desde un Delphi 6 sin problemas. (En la cabecera del archivo EventAlerter.pas están las directivas del compilador, borrarlas si os da problemas)

Saludos