A

AliceinWonderland

Usuario (Argentina)

Primer post: 8 mar 2011Último post: 10 may 2011
3
Posts
360
Puntos totales
11
Comentarios
Wiki Conexión ADSL (cómo mejorarla)
Wiki Conexión ADSL (cómo mejorarla)
OfftopicporAnónimo3/8/2011

Temario del Post 0. Tres veces SI 1. Objetivo del post 2. Definición de terminos 3. Como funciona el ADSL 4. Distancia vs Velocidad 5. Ventajas y Desventajas del Par Trenzado 6. Potencia vs Ruido 7. Relación Señal Ruido (SNR) 8. Atenuación e Impedancia 9. Dos velocidades CAÑO y CAUDAL 10. Como funciona el MODEM 11. Diferentes modulaciones 12. Como hacer una prueba de la señal 13. Como hacer una prueba de la velocidad 14. Que es Internet 2.0 15. El problema de los otros EQUIPOS 16. El problema del MODEM FRIO 17. El problema del TERRIBLE ECO 18. Otros problemas comunes 19. Configuración de la modulación (G.dmt vs ADSL2+) 0. Tres veces SI Si no tienen problemas con su conexión ni la toquen. Si tienen problemas llamen al SERVICIO TÉCNICO o la MESA DE AYUDA (0800). Si tienen problemas y no encuentran solución lean el post. 1. Objetivo del post Mejorar el servicio ADSL. Este post va dirigido a todo quienes tienen o trabajan con una conexión ADSL (Telefónica/Telecom) y tienen problemas con el servicio o quieren verificar y mejorar la calidad del mismo. La idea es introducir solo los conceptos necesarios y aprender a utilizarlos. 2. Definición de términos ADSL es la digitalización de un par telefónico, en términos criollos: es darle capacidad de TRANSPORTE de DATOS a la línea de teléfono. MICROFILTRO bloquea la señal ADSL, para que no se escuche ruido a LLUVIA en el teléfono. Es un filtro pasa bajos, anula la señal ADSL (alta frecuencia) y deja pasar la VOZ (baja frecuencia), además corrige la impedancia del aparato telefónico. SPLITTER es un filtro de mejor calidad que el microfiltro, separa las señales y corrige la impedancia (su correcta instalación es indispensable en domicilios con varias bocas telefónicas, aparatos o alarma monitoreada). MODEM es el equipo que transmite y recibe la señal ADSL, en realidad es un PUENTE (bridge) entre la línea de teléfono y una conexión de RED (ethernet). DSLAM es el MODEM que está del lado de la central telefónica. PAR TELEFONICO dos alambres que proveen conexión eléctrica desde la central telefónica hasta el domicilio. MULTIPAR cable red de teléfonos, compuesto por muchos pares telefónicos. kBps kilo (1024) Bytes por segundo (1 Byte = 8 bits). kbps kilo (1024) bits por segundo. 3. Como funciona el ADSL La VOZ es una señal de muy baja frecuencia (< 4khz), entonces se inyecta en la línea de teléfono una señal de muy alta frecuencia (25khz – 1100khz) para enviar DATOS. El MODEM es como un equipo de radio que utiliza un par de cobre en vez del éter. En la CENTRAL TELEFÓNICA se mezclan las dos señales VOZ + ADSL y en el domicilio se separan (SPLITTER). 4. Distancia vs Velocidad Pongamos un ejemplo sencillo para entender esto de la velocidad y la distancia: Supongamos que el MODEM de la central telefónica (DSLAM) y el MODEM del domicilio son dos personas. Estas personas para comunicarse hablan, uno transmite por la boca y el otro recibe por el oído y viceversa. Si no se escuchan correctamente, ya sea porque hay mucho RUIDO en el ambiente o porque están muy lejos y escuchan bajito (ATENUACION), tienen que repetir varias veces lo que dicen para poder entenderse, de esta forma la VELOCIDAD de transmisión de palabras disminuye. Algo parecido sucede con los MODEM, si se escuchan bien pueden transmitir mayor cantidad de datos. 5. Ventajas y Desventajas del Par Trenzado La línea de teléfono es un par de alambres trenzados, enroscados uno con el otro y todos entre sí. Es lo mismo que un CABLE de RED, denominado UTP. Es lo más eficiente y económico hoy por hoy. La tecnología ADSL es sencillamente excelente (además de vieja, década del 80). Ventajas: 1. es una conexión PUNTO a PUNTO, el ancho de banda contratado es exclusivo del cliente, ningún otro cliente le puede quitar ancho de banda a otro 2. el CANAL de SUBIDA es independiente del de BAJADA (Internet 2.0) 3. se le puede meter mano a la instalación y la configuración del MODEM 4. hay mucho material e información útil (como este post) 5. tiene muy buen desempeño y hay veces que funciona hasta en condiciones extremas (corto circuito, sin circuito, mala aislación) Entonces ¿cuáles son las desventajas?: 1. la incorrecta instalación domiciliaria 2. los cables del tendido de redes mas nuevos tienen 20 años de antigüedad (desde la privatización), los otros son antiquísimos (esto trae problemas de ATENUACIÓN, RUIDO, DESBALANCE, MALA AISLACION, ETC) 3. los tendidos de cables son anteriores a la implementación del ADSL 4. la distancia que puede cubrir el servicio suele ser inferior a la de otras tecnologías 5. es mayor la latencia Las cableras (compañías de cable) que venden INTERNET (Fibertel, Multicanal, Cableras locales, etc) han tenido que renovar todos los tendidos de COAXIL para poder transmitir en alta frecuencia, cables con menos de 20 años. Esta es la razón por lo cual la calidad del servicio respecto de la señal es superior. El corazón de la tecnología ADSL es el PAR TELEFONICO e INSTALACION, no el MODEM, el MODEM es víctima de las condiciones de la línea, el pobre hace lo que puede. 6. Potencia vs Ruido Volvamos al ejemplo de los dos tipos hablando. Si en el medio de transmisión (PAR TELEFONICO) hay mucho RUIDO, ¿qué hacen los dos tipos? Y, tienen que GRITAR (levantar los db, suben el VOLUMEN, la POTENCIA) porque sino no se escuchan. Lo mismo pasa si están lejos el uno del otro, llega muy debilitada la voz (ATENUACION). Si el tipo tiene que estar mucho tiempo GRITANDO se pone AFONICO, similar le sucede a un MODEM, se agota. La POTENCIA todo lo soluciona, los usuarios que se encuentran cerca de la central suelen tener instalaciones desastrosas y todo funciona igual. 7. Relación Señal Ruido (SNR) Supongamos que nuestros dos amiguitos, Rojo y Azul, se fueron de parranda a un boliche. En el boliche hay mucho RUIDO, no solo hablan ellos sino que hay muchos otros hablando, además está la música de fondo (RUIDO AMBIENTE). La comunicación puede ESTABLECERSE (MODEM SINCRONIZADO), si el VOLUMEN de la VOZ de los tipitos es suficientemente superior al RUIDO AMBIENTE. La RELACION SEÑAL RUIDO (SNR) es la diferencia de VOLUMEN entre el RUIDO AMBIENTE y la COMUNICACIÓN. Si los de al lado GRITAN nuestros amiguitos sufren de mayor RUIDO AMBIENTE, entonces ellos empiezan a gritar, a su vez están contaminando el ambiente con sus gritos, los de alrededor tienen que gritar mas, y así esto es una rueda de nunca acabar. Con el CABLE MULTIPAR sucede lo mismo, si un PAR está defectuoso los MODEM del mismo transmitirán en ALTA POTENCIA, contaminando los pares aledaños, los vecinos subirán sus potencias para compensar el ruido, con lo que el par defectuoso deberá hacer lo mismo. Finaliza colapsando el PAR mas débil al no poder competir con los pares mas eficientes, interrupciones del servicio. Cuanto mayor la distancia (db) entre la señal y el ruido, mejor es la calidad de la señal. 8. Atenuación e Impedancia La ATENUACIÓN es la pérdida de señal en virtud de la LONGITUD del par telefónico, a mas distancia mayor atenuación, resultando en una señal mas débil, entonces la señal es mas próxima al ruido, la SNR es menor, con menor calidad de señal queda expuesto a interrupciones del servicio. Hay que tener en cuenta que el PAR funciona como ANTENA, capturando toda interferencia electromagnética del ambiente, por esto a mayor recorrido suele haber mas ruido. La IMPEDANCIA es la oposición que pone el PAR al paso de la señal. Los aparatos telefónicos tienen una impedancia de 600 ohm contra los 100 ohm del ADSL. La impedancia en ALTA FRECUENCIA se traduce como RESISTENCIA, si no se corrige la impedancia de la instalación (FILTROS) se carga una resistencia extra al par, resultando mayor IMPEDANCIA, entonces la señal es mas débil, es mas propenso a interrupciones del servicio. 9. Dos velocidades CAÑO y CAUDAL Hay que tener bien en claro que en la tecnología ADSL se manejan dos velocidades: 1. El MODEM establece un CAÑO digital, le da capacidad de transporte de datos a la línea. 2. La velocidad de navegación contratada es el CAUDAL. Lógicamente la capacidad de la línea (CAÑO) tiene que ser superior a la velocidad contratada (CAUDAL), y como ya vimos el caño está condicionado por la longitud del par. Por ejemplo el PERFIL ADSL de Telefónica para 1 Mega suele ser 1184 kbps en el caño de bajada, para garantizar 1024 kbps de caudal de descarga (aprox 128 kBps). 10. Cómo funciona el MODEM El MODEM es ESCLAVO del DSLAM (central), es decir que tiene que cumplir con los requerimientos de velocidad establecidos por el equipo de la central. Nosotros no le podemos cambiar el PERFIL, eso se establece comercialmente, con la velocidad contratada. Lo primero es establecer la velocidad contratada, el MODEM prueba la línea para determinar la velocidad máxima alcanzable en cada canal (subida/bajada). Si la velocidad alcanzable supera la velocidad contratada el MODEM SINCRONIZA. Tanto el MODEM como el DSLAM trabajan siempre a la potencia mínima, para no contaminar con ruido a los otros pares, es decir que LA POTENCIA NUNCA ES UN PARÁMETRO VÁLIDO DE MEDICIÓN. El MODEM si no puede cumplir con la velocidad contratada establece una SNR de 12db (aprox.) y SINCRONIZA A LA MÁXIMA VELOCIDAD CON ESE MARGEN DE 12db. Los fabricantes establecen que con 6db de SNR el equipo funciona correctamente, si el par telefónico fuera estable esto se cumpliría, se necesita mayor margen para cubrir las condiciones medioambientales, agua, viento, interferencias electromagnéticas, etc. 11. Diferentes modulaciones La MODULACIÓN es como el IDIOMA que utiliza el MODEM para comunicarse, la diferencia fundamental entre las modulaciones es la frecuencia máxima que utilizan. En el caso de Telefónica las modulaciones son G.dmt y ADSL2+, baja frecuencia y alta frecuencia respectivamente. Los PERFILES son todos con 608 kbps en el canal de subida (caño) y ambas modulaciones cumplen con este requisito, entonces lo que determina fundamentalmente el tipo de modulación es el canal de bajada. Los equipos eligen la modulación en forma automática pero ya veremos que en algunos casos es aconsejable establecerla manualmente. 12. Cómo hacer una prueba de la señal (SNR) Desconociendo el estado de la instalación telefónica en el domicilio, toda prueba se debe realizar a PUNTA DE LINEA, SE DESCONECTA TODO y lo único que queda conectado a la línea de teléfono es el MODEM con un TELEFONO FILTRADO. De esta forma se evalúa la CALIDAD DE LA SEÑAL, en muchos domicilios esto no es sencillo porque las instalaciones nacen desde el techo o cielorraso, la entrada de la línea no es accesible. Lo primero es ESCUCHAR si la línea tiene RUIDO, para esto se descuelga el teléfono y se oprime una tecla, por ejemplo asterisco, tapando el micrófono del tubo escuchamos atentamente. NO TIENE QUE HABER NINGUN RUIDO (fritura, zumbido, etc). OJO MUCHOS TELEFONOS HACEN RUIDO. Si la línea tiene RUIDO lo mas probable es que el MODEM no SINCRONICE o PIERDA SINCRONISMO constantemente. Una vez que el MODEM haya sincronizado vemos el resultado de la medición. Si la medición indica una SNR superior a 12db en ambos canales (subida/bajada) podemos concluir que el MODEM ha sincronizado cumpliendo con la velocidad pautada por el DSLAM (CAÑO), la cual tiene que ser superior al CAUDAL contratado. Si tenemos contratados 3 Megas, en el caso de Telefónica el CAÑO de BAJADA sería aproximadamente 3008 kbps, si nuestro MODEM SINCRONIZA a 14db con una velocidad de 2273 kbps, es evidente que NUNCA TENDREMOS 3 Megas, porque el CAÑO es inferior al CAUDAL. ALGO ANDA MAL. LA SNR SIEMPRE DEBERIA SER SUPERIOR O IGUAL A 12db EN AMBOS CANALES. Si en punta de línea no se obtienen los valores (VELOCIDAD/SEÑAL) apropiados, mucho menos conectando toda la instalación. Si en punta de línea los valores son correctos y al conectar todo como corresponde no lo son, entonces lo mejor es LLAMAR al SERVICIO TÉCNICO, igualmente ya veremos como se deben filtrar las instalaciones. 13. Cómo hacer una prueba de velocidad (CAUDAL) Primero verificar que el MODEM ha sincronizado a una velocidad superior a la contratada, por ejemplo para 1 Mega son 1184 kbps, para 3 Megas son 3008 kbps, etc. De forma análoga a la prueba de la señal, la PRUEBA DE VELOCIDAD debería realizarse desconectando todos los dispositivos de RED, dejando una sola PC conectada en forma directa con el MODEM y en lo posible SIN WINDOWS, es recomendable utilizar un Live CD con alguna distribución Linux, bootear desde ahí y realizar una prueba de descarga desde varios servidores de descarga directa en forma simultánea. Prácticamente NINGUN SERVIDOR provee 1 Mega de descarga en forma gratuita, por esto para probar una conexión de 3 Megas o más es necesario usar algún gestor de descarga como JDownloader, BitTorrent, etc. IMPORTANTE tener en cuenta estos datos al leer la medición: La velocidad CONTRATADA es la SUMA de los DOS CANALES. Por ejemplo 3 Megas son 300 kBps (download) + 64 kBps (upload), son 2400 kbps (download) + 600 kbps (upload) aproximadamente. Aproximadamente el 10% del ANCHO de BANDA se utiliza para SEÑALIZACIÓN de TRÁFICO, se lo lleva el PROTOCOLO (HTTP, UTP, UDP, FTP, etc). 14. Qué es Internet 2.0 La tecnología ADSL fue concebida con un canal de subida de 1 Mega, el cual no interfiere en absoluto con el canal de bajada, es decir que no se compromete el ancho de banda del cliente ni de ningún otro (cliente) par aledaño. Esta ventaja ha sido explotada por las compañías telefónicas para competir con las de cable (debido a que las cableras proveen excelente servicio), la tecnología cablemodem compromete el ancho de banda de todos los clientes, al ofrecer mayor caudal de subida resta en bajada a todos. Internet 2.0 es simplemente un canal de subida de 512 kbps o mayor, el cual se hace notorio al momento de compartir información hacia la web. 15. El problema de los otros EQUIPOS Es muy común que la alarma NO ESTE FILTRADA y peor aún que toda la instalación esté conectada al RETORNO de la alarma, es decir que la señal ADSL primero pasa por el circuito eléctrico de la alarma. Todo equipo por el cual circule la señal ADSL producirá ATENUACION, IMPEDANCIA, MALA AISLACION, RUIDO, REFLEJO, ECO, etc, etc, etc. A saber: PTR, PCR, FILTROS DE RF, PROTECTORES GASEOSOS, DIRECT TV, ALARMAS, POSTNET, ESTABILIZADOR DE TENSION, CALLER ID, CONTESTADOR, CAMPANILLA, PORTERO, CANDADO TELEFÓNICO, MODEM DIAL-UP, FAX, etc, etc, etc. Cualquier equipo (medianamente bueno), que se conecte a la línea telefónica NO UTILIZA ALTA FRECUENCIA, entonces para evitar ruidos suelen tener filtros de radio frecuencia, lógicamente recortan la señal ADSL, anulando algunas portadoras. 16. El problema del MODEM FRIO Es muy común en las FALTAS ADSL (avería o reclamo ADSL), llega el técnico con un MODEM FRIO, que no estuvo trabajando, lo cambia por el que está instalado y sale todo funcionando perfectamente, hasta que este NUEVO MODEM se QUEDA AFONICO y no puede transmitir ya en ALTA POTENCIA. 17. El problema del TERRIBLE ECO Es la principal causa de baja SNR, el MODEM se contamina a sí mismo con su propio REFLEJO o INDUCCIÓN. Esta falta se suele presentar fundamentalmente por alguno de los siguientes motivos: 1. INSTALACION INCORRECTA, mal filtrada 2. BOBINA, rollo de cable, ya sea instalación domiciliaria (blanca) o bajada (negra) Una bobina puede encontrarse en el techo del domicilio, al realizarse la instalación queda un rollo de cable; también se da el caso en el interior, los electricistas suelen dejar rollitos en las cajas de registro, sobra instalación y da pena cortarla, el MODEM va de una habitación a la otra, etc. En la bobina se induce la señal entre vuelta y vuelta, esta señal inducida también se desfasa y atenúa, como resultado tenemos el TERRIBLE ECO por INDUCCIÓN. Para ver en forma sencilla las otra causa vamos a comparar la señal ADSL con el sonido. Supongamos que nuestros amiguitos (Rojo y Azul) se aventuran y viajan a España a hacer espeleogía (se meten en una caverna). Rojo se interna en la cueva y Azul espera en la entrada. Azul preocupado porque Rojo no salía comienza a gritar -Rojo!!! Rojo!!!-, el sonido de su voz se mete en cada hueco de la caverna, al chocar con las paredes rebota volviéndose por donde venía. Cada rebote a su vez ingresa en todos los huecos formándose el TERRIBLE ECO. Rojo al oír la voz de su amigo QUE LLEGABA DE MULTIPLES DIRECCIONES!!! gira la cabeza hacia todos lados, desorientado grita -Azul dónde estás?- y presuroso busca la salida de la cueva. De igual manera sucede con las instalaciones, en cada bifurcación se divide la señal, al llegar al final de la instalación rebota (se refleja) y así llegan al MODEM la señal pura con mayor potencia y las señales ECO atenuadas y desfasadas, así se forma el TERRIBLE ECO por REFLEXIÓN. PARA EVITAR ESTO SE INSTALA EL SPLITTER EN PUNTA DE LINEA 18. Otros problemas comunes Síntoma: Al descolgar y hablar por teléfono el MODEM sincroniza. Causas posibles: Desbalanceo capacitivo, suele ser la línea o sulfato en algún empalme. Síntoma: Al descolgar y hablar por teléfono el MODEM pierde sincronismo. Causas posibles: Algún aparatos sin filtrar, lo ideal es colocar correctamente un splitter (puede ser una campanilla, cualquier cosa conectada a la línea). Síntoma: Al prender el MODEM funciona unos minutos y al rato no navega, hay que apagarlo y vuelve a suceder lo mismo. Causas posibles: VIRUS, el sincronismo no se cae, la conexión no se pierde, funciona un rato y luego no navega. Se pueden evaluar la cantidad de sockets abiertos con el comando netstat. Síntoma: Internet funciona pero no tengo tono, llamo a mi número y me dice "ABONADO FUERA DE SERVICIO" o salta la contestadora. Causas posibles: Corto circuito, la línea en corto, algún teléfono en corto queda descolgado, alguna instalación en mal estado (humedad, rota, etc). Síntoma: Tengo tono SIN RUIDO en el cordón telefónico del MODEM pero Internet no funciona. Causas posibles: Filtro mal conectado, probar desconectando todos los filtros que estén en la ficha donde está conectado el MODEM, conectar el MODEM directo (SIN FILTROS) en la ficha y probar. Síntoma: Se corta la conexión y tengo muy buena señal (SNR), los filtros están todos bien. Causas posibles: Baja tensión, el equipo necesita buena tensión para poder transmitir, fundamentalmente los equipos con WIFI, OJO EL ESTABILIZADOR NO SOLUCIONA ESTO. Síntoma: Se escucha ruido a lluvia en el teléfono. Causas posibles: Ausencia de filtro, el teléfono capta la señal de alta frecuencia como un ruido. 19. Configuración de la modulación (G.dmt vs ADSL2+) Si bien el MODEM es esclavo del DSLAM, este puede elegir la modulación (es como el idioma) que utilizara para comunicarse con el DSLAM. Las diferentes modulaciones tienen que ver con el calibre de los pares y con modificaciones que se han hecho para poder exprimir a fondo esto del ADSL, ampliar el canal de subida, duplicar la frecuencia, etc. G.dmt (ADSL) es una señal de 1 Ghz, ADSL2+ es de mayor frecuencia 2 Ghz. Si recordamos que IMPEDANCIA * ALTA FRECUENCIA = RESISTENCIA entonces nos conviene utilizar una señal de menor frecuencia, así la señal se debilitará mucho menos. Para los simples mortales que tienen un CAÑO menor o igual a 5 Megas (no confundir con velocidad contratada) es mejor G.dmt. Para quienes tienen un caño superior a 5 Megas es aconsejable utilizar ADSL2+, G.dmt.bis.plus, etc. Es conveniente tener el bitswap activado. Saludos.

