Programación General > Visual Basic 6.0 e inferiores
Boton Dibujado en un picture. Necesito una Sugerencia
Nebire:
Bueno, vamos por la siguiente parte.
Ya hemos creado los archivos necesarios del proyecto, por tanto ahora abrimos el entorno desde el archivo Group1.vbg que como señalé es el que mantiene unido ambos proyectos el del control y el de prueba.
Este tramo inicial podía haberla añadido al final de la parte de ayer , pero bueno....
Hay que señalar que cuando hay un grupo de proyectos, sólo uno es el 'proyecto inicial', el que se va a ejecutar cuando se arranque el proyecto para su ejecución, en este caso el proyecto inicial es el de prueba ya que el del control actúa como secundario dentro del proyecto de prueba... justo cuando se inicializa el formulario (cuando se carga los controles ya deben haberse cargado, de hecho se carga justo cuando el último control constituyende del formulario se ha cargado) se invoca la carga de todos y cada uno de los controles que lo integran. Para hacer la prueba vayamos al código del formulario y escojamos el procedimiento de evento del formulario initialize, este sería su código :
--- Código: Visual Basic --- Private Sub Form_Initialize() MsgBox "Formulario inicializando"End Sub Acto seguido vamos a la ventana de código del control, también en su procedimiento de evento initialize.... este sería su código:
--- Código: Visual Basic --- Private Sub UserControl_Initialize() MsgBox "Control inicializando"End Sub
Para verificar lo que se ha dicho finalmente exponemos código en el load del formulario:
--- Código: Visual Basic --- Private Sub Form_Load() MsgBox "formulario inicializado y cargando..."End Sub
Ahora ejecutamos paso a paso el grupo de proyectos (tecla F8).. veremos que vamos al procedimiento initialize del formulario, luego al initialize del control y finalmente al load del formulario.
Para el diseño del control esto no tiene mucha importancia, pero es precisamente lo que nos ayudará a entender la cascadade sucesos de arranque del control. Si se piensa bien el evento initialize del formulario ocurre una sola vez, en cambio el evento load del formulario puede ocurrir varias veces durante la vida del mismo, esto mismo sucede con el control, aunque más adelante se detallará esta cuestión. Ahora lo que nos importa es saber si el proyecto de inicio fue exitosamente el de prueba. si fue el del control debemos ir al 'explorador de proyectos' (CTRL + R) y fijarnos cualo de los dos proyectos aparece en negrita, eso marca el proyecto inicial, si es necesario, por tanto pulsamos click sobre el proyecto de prueba y sobre el menú emergente pulsamos en 'establecer como inicial'.
Ya borramos todo el código introducido anteriormente y guardamos el proyecto. Ahora empezamos realmente esta 2ª parte.
____________________________________-
Cuando vamos a crear un control, previamente es conveniente hacer una descripción sobre un papel de que funcionalidad es la que esperamos que tenga, evidentemente siempre en medio del proyecto podremos hacer añadidos y de hecho los haremos a propósito para ver como afecta los añadidos a última hora. Sin embargo cuanto mejor tengamos explicitado lo que queremos que haga en mejores condiciones estaremos para proveer lo que precisa...
Vamos pués a proceder a dar una somera descripción de lo que pretendemos crear... es habitual que la razón por la que queramos construir un control es que determinado existente no tenga tal o cual característica/s por lo que nuestro modelo podría ser una copia en cierto modo similar a otro ya existente pero con algunos cambios.... por ejemplo, a mi nunca me gustó que los botones de microsoft no tuvieran una propiedad 'forecolor', por narices siempre es negro (se puede cambiar empleando APIs, pero no hay una propiedad), si queremos cambiar el color del fondo debemos cambiar la propiedad estilo a 'graphical', sólo entonces toma el color que hayamos designado a la propiedad backcolor, ese comportamiento también nos parece 'idiota', otra cuestión es que por ejemplo admite una imagen, pero se pega al tamaño real, también debe estar establecido estilo a graphical (Command1.style=1: Command1.picture= loadpicture(ruta...)) . Para colmo incluso creando una imagen justo a la medida del botón, si es oscurapor abajo nos 'tapa' el texto del botón... en fin como puede sentirse, hay razones que ya justifican nuestro botón y ya de entrada tenemos unas descripciones de lo que tiene que ser...
El control debe poder cambiar el color del fondo, el color del texto, alinear el texto horizontalmente, admitir una imagen ajustada en todo momento al fondo, tener capacidad para un icono alineado siempre a la izquierda y poseer un borde perimetral que de momento no definimos como se comportará (podría ser fijo o que se hunda al pulsar y se eleve al soltar), debe tener algunas propiedades típicas, activo (enabled), font, etc...
Es una descripción pequeña, pero para empezar a contruirlo nos vale, luego a medida que vayamos diseñando y probando terminaremos de definir tal vez nuevas propiedades o cambios de funcionalidad... esto debe entenderse como algo lógico y razonable, tampoco es estrictamente necesario tener una descripción exhaustiva del mismo, siendo como es para nosotros... A medida que uno se familiariza creando controles bastantes ideas de la descripción están ya en la cabeza y no necesitamos tener que plasmarlas por escrito, en cambio si conviene dejar escrito esas funcionalidades específicas que pretendíamos para el control ya que si no, cabe la posibilidad de que tras algún tiempo sin retomar el proyecto se nos haya olvidado por completo la misma, la funcionalidad que lo haría diferente de otros modelos previos que ya hayamos realizado, por ejemplo puede que este nuevo botón se deba a que queramos unir varios botones en línea y columna formando una tabla y por ello queramos redefinir la forma en que se dibuja el relieve de los mismos... o podríamos querer en un nuevo botón que tenga una pequeña animación cuando se pasa el ratón por encima, o simplemente añadir un sonido cuando se oprime y suelta elbotón... estas 2 últimas funcionalidades las utilñizaremos como un añadido de última hora cuando se dé al control por terminado...
El mejor sitio para dejar escrito la funcionalidad (las ideas de como debe operar) es usar un archivo de texto en la misma carpeta que el control, así siempre estará disponible.
Por tanto empezaremos definiendo propiedades bastante conocidas: Backcolor, forecolor, enabled, Text... pero a mi particularmente me gusta usar el español que es mi idioma, por tanto todas las propiedades y demás se reflejarán en lo posible en español... hay una razón más o menos poderosa para dejarlo en inglés, que ya para el final se indicará si no me olvido, y si me olvido me lo recordais (me refiero al final de la elaboración del control).
Las propiedades pueden usarse tanto en clases como en formularios y por supuesto en los controles que es donde más lo vemos. Las propiedades son un trío de funciones realmente pero que el entorno de VB lo enmascara como si fueran algo distinto de funciones, de hecho podríamos olvidar las propiedades y tratarlas como funciones pero nos perderíamos ciertas funcionalidades del entorno, como mostrar los valores de las propiedades del control en la 'Ventana de propiedades'.
Las propiedades como digo son un trío, una devuelve un valor y las otras 2 entregan un valor. de estas 2 una se utiliza cuando se usa un tipo de datos genérico y la otra cuando se usa como tipo de datos un objeto.
A continuación iremos poniendo código y explicando...
--- Código: Visual Basic --- Public Property Get ColorTapiz() As OLE_COLOR ColorTapiz = UserControl.BackColorEnd Property Public Property Let ColorTapiz(ByVal ct As OLE_COLOR) If ct <> UserControl.BackColor Then UserControl.BackColor = ct UserControl.PropertyChanged "ColorTapiz" ' añadidos posteriores End If End Property Acabamos de definir la propiedad ColorTapiz (colorFondo, BackColor) para el control, analicemos cada parte...
GET: es como una función, devuelve un valor, se usaría así en el formulario: dim X as long: x= CtlBoton1.Colortapiz : msgbox x
Public: porque queremos que esta propiedad sea legible, si quisiéramos que no se pudiera leer pondríamos private y sería una propiedad sin derecho a lectura.
LET: es la asignación de la propiedad, puede verse que es como una función tiene un parámetro que es por donde se recibe el valor, a dicho parámetro le hemos lamado, ct (abreviatura de Colortapiz, yo suelo usar esta convención), este parámetro no es visible desde fuera, luego no importa el nombre que se le dé. Let también se declaró public, si lo hubiéramos declarado private la propiedad estaría privada de escritura, es decir sólo podría invocarse para ser leída pero nunca podría escribirse.
Más adelante se mostrará que es posible dar más de un parámetro a las propiedades, sin embargo debe quedar claro que 1º no es obligatorio el par de procedimientos de la propiedad, puede usarse sólo el get y no aparecer el let, o al revés. 2º si aparecen ambos deben ser congruentes en el tipo de datos y en la cantidad de parámetros, veremos que siempre LET tiene un parámetro más que GET, 3º en las propiedades del control (en el formulario) sólo aparece una propiedad si aparecen ambos métodos y también ambos son públicos, si uno de los 2 es privado no aparece en las propiedades del control. (conviene que luego se pruebe a cambiarlos a private uno de ellos y se verá como deja de aparecer).
La propiedad almacena un color que a fin de cuentas es un tipo de datos long, porqué lo hemos declarado del tipo OLE_COLOR ?, porque cuando se declara de esa manera el entorno de VB nos despliega una 'página de propiedades' para elegir visualmente el color, si utilizamos un tipo de datos long, desde el entorno sólo podríamos introducir valores numéricos desde el teclado. Los controles pueden poseer páginas de propiedades que es casi un pequeño mundo lo mismo que los controles pero que una vez conocidos los controles son más sencillos de conocer, el uso de páginas de propiedades para los controles facilita al programador poder cambiar cómodamente algunas propiedades incluso a varios controles a la vez, la contrapartida es que diseñar una página de propiedades nos lleva más tiempo y hace al control más pesado (el control una vez compilado pesa más)... si al final hay ganas podríamos crear una sencilla página de propiedades.
Véase que usando una propiedad en vez de 2 funciones, ambos métodos se llaman iguales, usando funciones por necesidad cada uno debería tener un nombre distinto)... Ahora ponemos un ejemplo de como se asigna la propiedad del control en el formulario.... Dim x as long : x= 1234567: CtlBoton1.ColorTapiz = x .Aquí escritura y antes dimos un ejemplo de lectura de la propiedad. Antes de seguir, podemos probar ya esta nueva propiedad, para ello debemos ir al formulario y pulsar en el control, en las propiedades del control veremos que aparece la propiedad ColorTapiz y podemos ver como tiene el color que le asignamos (el tipo Ole_color hace que el entorno nos muestre el color visualmente en vez de un número y podemos cambiar el color y ver como el cambio se refleja en el control...por ejemplo cambíalo a azul... IMPORTANTE: si el control en el formulario aparece 'rayado diagonalmente', tal como se dijo ayer es porque está en modo diseño, para poder probarlo en el formulario es preciso ponerlo en modo ejecución, y para ponerlo en modo ejecución tenemos que cerrar la ventana del control (no la de código si no la de la interfaz del control)... esto no lo voy a repetir más veces, debe quedar claro YA.
Sólo nos queda describir el código introducido en los métodos de la propiedad ColorTapiz... para el get le decimos que colortapiz es igual a un valor que el propio control ya mantiene una variable... (Cada contenedor provee determinada funcionalidad, disponible para los objetos insertados en él y los contenedores del entorno de VB proveen propiedades como Backcolor,y algunas otras que se irán describiendo, al objeto Usercontrol, nuestra instancia. Por tanto la línea 'ColorTapiz = UserControl.BackColor' le está diciendo que entregue la propiedad almacenada en la variable Backcolor que provee ya el control.
En cuanto al código de la asignación LET, lo primero que hacemos es verificar si el color recibido es el mismo, y sólo si es distinto se lo asignamos a la propiedad backcolor del usercontrol. La razón de esto es doble por un lado más adelante tenemos que añadir funcionalidad al final del código de este método, dicho código queremos que sólo se ejecute si realmente el color asignado cambió si no estaremos ejecutando cosas para que quede todo como estaba antes. La otra razón es que ahora el valor del color se guarda en la propiedad backcolor del control (en el que hemos delegado), pero si (cambiamos el color del tapiz a azul como se sugirió más arriba al probar y cerramos el entorno y volvemos a abrirlo, volverá a ser rojo, es decir el cambio no se ha mantenido, justo para eso está la siguiente línea: PropertyChanged "ColorTapiz", esta línea le dice al entorno que esta propiedad ha cambiado, sin embargo esta línea no trabaja sola sino que trabaja en conjunto con 2 métodos del control, uno se encarga de guardar a fichero el cambio registrado (cuando le demos al botón de guardar cambios en el proyecto), y la otra funcionalidad se encargará de volver a leer el valor cambiado cuando arranquemos el proyecto, o se ponga en modo de ejecución el proyecto.
La última línea de código del método LET es una línea comentada, más adelante tenemos que introducir código ya que el cambio del color del fondo, implica que nos borrará el resto del contenido gráfico del control, por lo que se añadirá una línea que salte a una función que vuelva a dibujar todo lo que teníamos y que se ha borrado. Como ese método aún no lo tenemos, dejamos una línea comentándolo.
Justo ahora describiremos estos métodos que ayudan a almacenar de forma permanente las propiedades y que permiten luego al ejecutarlo volver a leer ese valor guardado y lo dejamos por hoy, mañana iremos más rápido con las siguientes propiedades una vez que hemos comprendido de forma general acerca de las propiedades, faltan detallesque se irán presentando según surjan los casos.
Para probar que el color no permanece, vayamos al modo ejecución y cambiemos el color del fondo... y acto segudio pulsemos la tecla F5, veremos que aparecerá el formulario pero con el color que le asignamos en la propiedad backcolor de la ventana de propiedades de la interfaz del control.
Ahora añadamos este código al control (abajo del todo):
--- Código: Visual Basic --- Private Sub UserControl_WriteProperties(PropBag As PropertyBag) With PropBag .WriteProperty "ColorTapiz", UserControl.BackColor, Ambient.BackColor End WithEnd Sub Private Sub UserControl_ReadProperties(PropBag As PropertyBag) With PropBag UserControl.BackColor = .ReadProperty "ColorTapiz", Ambient.BackColor End WithEnd Sub Estos métodos pueden localizarse en la ventana de código del control sobre la lista de procedimientos del control (usarcontrol), por tanto elparámetro éste de propbag, lo provee el entorno de VB, este es un objeto interno de VB cuya funcionalidad procede de una interfaz cuyo objetivo es precisamente la de almacenar y recuperar valores de propiedades que se han asignado durante el diseño (impronta), es algo así como si compras un vehículo, cuando se fabrica es metal y debe ser pintado para evitar que se oxide, aunque a los vehículos se los elige el color y se piden se nosenvía cuando la fábrica dispone de una demanda suficiente de ese color para cambiar los tambores de pinturas en el proceso en cadena, por ejemplo para pintar 200 vehículos seguidios de ese color. sin embargo aquí en VB es como si todos salieran con una capa antióxido gris (que en el entorno se llama Ambient.Backcolor y que es el contenedor quien decide ese color, de hecho es el color del contenedor, es como si la fábrica fuera azul por defecto todos los vehículos salieran azules), luego para asignar el color que actualmente hay guardado el objeto Propbag (Bolsa de propiedades) le dice asigna a la variable (Usercontrol.Backcolor) leyendo en las propiedades guardadas ( .Readproperties) el valor de la propiedad ColorTapiz que se guardó bajo ese mismo nombre en el fichero, y si no se encuentra en el fichero un valor guardado tomamos el valor almacenado en usercontrol.backcolor actualmente.
Es decir el método readproperties se compone de 3 partes la asignación (usercontrol.Backcolor =), la invocación del método del objeto pararecuperar el valor (propbag.Readproperties), pero que como usamos with, nos ahorramos escribir en cada línea el nombre del objeto), y los parámetros que necesita esta invocación, que al caso son 2, el nombre de la propiedad guardada y un valor que se asigna por defecto si tal propiedad no se encuentra: UserControl.BackColor = .ReadProperty ("ColorTapiz", Ambient.BackColor) igualmente en vez de poner por defecto el color del contenedor podríamos poner un color específico, por ejemplo: .ReadProperty( "ColorTapiz", vbBlue) o incluso un valor numérico: .ReadProperty ("ColorTapiz", 234567)
El método readproperties ocurre 1 única vez (realmente es un evento que se dispara para el usercontrol) y es cuando el control entra en modo de ejecución, una vez leídas las propiedades ya no se vuelve a invocar dicho método.
Writeproperties, es el método que usa el control para que se guarden las propiedades y ocurre también una sola vez, justo antes de que el control sea descargado de memoria. Esto sólo sucede mientras la 'aplicación cliente' está en diseño, es decir el control está en modo de ejecución, ya que en modo diseño lo que se guarda es el valor de la propiedad que nosostros le dimos en la ventana de propiedades del control, pero ese no es accesible a nosotros. Es decir nosotros hemos diseñado que cada vez que un cliente utilice el control éste aparecerá cuando se cargue por primera vez la instancia el color rojo, sin embargo el cliente querrá y podrá cambiar que cuando su aplicación se ejecute el control aparecerá con el color que él eligió, es ese valor el que nosotros guardamos en writeproperties, los cambios que el cliente introduce cuando está diseñando la aplicación.
Este método consta de un parámetro más que el método readproperties del objeto propertybag: guarda a fichero (writeporperties) una propiedad con el nombre "ColorTapiz", cuyo valor actual está en la variable: Usercontrol.Backcolor (la variable que actalmente contiene su valor) y si el valor no se encuentra o no es válido entonces asigna éste por defecto (Ambient.Backcolor, el del contenedor donde se halleel control). Igualmente que para readproperties podría asignar un valor directo, como vbBlue ó 56789. Conviene en cualquier caso que en ambos sitios el color asignado por defecto sea el mismo en ambos casos
Bien ahora que ya hemos descrito como se logra que las propiedades permanezcan entre sesiones, podemos volver al formulario cambiar el color a uno que no hayamos usado (por ejemplo un violeta) y guardar el proyecto para que ocurra un evento writeproperties y efectivamente se guarden, luego pulsamos la tecla F5, con lo que ahora se crea el control (recordemos el evento initialize que probamos al principio de esta parte) y luego se invoca el evento readproperties, que asigna las propiedades que hemos guardado. en el evento initialize el color será rojo, pero ahora tras el readproperties será el que le dimos la última vez (violeta)...
Y finalmente por hoy ya que hemos guardado el color violeta para esta instancia veamos como al añadir una nueva instancia del control al formulario el color del fondo de éste es el rojo. Ahora debería quedar claro que valores son los que se guardan en writeproperties y se leen con readproperties Y también debería quedar claro el significado del método propertychanged.
También podemos probar a cambiar la propiedad del control por código, no sólo desde la ventana de propiedades de los objetos del formulario. Por ejemplo añade un botón microsoft y escribe código, para que cuando se pulse cambie nuestro control de color o nos diga el color actual.
Después hacer todas las pruebas que queramos, conviene dejar sólo una instancia del control (del que estamos diseñando) en el formulario para no liarnos cuando mañana continuemos...
Mañana ahondaremos en las propiedades y crearemos bastantes más ahora que se ha explicado como crearlas. También mañana explicaremos como se soportan propiedades que ni siquiera existen, backcolor lo posee el usercontrol y enabled, por lo que en estos caso podemos delegar en elas aunque como hemos visto podemos cambiarle el nombre por el camino...
No lo he revisado ya que sale un tocho largo y me llevaría más tiempo, espero que no se me haya escapado ningún gazapo...
Nebire:
Si alguien no se ha acabado de enterar muy bien de las propiedades puede abrir el grupo de proyectos ir al código que escribimos seleccionar la palabra property 1 pulsar la tecla F1 que nos lleva a la ayuda, naturalmente se explicará bien incluso mejor que yo el significado y declaración de la propiedad aunque todo el intríngulis de las propiedades no aparece... pero entre la ayuda y mis indicaciones creo que todo el mundo podría saciar sus dudas.
En el apartado de ayer se explicó gran parte de las propiedades y también se explicó la persistencia (preservación) de los valores de las propiedades. Hoy avanzaremos con más propiedades de nuestro control y se indicarán más detalles acerca de las propiedades a medida que surja cada caso.
Quedamos en que íbamos a requerir determinadas propiedades: ColorTapiz (BackColor), ColorTexto (ForeColor), el propio Texto del botón (Caption o Text), alinear el texto, un Icono, una Imagen autoajustable al botón... pués hoy procederemos con el código para estas propiedades y de fondo otras característica sobre el Usercontrol...
El código para la propiedad ColorTexto (ForeColor)
--- Código: Visual Basic --- Public Property Get ColorTexto() As OLE_COLOR ColorTexto = UserControl.ForeColorEnd Property Public Property Let ColorTexto(ByVal ct As OLE_COLOR) If ct <> UserControl.ForeColor Then UserControl.ForeColor = ct UserControl.PropertyChanged "ColorTexto" ' añadidos posteriores End If End Property Vemos que prácticamente podemos copiar el código del Backcolor y hacer manualmente los cambios (de hecho yo lo he hecho así). ColorTexto es lo que nos va a permitir cambiar el color al texto que se escribe en el botón, de momento no hemos realizado la propiedad texto, por lo que aparece también la línea comentada 'añadidos posteriores. Lo dicho ayer sobre Usercontrol.BackColor es aplicable también sobre Usercontrol.ForeColor, es una propiedad también sobre la que delegamos ya que la 'plantilla' que se nos presenta del contrl la trae, estas propiedades sobre las que podemos delegar pueden verse sobre la ventana depropiedades de la ventana de la interfaz del control. Igualmente si por defecto deseamos que la aplicación cliente tenga nada más cargar una instancia en un formulario un color determinado es en esa ventana donde debemos designarlo. Yo lo dejo en negro.
Debe quedar claro desde ya que todas las propiedades que vayamos a añadir y que el control posee delegaremos sobre ellas, salvo que la funcionalidad sea totalmente distinta a la que realiza la plantilla... si surge un caso así ya se explicará.
Es conveniente (no obligatorio) que cada vez que añadimos una propiedad hagamos los propio para su persistencia, añadiendo las entradas correspondientes en los métodos Readproperties y Writeproperties... Yo copio una entrada anterior, pego y luego la modifico... entonces ya tendremos esto:
--- Código: Visual Basic --- Private Sub UserControl_ReadProperties(PropBag As PropertyBag) With PropBag UserControl.BackColor = .ReadProperty("ColorTapiz", Ambient.BackColor) UserControl.ForeColor = .ReadProperty("ColorTexto", Ambient.ForeColor) End WithEnd Sub Private Sub UserControl_WriteProperties(PropBag As PropertyBag) With PropBag .WriteProperty "ColorTapiz", UserControl.BackColor, Ambient.BackColor .WriteProperty "ColorTexto", UserControl.ForeColor, Ambient.ForeColor End WithEnd Sub Es decir ayer pusimos la del color tapiz ahora metemos la del color texto.... en lo sucesivo sólo pondré la línea concreta como recordatorio, ya vosotros sabeis donde debe añadirse (a continuación de la anterior y dejamos una nueva línea para la siguiente entrada... bueno esto al gusto).
Ahora vamos con la siguiente propiedad; Texto. Vemos que el control no tienen una propiedad Text ni Caption, esto significa que no podemos delegar en ella sino que la tenemos que contruir completamente. Al hacerlo se entenderá fácilmente su lógica y se explicará como se le cede su primer valor.
--- Código: Visual Basic --- ' esta línea de código debe ir arriba del todo, por encima de cualquier declaración de propiedades y funciones...Private p_Texto As String ' el texto del botón se almacena en esta variable internamente. ' esta línea podemos ponerla a continuación (debajo de) la propiedad ColorTexto.Public Property Get Texto() As String Texto = p_TextoEnd Property Public Property Let Texto(ByVal t As String) If t <> p_Texto Then p_Texto = t PropertyChanged "Texto" ' añadidos posteriores End If End Property Como se puede ver, el valor de la propiedad realmente la almacenamos en una variable (p_Texto) declarada privada, por tanto no es accesible desde fuera y debe verse como lógicamente la hemos declarado de tipo string. Surge una pregunta lógica de parte del aprendiz, porqué no guardamos directamente la propiedad sobre la variable p_Texto ?. De hecho podríamos hacerlo, pero entonces si tenemos algo como esto:
--- Código: Visual Basic --- public TextoBoton as string ...Es realmente una propiedad con todas las de la ley, pero presenta unos inconvenientes para determinadas situaciones: 1º Si utilizamos una propiedad así, no hay forma de saber cuando el cliente ha cambiado su valor, por tanto tampoco sabemos cuando debe actualizarse lo que deba actualizarse.
2º Si necesitamos garantizar que el valor que introduce el cliente (el cliente en este caso es el programador que utiliza el control compilado) cumpla ciertos requisitos no tenemos forma de hacerlo porque como se indicó antes no sabemos cuando se introduce un nuevo valor.
Al expresarse las propiedades en forma de 2 funciones, una que entrega el valor y otra que lo recibe, la que lo recibe cuenta con 2 notificaciones, a) que se recibe un valor, B) si lo deseamos podemos comprobar si ese valor es distinto del actual, es decir si cambia de valor. Hasta ahora si os fijais en el código de las 3 propiedades que llevamos hasta el momento, en los 3 casos hemos verificado si el valor que se introduce es distinto del actual, ya se explicó ayer porque hacemos esa comprobación, no siempre la haremos como severá más adelante, básicamente cada vez que una comprobación se amás engorrosa o conlleve más tiempo que hacer las operaciones que un cambio supone.
Hemos creado la propiedad texto, pero sabemos que cuando se declara una variable antes de usarse su valor es el que VB asigna por defecto, en el caso de los números (byte, long,etc...) siempre valen 0 y en el caso de los tipo string valen "" es decir una cadena vacía. Naturalmente nosotros podemos querer que ya de entrada cuando se cree una instancia sobre el formulario del cliente éste texto (y en general casi todaslas propiedades), tenga ya un valor concreto y determinado por nuestro interés, etc... bien esto se llama inicialización de variables y nos interesa que sólo ocurra una vez, justo cuando se ha creado la instancia, luego ya sabemos que writepoprties guardará el valor y que readproperties lo recuperará, además si el cliente cambia el valor entonces el que se guardará y luego se leerá será ese valor y no el que nosotros inicialmente le dimos, para que este funcionamiento sea así, por tanto este valor debe ejecutarse una sola vez, justodurante la creación por primera vez de la instancia. es preciso saber que el evento initialice ciertamente crea la instancia, sin embargo cuando se pasa al modo ejecución se destruye y es vuelta a crear es decir se vuelve a ejecutar initialize, parece pués un buen sitio para colocar ese código, pero claro si se carga y descarga 10 veces el formulario, también 10 veces se ejecuta initialize y por tanto 10 veces le estamos asignando nuestro valor 'de capricho' siendo que luego 10 veces también se ejecutará el readproperties... no sería más útil que sólo se ejecutara 1 única vez ?.... pués esto es precisamente lo que hace un procedimiento del usercontrol.... ponemos el código (puede encontrarse el método en la lista de procediemientos de eventos cuando en la lista de la izquierda tenemos seleccionado el usercontrol, si tenemos seleccionado general, aparecerán las propiedades y funciones que vayamos creando ):
Por tanto en la lista izquierda seleccionamos usercontrol y en la lista derecha desplegamos hasta encontrar 'InitProperties'
--- Código: Visual Basic --- Private Sub UserControl_InitProperties() p_Texto = UserControl.Extender.NameEnd Sub En este código hemos asignado el valor que tomará justo cuando el cliente inserta una instancia del control, sólo en esa ocasión ocurre el evento initproperties, por tanto en efecto se ejecutauna sola vez, aunque siempre sucede (cuando sucede) justo después del evento initialize (que de momento no lo tenemos presente, porque de momento no le hemos asignado código).
Hay nuevamente que recordar que hemos añadido una línea comentada, que está destinada a poner algó de código más adelante, de momento podemos cambiar el valor del texto, pero en nuestro botón no aparece el texto, esa línea comentada es justo donde le indicaremos cuando dónde y cómo dibujarse, pero esto loharemos cuando tengamos más elementos de juicio para dibujarlo, concretamente cuando decidamos como funcionará el alineamiento, ya que pensamos también incluir un icono, es lógico que el texto no quede cubierto por dicho icono... justo cuando describamos esas 2 propiedades pasaremos a dibujar el texto, porque ya tendremos definido todos los elementos que afectan al texto.
Ahora comentamos la línea de código: p_Texto = UserControl.Extender.Name
Podríamos haber puesto p_Texto = "Viva Yo y mi botón", y eso sería lo que siempre saldría cuando un cliente pusiera una nueva instancia del control sobre un formulario. Sin embargo lo lógico (y que estamos acostumbrado) es que cuando creamos un boton de microsoft , éste se llama Command1 y si ponemos otro este se llama Command2 y el nombre es precisamente el texto que contiene el botón. Esto es coherente y en esa coherencia nos movemos. Entonces que significa la parte derecha de la igualdad de p_Texto ?. Bien digamos que mientras se crea el control, VB crea y mantiene unas propiedades siempre vinculadas a cada control, esas propiedades están almacenadas en un objeto llamado VbControlExtender y de la que el usercontrol mantiene una instancia a través de la propiedad extender. Para curiosos decir que estas propiedades son aquellas que son propias e intrínsecas del control PERO DE las que el contenedor, realiza un seguimiento.
Ayer hablamos de otro objeto que es una propiedad del usercontrol Ambient. Conviene no confundir uno con otro. Extender, Por ejemplo además del nombre del control, mantiene la propiedad de posición Left, Top y de tamaño Width y Height... si quereis saber con exactitud cuantas y cuales son las propiedades alojadas en el objeto extender, ir al formulario, seleccionad el control en el formulario y mirad las propiedades que tiene el control en la ventana de propiedades, si está en modo ejecución, pués son todas excepto las que nostros hemos creado hasta el momento, y si el control está en modo diseño (rayado) todas esas propiedades nosotros no las hemos creado, es el propio VB quien se encarga de crearlas para cada control y son aquellas que los contenedores deben tener acceso... al contenedor no le importa ni necesita saber si yo tengo una propiedad llamada Icono ó Realte ó AlineaciónTexto... pero si necesita saber la posición del control, para saber si está parcialmente tapado o no, igual que la propiedad visible, etc... Hay también que decir que algunas de estas propiedades son de sólo lectura...
Podemos por tanto decir que un control se compone de 4 tipos de propiedades:
1 - Las que trae la propia plantilla del usercontrol (BackColor, Forecolor, Font, Picture, etc...) estas podemos usarlas o no, son opcionales no estamos obligados a usarlas si no las necesitamos.
2 - Las que se proveen a través de la propiedad Extender (del objeto VBControlExtender del que el usercontrol tiene una instancia accesible a través de la propiedad Extender) estas son obligadas y por tanto las provee el propio VB, como ya dije, Left, Top TooltipText, Name, Tag... ).
3 - Las que provee el contenedor y que tenemos acceso a ellas a través de la propiedad Ambient (del objeto AmbientProperties, como ), estas son todas de sólo lectura para el control.
4 - Y finalmente las que nosotros implementamos, ColorTapiz, ColorTexto, Texto, AlineacionTexto, Icono...
Tanto extender como Ambient no sólo proveen propiedades sino también funciones y eventos.
Finalmente, como hemos hecho con las otras propiedades, nos aseguramos de su persistencia en el tiempo:
--- Código: Visual Basic --- ' en el evento readpropertiesp_Texto = .ReadProperty("Texto", UserControl.Extender.Name) ' en el evento writeproperties.WriteProperty "Texto",p_Texto, UserControl.Extender.Name En lo sucesivo ya no se comentará donde irá cada línea de persistencia de la propiedad... nos debe quedar claro donde corresponde cada una. Si pondré cada línea para que no se nos olvide...
Ahora deberíamos avanzar con una propiedad relacionada directamente con el texto AlineacionTexto:
--- Código: Visual Basic --- ' declaramos la variable que contendrá el valor.Private p_Alineacion As TiposDeAlineacion 'AlignmentConstants ' Guarda el valor de alineación del texto. ' Creamos una enumeración para los valores posibles. Este código debe ir arriba del todo, incluso por encima de la declaración de variables.Public Enum TiposDeAlineacion ALINEACION_IZQUIERDA = 0 ALINEACION_CENTRADA = 1 ALINEACION_DERECHA = 2End Enum ' la declaración de la propiedad, la pondremos debajo de la última declaración de propiedad que pusimosPublic Property Get AlineacionTexto() As TiposDeAlineacion AlineacionTexto = p_AlineacionEnd Property Public Property Let AlineacionTexto(ByVal at As TiposDeAlineacion) If at <> p_Alineacion Then p_Alineacion = at PropertyChanged "AlineacionTexto" ' añadidos posteriores End If End Property ' el establcecimiento inicial de la propiedad dentro del evento initproperties, que ahora va quedando así...Private Sub UserControl_InitProperties() p_Texto = UserControl.Extender.Name p_Alineacion = ALINEACION_IZQUIERDA End Sub ' y finalmente el código de persistencia, cada línea donde corresponde ya debeis saber cual en cual... p_Alineacion = .ReadProperty("AlineacionTexto", TiposDeAlineacion.ALINEACION_IZQUIERDA) .WriteProperty "AlineacionTexto", p_Alineacion, TiposDeAlineacion.ALINEACION_IZQUIERDA Ahora comentamos el código... Vemos que esta propiedad tampoc delega el en objeto usercontrol, por lo que hemos declarado una variable para contener su valor (yo a las variables de apoyo a la propiedades siempre les doy un nombre como p_nombrepropiedad , aunque no suelo poner el nombre de la propiedad completa, aldecidir un nombre de propiedad debe pensarse que debe resultar intuitivo al cliente su significado, por lo que no demos escatimar letras, si pusieramos alineación, el cliente podría tener dudas si se refiere al icono, o al control, pero con alineacionTexto, no da lugar a confusión, como internamente el cleinte no tiene acceso y sólo tengo una propiedad que tenga alineación, en la variable privada no me complico, un sencillo comentario al final de cada declaración, nos puede ayudar a recordar para qué se usa.
Se puede ver que hemos creado una enumeración para los 3 valores de la alineación, realmente vb, tiene una enumeración equivalente llamada AlignmentConstants, pero henmos desistido de usarla por 3 razones:
1º Nos sirve de excusa para aprender a usar enumeraciones como tipo de datos para cuando necesitemos variables.
2º La enumeración de windows no es correlativa de derecha a izquierda, nosotros tenemos izquierda, centro, derecha, con valores 0,1 y 2. Vb usa izquierda, derecha y centro que al menos a mi me parece menos coherente.
3º Mantenemos en mente que estamos creando nuestro control en español (que se jodan los estadounidenses :devil: :devil: :devil: así les damos algo de su propia medicina ... es broma).
En vez de una enumeración realmente para la declaración de la variable podríamos haber usado un tipo de datos byte, pero si usamos la enumeración mientras escribamos código el intellisense, nos ayudará sugiriéndono cual delos valores usar, con ello adelantamos al escribir y tampoco nos obliga a memorizar, esto limita en algo un posible error por ese problema de no haber recordado bien el valor... De modo consecuente, también se ha optado por usar el tipo de datos enumeración para la propiedad, porque así en la ventana de propiedades cuando el cliente decida cambiar el valor se despliega una lista precisamente exponiendo los nombres que les hemos dado a nuestras constantes de alineación.
Respecto de la alineación, nosotros sólo realizaremos una alineación horizontal, queda como ejercicio para el interesado cambiarlo por una alineación también vertical. A este respecto VB tiene otra enumeración AlignConstant, que usa valores semejantes a los anteriores pero que además añade top y bottom.
El código de la propiedad no está realmente bien terminado, si el lector es avispado hace rato que debería estar rondándole en la cabeza lo que vamos a describir a continuación...
Puesto que la alineación sólo tiene 3 posibilidades, que le impide al cliente introducir un valor de por ejemplo 25 ?. Realmente si así lo hiciera ahoraese valor sería introducido y si nuestro cálculo de reposicionamiento del texto estuviera ya listo, nos arrojaría un error, nos falta por tanto un 'hervor' en el código de la asignación de la propiedad.
Ese hervor es controlar que valor se introduce, y al efecto sólo debemos permitir que se introduzca uno de los 3 valoresposibles. Hay 2 formas (coherentes) de resolver esto, si el control es para nosotros, (como lo es) lo solucionaremos de una manera 'llana y sencilla' porque conocemos los detalles y sabemos que entenderemos laforma de funcionar que le vamos a dar, ahora bien si el control fuera un encargo de una empresa, lo que deberíamos hacer realmente es marcar un error cuando se introdujera un valor incorrecto, con ello el cleinte sabría que ha introducido un valor noadmitido y se supone que acabaría por 'conocer' bien el control. Pondremos ambos ejemplos de código.
' el código para un encargo de empresa:
--- Código: Visual Basic --- Public Property Let AlineacionTexto(ByVal at As TiposDeAlineacion) If at <> p_Alineacion Then If (at < 0) or (at > 2) Then Raise Error(380) ' el error 380 tiene asignado el significado de: Se ha asignado a una propiedad un valor incorrecto else p_Alineacion = at PropertyChanged "AlineacionTexto" ' añadidos posteriores End If End If End Property
El código, más inteligente, para gente más inteligente:
--- Código: Visual Basic --- Public Property Let AlineacionTexto(ByVal at As TiposDeAlineacion) If at <> p_Alineacion Then If at < 0 Then at = 0 'ALINEACION_IZQUIERDA ElseIf at > 2 Then at = 2 ' ALINEACION_DERECHA end if p_Alineacion = at PropertyChanged "AlineacionTexto" ' añadidos posteriores End If End Property
En este caso hemos optado por no señalar un error, sino que cualquier valor por encima de 2 lo hacemos igual a 2 y cualquier valor menor que 0 lo hacemos = a 0. Cyuando el cliente está en modo de diseño y escribe una propiedad, se pasa el valor al procedimiento de propiedad, lapropiedad actúa, como no marcamos error, el valor queda correctamente almacenado en la variable p_Alineacion,pero partimos de que el cliente introdujo por ejemplo un valor de 37, entonces qué ocurre ?. a continuación (estando en diseño como hemos dicho), el entorno de vb una vez termina el procedimiento let realiza una llamada (de forma transparente) al procedimiento get, para actualizar el valor en la ventana de propiedades, con lo que en efecto al leer el valor de p_Alineacion, le entregará el valor 2 que sustituirá al 37 que introdujo el cliente.
Todavía podría decir que a la propiedad alineacionTexto le falta un hervor más, pero en este caso no se aplica del todo, así que lo dejamos para más adelante cuando una propiedad lo justifique y si vamos terminando el control y no ha sucedido pués aprovechando esta propiedad locomentaríamos y dejaríamos puerta libre para que qien qiera lo aplicara o no.
Sobre la propiedad alineación sólo nos resta decir que al igual que las propiedades anteriores hemosdejado un comentario como recordatorio de que ahí va algo de código, que mañana cuando por fin pongamos el código para los gráficos se añada el código faltante en estas propiedades.
Ahora vamos a añadir una propiedad Font, para que nuestro cliente pueda decidir que tipo de letra se pone al texto del botón, este es el sitio adecuado para introducirlo (la posición).
--- Código: Visual Basic --- ' el código de la propiedad fuente (Font)Public Property Get Fuente() As IFontDisp Set Fuente = UserControl.FontEnd Property Public Property Set Fuente(ByRef f As IFontDisp) Set UserControl.Font = f PropertyChanged "Fuente" ' añadidos posteriores End Property Comentamos el código para esta propiedad. Esta propiedad es una propiedad de ambiente, por lo que también el usercontrol la contiene, por tanto delegamos en ella para contenerla. Ayer hablamos de que había 3 procedmientos de propiedad y que uno de ellos era para cuando el tipo de datos era un objeto. Este es el caso presente, Font es un objeto, por tanto no puede usarse 'property let' sino que deb usarse 'property Set', véase igualmente como la asignación de objeto utiliza Set para almacenarlo en la variable, el procedmiento Get, igualmente debe usarse un set y el cliente necesariamente deberá alamcenar este objeto en una variable que él designe como objeto sino obtendrá un error.
Luego hemos usado la interfaz Ifontdisp, del mismo modo y razón por la que usamos OLE_COLOR para las propiedades de color, ya que así VB presenta el cuadro de diálogo para que el cliente elija la fuente deseada así como sus propiedades (size, Bold, etc...)
Podríamos dividir la asignación de fuente en cada una delas propiedades del objeto, por ejemplo FontSize por un lado y fontName por otro, FontBold,etc... esto sólo interesa si por ejemplo queremos limitar al usuario de una de dichas características, aunque también en lapropiedad Fuente que hemos declarado, podríamos incluir código para hacer lo mismo, es cuestión de gustos, yo prefiero poner una sola propiedad fuente en vez de varias, que llenen la ventana de propiedades... Finalmente hemos elegido pasar el dato por referencia y no por valor, aunque realmente VB hace una copia de la fuente, ya que si no un cambio originaría un cambio de donde procede, es decir el contenedor.
Ahora como ya habeis aprendido, a asignar un valor por defecto para la propiedad y también darle persistencia:
--- Código: Visual Basic --- Private Sub UserControl_InitProperties() p_Texto = UserControl.Extender.Name p_Alineacion = ALINEACION_IZQUIERDA Set UserControl.Font = Ambient.Font ' no es estrictamente necesario, ya que esto sucede automáticamente End Sub ' persistencia: Set UserControl.Font = .ReadProperty("Fuente", UserControl.Ambient.Font) .WriteProperty "Fuente", UserControl.Font, UserControl.Ambient.Font
A continuación, crearemos una propiedad icono y damos por terminado esta parte de hoy.
--- Código: Visual Basic --- ' en la sección de declaración de variables....Private p_Icono As IPictureDisp ' icono que se presenta alineado enla izquierda ' debajo de todas las propiedades hasta el momento....Public Property Get Icono() As IPictureDisp Set Icono = p_IconoEnd Property Public Property Set Icono(ByRef i As IPictureDisp) Set p_Icono = i PropertyChanged "Icono" ' añadidos posteriores End Property ' persistencia. Set p_Icono = .ReadProperty("Icono", Nothing) .WriteProperty "Icono", p_Icono, Nothing Esta propiedad es fácil y rápido de comentar... al como pasó para el color y la fuente usamos un tipo de datos que nos mostrará una ventana de propiedades como cuadro de diálogo para localizar y elegir la imagen deseada que se usará como icono. Hay que señalar que por defecto el icono es... ninguno, es decir si el cliente no elige un icono, no dibujamos nada. Por ello en el evento initproperties no le asignamos nada, ya es nothing...
Ahora guardar todo y podeis probar los añadidos realizados, claro que al ejecutar de momento sólo nos resulta tangible el colorTapiz ya que de momento no hacemos nada gráfico...
Mañana nos centramos en graficar todo lo que tenemos hasta ahora y que se pueda plasmar en el control. Veremos que alinear el texto tiene consecuencias según exista o no un icono, así mismo decidiremos un margen entre el control y el icono, ya que todavía nos falta una propiedad que realice un relieve sobre el contorno del control...
Jimbenit:
Volviste de las vacaciones con todas las baterias puestas (espero que sigas asi por mucho mas tiempo) :lol:
(aun te sigo leyendo, con tu explicacion se me vienen muchas ideas a la cabeza, pero no me apresuraré, esperaré ver el final de tu artículo antes de hacer cualquier cosa)...
Espero mas de tu valiosa explicacion. :good:
Nebire:
Bueno, hoy ya repasando lo de ayer (aparte de la ortografía, porque en el teclado me falla, sobre todo la tecla de espacio) observo que seme escapó un error, lo corrijo allí, pero lo señalo aquí por si alguien ya lo copió, para que sea consciente del cambio...
Donde aparece:
--- Código: Visual Basic --- Public Property Let AlineacionTexto(ByVal at As TiposDeAlineacion) If at <> p_Alineacion Then If at < 0 Then at = 0 'ALINEACION_IZQUIERDA ElseIf at > 2 Then at = 2 ' ALINEACION_DERECHA Else p_Alineacion = at PropertyChanged "AlineacionTexto" ' añadidos posteriores End If Else Raise Error(380) End If End Property Debe sustituirse por:
--- Código: Visual Basic --- Public Property Let AlineacionTexto(ByVal at As TiposDeAlineacion) If at <> p_Alineacion Then If at < 0 Then at = 0 'ALINEACION_IZQUIERDA ElseIf at > 2 Then at = 2 ' ALINEACION_DERECHA end if p_Alineacion = at PropertyChanged "AlineacionTexto" ' añadidos posteriores End If End Property
Puede verse que el bloque de decisión If...elseif, no puede finaliza con un else... sino que tras garantizar un valor aceptable (corrigiendo los valores fuera de ambos extremos) las líneas de código siguiente se aplican para todos los casos...
También a causa del copia-pega, del que partí para modificarlo, sobra la provocación de fallo: Else : Raise Error(380)
Este es el tipo de errores que cuando probamos un control vemos que su comportamiento no es como se esperaba y debemos buscar donde está el origen de la causa, a veces sólo con leerlo de nuevo, el error se ve claramente.
-------------
Nilson, aunque te guardes las preguntas para más adelante, convendría que las fueras haciendo y las guardaras en texto, para que no se te olvide y también conviene que el código lo vayas redactando a la par que yo me explico para que puedas ir probando...
Más tarde expondré la siguiente parte, ahora tengo algunas cosas pendientes que hacer...
Jimbenit:
--- Citar ---también conviene que el código lo vayas redactando a la par que yo me explico para que puedas ir probando...
--- Fin de la cita ---
Me parece bien.
Pero te pedire que pares 1 día para ir probando. Mañana no publiques nada para que me de tiempo de alcanzarte ^_^ ....
El día de mañana probare todas las cosas minuciomente para luego hacerte las preguntas que me asalten.
Eres muy valioso Nebire, ya impresionaste a mi novia que también te lee :lol:
Navegación
[#] Página Siguiente
[*] Página Anterior
Ir a la versión completa