272
42
D
Delphi cómo crear un componente (2/3)
OfftopicporAnónimo5/10/2011

Creando componentes propios en Delphi (2/3) - el objetivo es proporcionar un buen punto de partida completamente en castellano - se utiliza Delphi 2009 para hacer las capturas y demo, pero es equivalente en los anteriores y sucesivos - es una traducción casera, no tiene una semántica perfecta sobre poo o delphi, se utilizan términos conocidos para hacerlo accesible a cualquier programador - no se responderán preguntas, no tengo mayores conocimientos, hay libros y foros especializados en estos temas - para ser buen "escritor" primero hay que ser buen "lector" Cómo escribir las propiedades avanzadas de un componente Artículo publicado por: Peter Morris http://delphi.about.com/library/bluc/text/uc080701a.htm "Este artículo apareció originalmente en Delphi Developer. Copyright Pinnacle Publishing, Inc. Todos los derechos reservados." En la primera parte cubrimos los aspectos básicos sobre la creación de componentes, ahora veremos como escribir propiedades mas avanzadas, los métodos (streaming) para estas propiedades, y las subpropiedades. Referencias a componentes Algunos componentes necesitan hacer referencia a otros componentes. Por ejemplo TLabel tiene una propiedad "FocusControl". Cuando se incluye un ampersand & en la propiedad "Caption" este subraya la letra siguiente, &Hello se convierte en Hello, presionar el método abreviado alt-H en el teclado desencadenará un evento en la etiqueta. Si la propiedad "FocusControl" esta activada, el foco pasará al control especificado. Tener tal propiedad en un componente es bastante simple. Todo lo que hay que hacer es declarar una nueva propiedad, y establecerla en la clase ancestro mas elevado posible (en el arbol de herencia), que acepte este tipo de propiedad, (TWinControl permitirá a cualquier descendiente de TwinControl ser utilizarlo), pero tiene sus consecuencias. type TSimpleExample = class(TComponent) private FFocusControl : TWinControl; protected procedure SetFocusControl(const value : TWinControl); virtual; public protected property FocusControl : TWinControl read FFocusControl write SetFocusControl; end; procedure TSimpleExample.SetFocusControl(const Value : TWinControl); begin FFocusControl := Value; end; Tomemos el ejemplo de arriba. Es un ejemplo un poco simple (mas allá del nombre del componente) de como escribir un componente que hace referencia a otro componente. Si tenemos tal propiedad en un componente, el inspector de objetos (Object Inspector) nos muestra un combo con la lista de los componentes que encajan en este criterio (todos los componentes de TWinControl). Nuestro componente puede hacer algo como esto: procedure TSimpleExample.DoSomething; begin if (Assigned(FocusControl)) and (FocusControl.Enabled) then FocusControl.Setfocus; end; Primero verificamos si la propiedad fue asignada, si es así establecemos el foco a esta, pero hay situaciones en las cuales la propiedad no es Nil y sin embargo el componente al que apunta ya no es válido. Esto sucede a menudo cuando una propiedad hace referencia a un componente que ha sido destruido. Afortunadamente Delphi provee una solución. Cada vez que un componente es destruído este notifica a su propietario (owner) que está siendo destruido. En este punto cada componente que es hijo (owned) por el mismo formulario (form) es notificado de este evento también. Para atrapar este evento debemos sobre escribir el método definido en el componente base TComponent, llamado "Notification". type TSimpleExample = class(TComponent) private FFocusControl : TWinControl; protected procedure SetFocusControl(const value : TWinControl); virtual; public protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; property FocusControl : TWinControl read FFocusControl write SetFocusControl; end; procedure TSimpleExample.SetFocusControl(const Value : TWinControl); begin FFocusControl := Value; end; procedure TSimpleExample.Notification(AComponent : TComponent; Operation : TOperation); begin If (Operation = opRemove) and (AComponent = FocusControl) then FFocusControl := Nil; end; Ahora cuando el componente al que hacemos referencia es destruído somos notificados, en este caso podemos establecer la referencia a Nil. Vale aclarar que solo serán notificados los componentes contenidos en el mismo formulario. Esto nos trae otro problema. Solo recibiremos la notificación de que el componente está siendo destruído si se encuentra en el mismo formulario. Es posible que la propiedad esté apuntando (haciendo referencia) a un componentes de otros formularios (o también sin ningún formulario asociado), y cuando estos componentes son destruídos no recibimos notificación de ningún tipo. Una vez más, hay una solución. TComponent introduce un método llamado "FreeNotification". El objetivo de FreeNotification es decirle al componente (FocusControl) que nos tenga en mente cuando es destruído. Una implementación se vería como esta: type TSimpleExample = class(TComponent) private FFocusControl : TWinControl; protected procedure SetFocusControl(const value : TWinControl); virtual; public protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; property FocusControl : TWinControl read FFocusControl write SetFocusControl; end; procedure TSimpleExample.SetFocusControl(const Value : TWinControl); begin if Assigned(FFocusControl) then FFocusControl.RemoveFreeNotification(Self); FFocusControl := Value; if Assigned(FFocusControl) then FFocusControl.FreeNotification(Self); end; procedure TSimpleExample.Notification(AComponent : TComponent; Operation : TOperation); begin if (Operation = opRemove) and (AComponent = FocusControl) then FFocusControl := Nil; end; Cuando establecemos la propiedad FocusControl lo primero es verificar si ya está asociada a un componente. Si este es el caso necesitamos decirle al componente original que ya no necesitamos saber cuando es destruído. Una vez que la propiedad hace referencia al nuevo componente le informamos a este que necesitamos una notificación cuando es liberado. El resto de nuestro código continúa igual ya que el componente al que hacemos referencia sigue llamando a nuestro método de notificación. Conjuntos Conjuntos Esta sección es realmente bastante simple y no tardaremos en cubrirla. Consideramos que ya conocemos como crear tipos ordinales. type TComponentOption = (coDrawLines, coDrawSolid, coDrawBackground); Propiedades de este tipo se mostrarán en un combo con la lista de todos los valores posibles, pero aveces necesitamos una combinación de muchos de estos valores. Aquí es donde entran en juego los conjuntos. Type TComponentOption = (coDrawLines, coDrawSolid, coDrawBackground); TComponentOptions = set of TComponentOption; Publicar una propiedad del tipo TComponentOptions resultará en que aparece un [+] al lado del nombre de la propiedad. Este botón expande las opciones de la propiedad. Para cada elemento en TComponentOption veremos una propiedad Boolean (tilde check box), podemos incluir / excluir elementos del conjunto estableciendo el valor a True / False. Como vemos, es sencillo verificar / alterar elementos en un conjunto en nuestro componente. if coDrawLines in OurComponentOptions then DrawTheLines; o procedure TsomeComponent.SetOurComponentOptions(const value : TComponentOptions); begin if (coDrawSolid in Value) and (coDrawBackground in value) then {raise an exception} FOurComponentOptions := Value; Invalidate; end; Propiedades Binarias Propiedades Binarias Aveces es necesario escribir nuestras propias rutinas de streaming para leer y escribir las propiedades de tipos propios (así es cómo Delphi lee / escribe las propiedades de arriba a la izquierda en los componentes no visibles sin tener que publicar las propiedades en el inspector de objetos). Por ejemplo, una vez escribí un componente para dar forma al formulario en base a un bitmap. El código al momento de convertir el bitmap en una ventana era extremadamente lento y no posibilitaba su uso. La solución fue convertir los datos en tiempo de diseño, y hacer un stream con los datos resultantes de la conversión. Crear una propiedad binaria es un proceso de tres pasos. 1. Escribir un método para escribir los datos. 2. Escribir un método para leer los datos. 3. Decirle a Delphi que tenemos una propiedad binaria, y pasarle nuestros métodos de escritura / lectura. type TBinaryComponent = class(TComponent) private FBinaryData : Pointer; FBinaryDataSize : DWord; procedure WriteData(S : TStream); procedure ReadData(S : TStream); protected procedure DefineProperties(Filer : TFiler); override; public constructor Create(AOwner : TComponent); override; end;Delphi llama DefineProperties cuando necesita hacer stream nuestro componente. Todo lo que necesitamos es sobre escribir este método, y agregarle una propiedad, ya se usando TFiler.DefineProperty o TFiler.DefineBinaryProperty. procedure TFiler.DefineBinaryProperty(const Name: string; ReadData, WriteData: TStreamProc; HasData: Boolean); constructor TBinaryComponent.Create(AOwner: TComponent); begin inherited; FBinaryDataSize := 0; end; procedure TBinaryComponent.DefineProperties(Filer: TFiler); var HasData : Boolean; begin inherited; HasData := FBinaryDataSize <> 0; Filer.DefineBinaryProperty('BinaryData',ReadData, WriteData, HasData ); end; procedure TBinaryComponent.ReadData(S: TStream); begin S.Read(FBinaryDataSize, SizeOf(DWord)); if FBinaryDataSize > 0 then begin GetMem(FBinaryData, FBinaryDataSize); S.Read(FBinaryData^, FBinaryDataSize); end; end; procedure TBinaryComponent.WriteData(S: TStream); begin //This will not be called if FBinaryDataSize = 0 S.Write(FBinaryDataSize, Sizeof(DWord)); S.Write(FBinaryData^, FBinaryDataSize); end;Primeramente sobre escribimos DefineProperties. Una vez que hecho esto definimos una propiedad binaria con los valores: · BinaryData : El nombre de la propiedad invisible que usaremos. · ReadData : El procedimiento responsable de leer los datos. · WriteData : El procedimiento responsable de leer los datos. · HasData : Si esto es falso ni siquiera se llama al procedimiento WriteData. Persistencia Una explicación rápida sobre persistencia es necesaria para poder referirnos a ella en las siguientes secciones. Persistencia es lo que hace posible que Delphi lea y escriba propiedades de todos sus componentes. TComponent deriva (desciende) de una clase llamada Tpersistent (ver imagen de más arriba, herencia de TWinControl). TPersistent es sencillamente una clase de Delphi con la capacidad de tener propiedades que son leídas y escritas por Delphi, lo que significa que todas las clases descendientes de ella tienen la misma característica. Colecciones Colecciones A medida que progresamos en este artículo vamos viendo características más complejas de los componentes. Las colecciones (Collections) son uno de los tipos de propiedades (tipos de datos) estandar de Delphi mas complejas. Si colocamos un TDBGrid en un formulario y vemos sus propiedades en el inspector de objetos, veremos una propiedad llamada columnas (Columns). Columns es una propiedad de tipo colección, cuando hacemos click en el botón […] vemos una pequeña ventana emergente (pop up). Esta ventana es el editor de propiedades estandar para las propiedades TCollection (y los descendientes de TCollection). Cada vez que se presiona el botón New vemos que se agrega un nuevo elemento a la lista (un elemento TColumn), seleccionando este elemento en la lista aparecerán sus propiedades en el inspector de objetos, así se pueden cambiar sus propiedades y eventos. Cómo se hace esto? La propiedad Columns desciende de TCollection. TCollection es similar a un arreglo, el cual contiene una lista de elementos TCollectionItem. Ya que TCollection desciende de TPersistent es posible hacer fluir (stream) esta lista de elementos, similarmente, TCollectionItem también es descendiente de TPersistent y tambien puede hacer fluir (stream) sus propiedades. Lo que tenemos es un elemento tipo arreglo capaz de hacer fluir todos sus elementos y propiedades. Lo primero por hacer cuando creamos una estructura propia basada en TCollection / TCollectionItem es definir nuestro propio elemento CollectionItem. type TOurCollectionItem = class(TCollectionItem) private FSomeValue : String; protected function GetDisplayName : String; override; public procedure Assign(Source: TPersistent); override; published property SomeValue : String read FSomeValue write FSomeValue; end;Lo que hacemos es crear una clase descendiente de TCollectionItem. Hemos agregado una propiedad token llamada "SomeValue", sobre escribiendo la función GetDisplayName (para modificar el texto que se muestra en el editor preestablecido (default)), y finalmente sobre escribimos el método Assign para permitir que TOurCollectionItem sea asignada a otro TOurCollectionItem. Si no realizamos este último paso el método Assign de nuestra clase Collection no funcionará! procedure TOurCollectionItem.Assign(Source: TPersistent); begin if Source is TOurCollectionItem then SomeValue := TOurCollectionItem(Source).SomeValue else inherited; //raises an exception end; function TOurCollectionItem.GetDisplayName: String; begin Result := Format('Item %d',); end;La implementación de TOurCollection es mucho más compleja, y requiere un poco mas de trabajo. TOurCollection = class(TCollection) private FOwner : TComponent; protected function GetOwner : TPersistent; override; function GetItem(Index: Integer): TOurCollectionItem; procedure SetItem(Index: Integer; Value: TOurCollectionItem); procedure Update(Item: TOurCollectionItem); public constructor Create(AOwner : TComponent); function Add : TOurCollectionItem; function Insert(Index: Integer): TOurCollectionItem; property Items[Index: Integer]: TOurCollectionItem read GetItem write SetItem; end;Hay una serie de puntos a ver para cubrir la explicación de esta declaración, así que empezamos desde arriba y vemos uno por uno. GetOwner es un método virtual introducido en TPersistent. Es necesario sobre escribirlo ya que el código por defecto de este método devuelve Nil. En nuestro caso modificamos el constructor para recibir solo un parámetro (AOwner : TComponent). Almacenamos este valor en FOwner, el cual es devuelto como resultado de GetOwner (TComponent desciende de TPersistent, por consiguiente el resultado es un tipo válido). constructor TOurCollection.Create(AOwner: TComponent); begin inherited Create(TOurCollectionItem); FOwner := AOwner; end; function TOurCollection.GetOwner: TPersistent; begin Result := FOwner; end; Create no solo almacena al dueño (owner) (el cual es requerido por el inspector de objetos para funcionar correctamente), también le dice a Delphi cual es nuestra clase CollectionItem haciendo la llamanda "inherited Create(TOurCollectionItem)". GetItem / SetItem se declaran de la misma forma que en TCollection, pero en vez de trabajar sobre TCollectionItem trabajan en la nueva clase TOurCollectionItem. Luego son usados en nuestros elementos de propiedad (item property). Update como el anterior es un reemplazo directo del original, solo que trabajando en nuestra nueva clase TCollectionItem. Add / Insert ambos son responsables de agregar elementos a la lista, fueron reemplazados para devolver objetos del tipo (clase) apropiado. Por último introducimos la propiedad "Items" para reemplazar a la original, así obtenemos un resultado de TOurCollectionItem en vez de TCollectionItem ahorrándonos el problema innecesario de moldear (cast) el tipo del resultado cada vez que es devuelto. Finalmente vemos un ejemplo de como introducir esta característica en las propiedades de nuestro propio componente. TCollectionComponent = class(TComponent) private FOurCollection : TOurCollection; procedure SetOurCollection(const Value: TOurCollection); public constructor Create(AOwner : TComponent); override; destructor Destroy; override; published property OurCollection : TOurCollection read FOurCollection write SetOurCollection; end;Es tan sencillo como eso. El trabajo duro es escribir la clase TCollection. Nuestro constructor crea la clase colección, el destructor la destruye, y SetOurCollection hace esto: constructor TCollectionComponent.Create(AOwner: TComponent); begin inherited; FOurCollection := TOurCollection.Create(Self); end; destructor TCollectionComponent.Destroy; begin FOurCollection.Free; inherited; end; procedure TCollectionComponent.SetOurCollection( const Value: TOurCollection); begin FOurCollection.Assign(Value); end;Como ya vimos, el parámetro (Self) pasado a TOurCollectionItem.Create es almacenado en la variable FOwner de TOurCollection, la cual es devuelta como resultado de GetOwner. Algo a tener en cuenta es que en SetOurCollection no establecemos FOurCollection := value como reemplazando el objeto (los objetos son simples punteros), asignamos el valor a nuestra propiedad. Versiones posteriores de Delphi hacen que esto sea aún mas simple. En vez de tener que sobre escribir GetOwner en nuestra clase Collection, en cambio ahora podemos derivar (descender) nuestra clase Collection de TOwnedCollection. TOwnedCollection es un contenedor para TCollection con este trabajo hecho por nosotros. Sub-propiedades Sub-propiedades Anteriormente en este artículo vimos como es posible crear una propiedad extensible. La limitación de la técnica anterior era que cada sub-item aparecía como una propiedad Boolean. En la siguiente sección demostramos como crear propiedades extendibles que pueden contener propiedades de cualquier tipo. Si un componente requiere una propiedad que es de tipo registro (record), esto podría implementarse fácilmente poniendo cada una de las propiedades en forma separada. Si nuestro componente necesita introducir dos o mas propiedades del mismo tipo de complejidad (records) la representación de nuestro inspector de objetos se vuelve repentinamente mas complicada. La respuesta es crear una estructura compleja (parecida a un registro de objetos) y publicar esta estructura como una propiedad cuando sea necesario. El problema evidente es que Delphi no sabe como representar esta propiedad, a menos que le digamos como. Crear un editor de propiedades super desarrollado (con diálogos, etc) sería mortal, afortunadamente Delphi provee una solución. Como vimos anteriormente, los flujos (streams) internos de Delphi están basados en la clase TPersistent. El primer paso es entonces derivar (descender) nuestra estructura compleja de esta clase. type TExpandingRecord = class(TPersistent) private FIntegerProp : Integer; FStringProp : String; FCollectionProp : TOurCollection; procedure SetCollectionProp(const Value: TOurCollection); public constructor Create(AOwner : TComponent); destructor Destroy; override; procedure Assign(Source : TPersistent); override; published property IntegerProp : Integer read FIntegerProp write FIntegerProp; property StringProp : String read FStringProp write FStringProp; property CollectionProp : TOurCollection read FCollectionProp write SetCollectionProp; end;En la estructura de arriba hemos creado un descendiente de TPersistent y le pusimos tres propiedades como ejemplo, un entero (integer), una cadena (string), y la colección que habiamos creado antes TOurCollection. El constructor y el destructor simplemente se encargan de crear y destruir el objeto CollectionProp, y SetCollectionProp está puesto para impedir que la referencia a este objeto se pierda (hacemos Assign(value), en vez de FCollectionProp := value). Considerando que Assign está implementado para permitirnos asignar las propiedades de TExpandableRecord a otro TExpandableRecord (nuevamente, es innecesario porque necesitaremos Assign cuando este implementado finalmente como propiedad de un componente). procedure TExpandingRecord.Assign(Source: TPersistent); begin if Source is TExpandingRecord then with TExpandingRecord(Source) do begin Self.IntegerProp := IntegerProp; Self.StringProp := StringProp; //This actually assigns Self.CollectionProp := CollectionProp; end else inherited; //raises an exception end; constructor TExpandingRecord.Create(AOwner : TComponent); begin inherited Create; FCollectionProp := TOurCollection.Create(AOwner); end; destructor TExpandingRecord.Destroy; begin FCollectionProp.Free; inherited; end; procedure TExpandingRecord.SetCollectionProp(const Value: TOurCollection); begin FCollectionProp.Assign(Value); end;Una vez que este código fue implementado el trabajo duro ya esta hecho. Ahora implementar esta clase como un objeto es algo directo. TExpandingComponent = class(TComponent) private FProperty1, FProperty2, FProperty3 : TExpandingRecord; protected procedure SetProperty1(const Value : TExpandingRecord); procedure SetProperty2(const Value : TExpandingRecord); procedure SetProperty3(const Value : TExpandingRecord); public constructor Create(AOwner : TComponent); override; destructor Destroy; override; published property Property1 : TExpandingRecord read FProperty1 write SetProperty1; property Property2 : TExpandingRecord read FProperty2 write SetProperty2; property Property3 : TExpandingRecord read FProperty3 write SetProperty3; end;El constructor tendrá que crear estos tres objetos usados como propiedades, el destructor obviamente tendrá que hacer lo propio. El procedimiento SetPropertyX hará Assign(Value) al objeto correspondiente. Compilamos este componente en un paquete y ponemos un componente TExpandingComponent en nuestro formulario. Al mirar en el inspector de objetos vemos que todas nuestras propiedades TExpandingRecord tienen al lado un [+], este botón expande nuestra propiedad para revelar todas sus sub-propiedades. A primera vista todo parece bien, nuestras propiedades se muestran con un sub-conjuto expandible de propiedades, pero no todo es tan perfecto como parece ser. Al hacer click en el botón […] de nuestra colección "CollectionProp" no se invoca al editor estandard de TCollection, de hecho, no pasa nada. Y ahora qué hicimos mal?, la respuesta es nada. El error no es nuestro, la falla proviene de los desarrolladores de Delphi, Borland. Aunque me gusta pensar que los desarrolladores de Delphi son infalibles, no lo son, y aveces también cometen errores, y este es un ejemplo. Al registrar un editor de propiedades se puede limitar el alcance de los componentes a los cuales se aplica. Se puede especificar que este solo trabaje con ciertos nombres de propiedades, o solo ciertos componentes, y esto es lo que hemos hecho. Aunque la arquitectura de Delphi define que el primer objeto con capacidad de streaming es TPersistent, alguien en Borland registró al editor de propiedades para trabajar solo con objetos descendientes de TComponent. Un descuido seguramente, pero no ayuda en nada. La solución a este problema es hacer descender nuestro TExpandableRecord de TComponent en vez de TPersistent, entonces el editor de propiedades será invocado. El problema es que el editor de propiedades que trae Delfi por defecto muestra un combo para las propiedades de tipo TComponenet y sus descendientes, en vez de mostrar una lista expandible de sub-propiedades. La solución total a este dilema radica en los editores de propiedades, y por último en este artículo veremos este tema. Fin de la parte 2/3 En el próximo post veremos el editor de propiedades de los componentes, cómo escribir editores de propiedades para nuestros componentes y cómo crear componentes ocultos ´hidden´. Nos vemos...

3
0
D
Delphi cómo crear un componente (1/3)
OfftopicporAnónimo5/9/2011

Creando componentes propios en Delphi (1/3) - el objetivo es proporcionar un buen punto de partida completamente en castellano - se utiliza Delphi 2009 para hacer las capturas y demo, pero es equivalente en los anteriores y sucesivos - es una traducción casera, no tiene una semántica perfecta sobre poo o delphi, se utilizan términos conocidos para hacerlo accesible a cualquier programador - no se responderán preguntas, no tengo mayores conocimientos, hay libros y foros especializados en estos temas - para ser buen "escritor" primero hay que ser buen "lector" Por qué, cuándo y cómo es necesario escribir un componente Artículo publicado por: Peter Morris http://delphi.about.com/library/bluc/text/uc080701a.htm "Este artículo apareció originalmente en Delphi Developer. Copyright Pinnacle Publishing, Inc. Todos los derechos reservados." Esta primera parte muestra algunos de los mejores enfoques para crear componentes, y al mismo tiempo proporciona consejos para decidir cuál es la mejor clase base a usar como ancestro (herencia), el uso de declaraciones virtuales (virtual), las complejidades de la sobre escritura (override), y así sucesivamente. En la segunda parte se verá cómo escribir las propiedades avanzadas, como escribir el stream por defecto para esas propiedades y sub propiedades. La parte final cubrirá los editores de propiedades de los componentes, como escribir editores dedicados para las propiedades de los componentes, y la forma de escribir componentes "ocultos". Las dos primeras cosas que hay que preguntarse son: por qué debería escribir un componente, y cuándo debería escribirlo. La primer pregunta es fácil de responder, de hecho, tiene muchas respuestas. • Facilidad de uso: Código encapsulado significa que simplemente se coloca el mismo componente una y otra vez en varios formularios sin escribir una simple línea de código. • Depuración: El código centralizado facilita la reparación de toda la aplicación (o el conjunto de aplicaciones) corrigiendo el error en un simple componente y volviendo a compilarlo. • Costo: Hay un montón de compañías que están mas que felices de pagar por tener el privilegio de no reinventar la rueda. La segunda pregunta no es muy difícil de responder tampoco. Cuando te encuentras que tienes que escribir el mismo código mas de una vez es una buena idea escribir un componente, especialmente si el código se comporta diferente en base a los parámetros de entrada. Como son creados los componentes El primer paso es decidir que clase se necesita para usar como base (ancestro) para el componente. Cuando se deriva (descendente) de otra clase se heredan las propiedades, métodos y eventos que esta clase posee. Debajo hay un ejemplo de cómo decidir qué clase se debería heredar cuando se escribe un componente propio. En el caso del método A y el B, se presume que el componente en cuestión es TMemo, por poner un ejemplo, en realidad vamos a utilizar el que sea mas afín a nuestra necesidad. Método / Solución A / Deriva de Tmemo B / Deriva de TcustomMemo C / Deriva de TcustomControl D / Deriva de TgraphicControl E / Deriva de Tcomponent Esto puede parecer un poco complicado, entonces vamos explicar el proceso. A: Cuando sólo se necesita agregar funciones extras a un componente existente se deriva (descendente) de ese componente. Esto le dará automáticamente al nuevo componente toda la funcionalidad y propiedades del componente existente. B: A veces no solo es necesario agregar funcionalidad, también quitarla al mismo tiempo. Comúnmente cuando se escribe un componente primero se escribe un componente TCustomXXXXX donde se declaran todas las propiedades, eventos y métodos dentro de la sección protegida (Protected) del componente. El componente real es derivado (descendente) de esta clase base abstracta. Entonces el truco no es ocultar funcionalidad, sino derivar (descender) el nuevo componente de la versión predefinida (custom) o abstracta del componente y solo publicar e implementar las propiedades que se quieren conservar (efectivamente remover las propiedades y eventos indeseados). C: Cualquier componente que necesita recibir foco necesita un window handle. TwinControl es donde este handle se introduce por primera vez. TCustomControl es simplemente un TWinControl con su propia propiedad Canvas. D: TGraphicControl no tiene un window handle y por lo tanto no puede recibir foco. Componentes como TLabel son derivados (descendentes) de esta clase. E: Algunos componentes no son creados para tener interacción a través de la GUI pero hacen la vida más sencilla. Todo lo derivado (descendente) de TComponent es visible solo al momento de diseño. Un uso típico de estos componentes es para la conexión con bases de datos, timers, etc. Creando el primer componente (Encapsulamiento) Creando el primer componente Una vez que hemos decidido que clase se usará de base, el siguiente paso es crear el componente. Desde el menú de componentes (Component), seleccionamos nuevo componente (New VCL Component) y veremos el siguiente cuadro de diálogo. Ancestor Component: Este es el componente base (clase tipo) que necesitamos para hacer la herencia. Class Name: Este es el nombre de la clase del nuevo componente. Palette Page: Esta es la ficha (tab) de la paleta de componentes en la cual queremos que aparezca el componente, ingresando una ficha inexistente le decimos a Delphi que queremos crear una nueva. Unit: Una unidad es una clase o un conjunto de clases (type), que definen uno o varios componentes (objetos). Package: Los componentes se suelen distribuir en paquetes, es similar a una librería. Escribiendo algo de código Es momento de escribir algo. Este primer ejemplo no tendrá ninguna utilidad excepto demostrar algunos conceptos básicos de cómo escribir componentes. Primero que nada, seleccionamos componente (Component) en el menú principal de Delphi y luego nuevo componente (New VCL Component). Ingresamos TComponent como el "Ancestor Component" >>. TFirstComponent como nombre del nuevo componente. Elegimos la paleta (tool palette) donde el colocarlo >>. A esta altura ya se puede instalar el nuevo componente en un paquete existente (un paquete contiene una colección de componentes) o en un nuevo paquete. Hacer click en "Install to New Package" >>. Una vez que hemos seleccionado un nombre de archivo proyecto (.dproj) y directorio para el nuevo paquete e ingresasmos la descripción hacer click en "Finish". En el siguiente cuadro de diálogo seleccionamos la ubicación y el nombre para el archivo de la unidad (.pas). Luego el paquete se compila e instala automáticamente, una vez que el paquete fue compilado e instalado se registra el nuevo componente. Hasta ahora hemos especificado la clase base, y también el nombre de nuestra nueva clase. Hemos creado un nuevo paquete que contenga nuestro componente y hemos sido presentados con el esqueleto de la estructura de una clase de Delphi. Si vemos el código fuente generado por Delphi (Component1.pas) veremos las secciones Private, Protected, Public, y Published. unit FirstComponent1; interface uses SysUtils, Classes; type TFirstComponent = class(TComponent) private { Private declarations } protected { Protected declarations } public { Public declarations } published { Published declarations } end; procedure Register; implementation procedure Register; begin RegisterComponents('Samples', ); end; end. Encapsulamiento El encapsulamiento es un concepto simple de entender, pero extremadamente importante al escribir componentes. El encapsulamiento se implementa a través de cuatro palabras reservadas: Private, Protected, Public, y Published, vemos que Delphi agregó automáticamente estas secciones en el código fuente del nuevo componente. Private: Los Métodos, Propiedades y Eventos declarados dentro de esta sección solo serán accesibles por la unidad que contiene al componente. Los componentes en una misma unidad pueden acceder unos a las elementos privados de los otros. Protected: Los Métodos, Propiedades y Eventos declarados dentro de esta sección también estarán accesibles a cualquier clase descendiente de esta clase. Public: Los Métodos, Propiedades y Eventos declarados dentro de esta sección son accesibles desde cualquier lugar. Published: Esta sección permite declarar propiedades y eventos que aparecerán en el inspector de objetos (object inspector). Estas configuraciones son valores en tiempo de diseño que son almacenados con el proyecto. Código del componente sin procesar; palabras reservadas Virtual, Dynamic, Abstract y Override Comenzando a escribir el nuevo componente Delphi ahora necesita saber todo sobre nuestro nuevo componente. Ingresamos las siguiente líneas en el código fuente del componente. Nótese que debe agregarse la unidad Windows en la cláusula uses. {uses SysUtils, Classes, Windows;} private { Private declarations } FStartTime, FStopTime: DWord; protected { Protected declarations } function GetElapsedTime : String; virtual; public { Public declarations } procedure Start; virtual; procedure Stop; virtual; property StartTime: DWord read FStartTime; property StopTime: DWord read FStopTime; property ElapsedTime: String read GetElapsedTime; published { Published declarations } end;Lo que hemos hecho es agregar dos variables FStartTime y FStopTime (es común anteponer la letra F al nombre de las variables). Hay dos métodos para controlar estas variables, Start y Stop. Hemos agregado la función GetElapsedTime la cual devuelve FStopTime - FStartTime como una cadena. Finalmente hemos agregado tres propiedades de solo lectura (read-only). Presionando SHIFT-CTRL-C Delphi completará automáticamente el código para la clase o presionando el botón derecho del ratón y seleccionando "Complete class at cursor".unit FirstComponent1; interface uses SysUtils, Classes, Windows; type TFirstComponent = class(TComponent) private { Private declarations } FStartTime, FStopTime: DWord; protected { Protected declarations } function GetElapsedTime : String; virtual; public { Public declarations } procedure Start; virtual; procedure Stop; virtual; property StartTime: DWord read FStartTime; property StopTime: DWord read FStopTime; property ElapsedTime: String read GetElapsedTime; published { Published declarations } end; procedure Register; implementation procedure Register; begin RegisterComponents('Samples', ); end; { TFirstComponent } function TFirstComponent.GetElapsedTime: String; begin end; procedure TFirstComponent.Start; begin end; procedure TFirstComponent.Stop; begin end; end.Luego ingresamos el código respectivo para cada método. {agregar la unidad Windows a la cláusula uses para poder utilizar GetTickCount} { TFirstComponent } function TFirstComponent.GetElapsedTime: String; begin Result := IntToStr(FStopTime - FStartTime); end; procedure TFirstComponent.Start; begin FStartTime := GetTickCount; end; procedure TFirstComponent.Stop; begin FStopTime := GetTickCount; end; end.El código completo queda así.unit FirstComponent1; interface uses SysUtils, Classes, Windows; type TFirstComponent = class(TComponent) private { Private declarations } FStartTime, FStopTime: DWord; protected { Protected declarations } function GetElapsedTime : String; virtual; public { Public declarations } procedure Start; virtual; procedure Stop; virtual; property StartTime: DWord read FStartTime; property StopTime: DWord read FStopTime; property ElapsedTime: String read GetElapsedTime; published { Published declarations } end; procedure Register; implementation procedure Register; begin RegisterComponents('Samples', ); end; { TFirstComponent } function TFirstComponent.GetElapsedTime: String; begin Result := IntToStr(FStopTime - FStartTime); end; procedure TFirstComponent.Start; begin FStartTime := GetTickCount; end; procedure TFirstComponent.Stop; begin FStopTime := GetTickCount; end; end.Prueba piloto Guardamos (Save) la unidad, y volvemos a abrir el paquete (File, Open Project desde el menú, y seleccionamos "Delphi Package" como tipo de archivo), una vez que el paquete este abierto hacemos click el botón "Compile". También se puede abrir el paquete seleccionando Component desde el menú principal y luego Install Packages. Seleccionamos el paquete y luego click el botón "Edit". Ahora podemos agregar un TFirstComponent en un formulario (VCL Forms Application), de hecho, podemos colocar tantos como se desee. Agregamos dos botones Start y Stop (btnStart y btnStop) y agregamos el siguiente código en el formulario, y luego se ejecuta la aplicación de prueba. procedure TForm1.btnStartClick(Sender: TObject); begin FirstComponent1.Start; end; procedure TForm1.btnStopClick(Sender: TObject); begin FirstComponent1.Stop; Caption := FirstComponent1.ElapsedTime; end;Haciendo click en el botón "Start" se marcará el tiempo de inicio (GetTickCount es un comando de la API de Windows (WinAPI) que devuelve el número de milisegundos desde que Windows inicializó). Haciendo click en el botón "Stop" marcará el tiempo de detención, y cambiará el título del formulario. Hay que agregar la unidad Windows para poder utilizar esta función. Virtual, Dynamic, Abstract y Override Vemos la declaración Virtual luego de Start, Stop y GetElapsedTime. El siguiente ejercicio explicará sus usos. Creamos un nuevo componente, derivado (descendente) del componente TFirstComponent (lo llamamos TSecondComponent) y se instala. Con las etiquetas Virtual y Dynamic los programadores de componentes le dicen a Delphi que el método puede ser reemplazado en un clase descendente. Si nosotros sobre escribimos (Override) un método en una clase, nuestro nuevo código será ejecutado en vez del código original. Protected { Protected declarations } function GetElapsedTime : String; override; Luego implementamos el código de arriba como sigue. function TSecondComponent.GetElapsedTime: String; var S : String; begin S := inherited GetElapsedTime; Result := S + ' milliseconds or ' + Format('%.2f seconds', [StopTime - StartTime) / 1000]); end; Nuestro nuevo código es llamado en reemplazo del código GetElapsedTime original, aun las llamadas en TFirstComponent a GetElapsedtime llamarán ahora a nuestro nuevo código. El código original es invocado a través del uso del comando Inherited. Nota: Si no sobre escribimos "override" un método base (porque este no fue declarado como Virtual o porque nos olvidamos), TSecondComponent llamará a nuestro nuevo código, pero hay que tener en cuenta que el código introducido en TFirstComponent continuará llamando al método original de TFirstComponent. El identificador Abstract le dice a Delphi que no espere ningún código de este método. No se debería crear ninguna instancia de un objeto que contenga métodos abstractos (como es el caso de TStrings). Comunmente se crea una clase descendiente por cada clase abstracta y se sobre escriben (override) todos los métodos abstractos (como lo hace TStringList). La diferencia entre Dynamic y Virtual es simplemente cuestión de velocidad vs tamaño. Un método dinámico (Dynamic) requerirá menos memoria en cada instancia, mientras que un método virtual (Virtual) se ejecutará más rápido con un pequeño costo de memoria. Agregando eventos Agregando eventos Pocos pasos son los que hay que seguir para agregar eventos a un componente. Los eventos le permiten al componente comunicarse con la aplicación, notificarle cuando ha sucedido algo importante. Un evento es meramente la lectura / escritura de una propiedad, en lugar de ser un tipo de variable común (como string, integer, etc) es un procedimiento o una función. Creamos un nuevo componente, descentende de TSecondComponent y lo llamamos TThirdComponent. Guardamos la unidad, instalamos el componente, y agregamos el siguiente código. type TState = (stStarted, stStopped); TStateChangeEvent = procedure (Sender : TObject; State : TState) of object; TThirdComponent = class(TSecondComponent) private { Private declarations } FState : TState; FOnStart, FOnStop : TNotifyEvent; FOnStateChange : TStateChangeEvent; protected { Protected declarations } public { Public declarations } constructor Create(AOwner : TComponent); override; destructor Destroy; override; procedure Start; override; procedure Stop; override; property State : TState read FState; published { Published declarations } property OnStart : TNotifyEvent read FOnStart write FOnStart; property OnStateChange : TStateChangeEvent read FOnStateChange write FOnStateChange; property OnStop : TNotifyEvent read FOnStop write FOnStop; end; Los eventos son simples procedimientos o funciones (las menos veces) que pertenecen a una clase (de ahí que vemos la cláusula "of object" en el evento TStateChangeEvent). Por ejemplo, TNotifyEvent es un tipo de evento estándard implementado por Delphi el cual solo pasa el objeto que ha desencadenado el evento, siempre es bueno enviar "Self" (Sender : TObject) como el primer parámetro de cualquier evento ya que el mismo código del evento puede estar siendo usado por múltiples componentes. TNotifyEvent se define como: Type TNotifyEvent = procedure (Sender: TObject) of object; Para llamar un evento de un componente solo hay que verificar si el evento fue asignado y, en este caso, llamarlo. Aquí están sobre escritos los métodos Start y Stop de TSecondComponent para desencadenar (trigger) estos eventos. procedure TThirdComponent.Start; begin inherited; //This calls TSecondComponent.Start FState := stStarted; if Assigned(OnStart) then OnStart(Self); if Assigned(OnStateChange) then OnStateChange(Self, State); end; procedure TThirdComponent.Stop; begin inherited; //This calls TSecondComponent.Stop FState := stStopped; if Assigned(OnStop) then OnStop(Self); if Assigned(OnStateChange) then OnStateChange(Self, State); end; constructor TThirdComponent.Create(AOwner: TComponent); begin inherited; //This is were you initialise properties, and create //and objects your component may use internally FState := stStopped; end; destructor TThirdComponent.Destroy; begin //This is where you would destroy //any created objects inherited; end; Volvemos a compilar el paquete (no olvidar guardar el paquete cada vez que se agrega un componente). Al colocar un nuevo componente en un formulario veremos que hay tres eventos. OnStart, OnStop, y OnStateChange. En el ejemplo Demo3 podemos ver como se usan estos eventos. OnStart pone "Started" en el título (caption). OnStop muestra el tiempo transcurrido OnStateChange Activa / Desactiva el botón correspondiente Start / Stop procedure TForm1.ThirdComponent1Start(Sender: TObject); begin Caption := 'Start'; end; procedure TForm1.ThirdComponent1Stop(Sender: TObject); begin Caption := ThirdComponent1.ElapsedTime; end; procedure TForm1.ThirdComponent1StateChange (Sender: TObject; State: TState); begin btnStart.Enabled := ThirdComponent1.State = stStopped; btnStop.Enabled := ThirdComponent1.State = stStarted; end; Consideraciones a tener en cuenta antes de escribir componentes Normas al escribir componentes Finalmente cubriremos algunos detalles sobre la implementación de componentes, incluyendo algunos aspectos sobre componentes base y normas de codificación. Creando y destruyendo el componente Los objetos son creados a través de un constructor y destruidos a través de un destructor. Sobre escribir (override) un constructor tiene un propósito triple: 1. Crear cualquier objeto que él mismo contiene (sub objetos) 2. Inicializar los valores de la clase (propiedades, etc) 3. Levantar una excepción y evitar que se cree la clase Es norma llamar al constructor heredado (del objeto base) desde dentro del constructor del componente nuevo así la clase padre puede hacer sus propias inicializaciones, aunque no sea necesario para crear el componente. (El nuevo componente es creado tan pronto como el constructor finalizó, no se crea llamando el constructor heredado) El objetivo de sobre escribir un destructor es simplemente liberar cualquier recurso que fue reservado durante la vida del componente. Se llama al destructor heredado luego de haber liberado estos recursos. Partes comunes de los componentes Paint: Se puede sobre escribir este método para que el componente haga su representación gráfica personalizada. Loaded: Este es llamado en tiempo de ejecución por Delphi, tan pronto como todas las propiedades han terminado de ser definidas cuando el formulario padre es creado. Se puede sobre escribir este método para realizar cualquier acción que dependa de un grupo de propiedades que ya han sido definidas (posición, tamaño, etc). Invalidate: Siempre que el cambio de una propiedad afecte el aspecto visual de un componente se debe llamar a este método. ComponentState: Esta propiedad es muy útil cuando se necesita verificar si el componente existe en tiempo de diseño o ejecución, o si sus propiedades están siendo leídas por un proceso en ejecución. Encapsulando el componente correctamente Es normal escribir un componente como una clase abstracta como TCustomMyClass y luego derivar (descender) el componente de esa clase base. La clase abstracta tendrá casi todos (sino todos) los métodos y propiedades del componente declarados en su sección Protected. Cuando se implementa la clase abstracta simplemente se vuelve a declarar las propiedades en las secciones Public o Published. type TCustomMyClass = class(TComponent) private FSomeString : String; protected procedure SetSomeString(const Value : String); virtual; property SomeString : String read FSomeString write SetSomeString; end; TMyClass = class(TCustomMyClass) published property SomeString; end; Lo bueno de hacer esto es que permitimos que si alguien quiere heredar este componente para hacer uno propio todavía puede quitar algunas propiedades. Vemos como SetSomeString fue declarado como virtual dentro de la sección protected. Esto es bueno porque permite a las clases descendientes responder a los cambios de los atributos y las propiedades, sobre escribiendo los procedimientos que los establecen (leen y escriben). También se aplica a los eventos, donde hay un evento OnStateChange encontraremos a menudo un método DoStateChange, como en el siguiente ejemplo: type TCustomMyClass = class(TComponent) private FOnStateChange : TStateChangeEvent; protected procedure DoStateChange(State : TState); virtual; published property OnStateChange : TStateChangeEvent read FOnStateChange write FOnStateChange; end; procedure TCustomMyClass.DoStateChange(State : TState); begin if Assigned(OnStateChange) then OnStateChange(Self, State); end; En vez de escribir la condición "If assigned(OnStateChange) then" cada vez que cambie de estado, simplemente llamamos "DoStateChange(NewState)". Aparte de ser una sentencia más corta, permite que las clases descendientes sobre escriban DoStateChange y determinen el código necesario que responda al evento. Resumen Hasta ahora hemos visto las normas para implementar componentes. También vimos como escribir nuestros componentes, y determinar la clase base a usar en la herencia de los mismos. Además discutimos los métodos Virtual y Dynamic, y como usarlos para “etiquetar” los componentes. Fin de la parte 1/3 En el próximo post veremos como escribir propiedades personalizadas: datos binarios, colecciones, conjuntos y subpropiedades expandibles. Nos vemos...

85
0
PosteameloArchivo Histórico de Taringa! (2004-2017). Preservando la inteligencia colectiva de la internet hispanohablante.

CONTACTO

18 de Septiembre 455, Casilla 52

Chillán, Región de Ñuble, Chile

Solo correo postal

© 2026 Posteamelo.com. No afiliado con Taringa! ni sus sucesores.

Contenido preservado con fines históricos y culturales.