|
Esta sección te permite ver todos los posts escritos por este usuario. Ten en cuenta que sólo puedes ver los posts escritos en zonas a las que tienes acceso en este momento.
Mensajes - Nebire
Páginas: 1 ... 6 7 [8] 9 10 ... 29
176
« en: Jueves 8 de Octubre de 2009, 08:50 »
No. Te aclaro... Talcomo lo tienes hecho, no podrás nunca llamarlo, porque estás creando instancias de la clase que implementa la clase abstracta (Interfaz)..., tal como indicas en el código: Private laClase as Interfaz laClase = new Abstracta
Si 'laclase' es una instancia de la clase 'Interfaz' sólo podrá invocar los métodos que pertenecen a dicha clase, además no admitirá que le asignes la clase 'abstracta', porque es de otro tipo. Lo más parecido a lo que quieres que puedes hacer sería esto: private laclase as Hereda= new abstracta private otraclase as OtraHereda= new abstracta
Pero desde la clase sólo puedes llamar a los métodos definidos en su clase y a los métodos definidos de las clases que anida. Para entender esto lo mejor es compararlos con cajas, imagina que la clase interfaz es una bola de cristal e imagina que la clase abstracta es una caja, metemos la bola en la caja... ahora creamos la clase hereda, sería parecido a meter una copia de la clase abstracta dentro de la clase hereda... pués bien al declarar un clase sólo podrá invocarse los métodos que hay dentro de la caja y a los métodos que hay dentro de las cajas que están dentro de la caja. Así una instancia de interfaz, sólo podrá invocar métodos de otra clase interfaz de ninguna otra. La clase abstracta podrá invocar métodos de la clase abstracta yde la interfaz, de ninfuna otra. La clase hereda sólo podrá invocar los métodos de su propia clase y de la clase abstracta y la clase interfaz. si has creado una nueva clase que hereda de abstracta (hereda2) la clase hereda 'no ve' los métodos específicos de esta clase hereda2. De hecho: * una interfaz se crea precisamente para eso, para compartir métodos (etc..) se llaman igual, pero que luego cada uno hace una cosa distinta, en cambio... * la herencia lo que permite es compartir métodos (etc...) que hacen exactamente lo mismo. La herencia se hace para que una clase amplía lo que realiza la clase heredada. * Una instancia es una copia idéntica de la clase de la que se instancia y se diferencia de otra instancia sólo en los datos que en un momento dado pueda tener cada una. p.d.: como ya hace tiempo que pusiste el mensaje lo dejo aquí, pero si necesitas más aclaraciones...
177
« en: Jueves 8 de Octubre de 2009, 08:03 »
Quería preguntar si se hace limpieza en el foro...
Aparte de los visitantes hay usuarios registrados desde hace mucho tiempo pero sin ni siquiera 1 sola intervención en el foro, entonces digo yo, que fundamento tiene que estén registrados, si son visitantes ocasionales y sólo vienen a mirar, pueden seguir siendo invitados. Por ejemplo un tal 'bugmenot' lleva 16 meses registrado, pero no tiene 1 sólo mensaje publicado.
Tambiébn hay otro tipo de usaurios que llevan años apuntados en el foro pero que sólo hicieron una pregunta y jamás volvieron a lo sumo tienen 3, 5 intervenciones...
No sería deseable eliminar de la base de datos del foro los usuarios cuya antigüedad supere pongamos 8 meses y no hayan publicado nada y del mismo paso no deberían eliminarse usuarios con una antigüedad mayor de (pongamos) 16 meses que tengan publicados menos de 10 mensajes y el último date de hace más de 12 meses...
Está claro que estos vinieron sólo a resolver una cuestión y listo, cuando quieran volver a venir se acordarán siquiera de su contraseña ? o crearán un nuevo usuario para la ocasión ?.
178
« en: Jueves 8 de Octubre de 2009, 07:18 »
Es relativamente fácil. Primero intentaré explicártelo a las claras y luego pasamos a código. imagina que vas a la tienda de un artesano, quieres una estatua, para colocarla en tu casa, el artesano tiene varios modelos de estatuas, las hay grandes y pequeñas las hay apoyadas sobreel suelo, colgadas de la pared y colgadas del techo... Entonces a la hora de elegir que estatua te vas a llevar es obvio que en tu casa deberás disponer de un alojamiento para ella, tal alojamiento debe ser exactamente del tipo que la estatua que elijas. supongamos que eliges una estatua que cuelga del techo, pongamos que estas estatuas miden 4 metros y por debajo de ellas hay un espacio libre de 3 metros, es decir necesita 7 metros de altura aproximadamente si tu casa no tiene esa altura hay que descartar esa estatua yelegir otra, no obstante este caso se sale del propósito del ejemplo, mejor diremos que si tu casa no cumple la altura, el garaje (separado de la casa) lo acondicionarás para que pueda cumplir dicho propósito. Supongamos ahora que eliges la estatua de suelo, esta estatua tiene laparticularidad de que ocupa un espacio de 4x4 metros, lo que quiere decir que la estancia debe disponer de esas medidas y una cantidad adicional para poder rodearla, no hay problema, podemos ensanchar nuestro garaje como prefiramos... Bien, esto mismo es lo que se debe hacer en programación. La tienda de estatuas es en tu caso la base de datos, y el formulario donde quieres mostrarlo hace las veces del garage. Los datos que tomas de la base de datos tienen unas especificaciones, esas mismas especificaciones tiene que tenerlas el formulario que lo va a recibir... En el formulario que busca: Recibir el nº de cédula a buscar buscar en la BD el elemento deseado y recibir los datos asociados Si no se encuentra nada mostrar mensaje no hay coincidencias Si se encuentra, enviar resultado al formulario de exhibiiiiición de resultados En la práctica esos 2 condicionales pueden ser una misma línea de código. En un módulo: ' declaramos las especificaciones del tipo de datos que vamos a usar en diferentes lugares 'para el caso se ha supuesto una estructura de tipo persona public type Personas Nombre as string Apellido1 as string Apellido2 as string NumeroCedula as string end type ' variable de compartición dentro del proyecto public Persona as Personas
Cuando busquemos en la base de datos, el resultado lo pasamos a Persona, acto seguido llamamos al formulario de exhibición Persona=.... call formExibirResultados.show(1) ' lo llamamos como modal... ' En el formulario de exhibición: ' Si no quieres mantener una variable compartida entre proyectos, entonces debes tener una variable pública en este formulario Public per as personas private sub Form_Load Me.Show Text1.text= persona.Nombre Text2.text= persona.Apellido1 Text3.Text= persona.apellido2 Text4.text= Persona.NumerCedula end sub ' este formulario tendría un botón de aceptar que al pulsarlo cierra el formulario
Como puedes ver nuestro formulario de exiihibición, está 'acondicionado', como haríamos con el garaje, para recibir el tipo de datos que va a utilizar, puesto que nuestra 'estatua' es un tipo personas, nuestro garaje debe estar capacitado para las especificaciones de loque va a alojar. Nada impide que el formulario acoja varios y diferentes tipos del mismo modo que nada impide que en el garaje pongas más de 1 estatua. en este caso cuando se abra la puerta del garaje, quizás lo que queramos es mostrar exactamente una estatua y no otra, en concreto, end dicho caso debemos proveer otra variable que especifique que estatua queremos mostrar (las demás quedarían oculatas, sin usar) En el código esto implica añadir una variable en el módulo (o en el formulario de exhibición)... public enum TiposdeDatos TIPO_PERSONAS=0 TIPO_DIRECCIONES=1 TIPO_DATOS_EMPRESA=2 end enum public Tipo as TiposDeDatos
Ahora entonces cuando vaya a leer un dato sobre personas pondré en el formulario que hace la búsqueda ' ... Persona= buscar.... Tipo= TIPO_PERSONAS call FormExibirResultados.show(1) ' ...
Y en el formulario de exhibición habrá que distinguir que 'estatua' recibimos... private sub Form_Load Me.Show select case Tipo case 0 ' personas Text1.text= persona.Nombre Text2.text= persona.Apellido1 Text3.Text= persona.apellido2 Text4.text= Persona.NumerCedula case 1 ' direcciones Text1.text= Direccion.Ciudad Text2.text= Direccion.Poblacion Text3.Text= Direccion.Calle Text4.text= Direccion.Numero case 2 '................ otros casos end select end sub
Para terminar también te indico que si tienes que hacer bastante trabajo al respecto sería mejor que crearas unas clases, con los tipos de datos y que en ella incluyeras los métodos que fueras a necesitar, incluso si los tipos de datos fueran comunes entre ellos utilizar una interfaz que implemente cada clase. En fin como puedes ver, no puedes presentarte en casa con una estatua de 7 metros. Primero tienes que tener acondicionado el garaje para acoger lo que vas a colocar allí, en el presente caso son unos textbox, y el código para operar sobre ellos... los textbox reciben la 'estatua' (tipo de datos persona) que ya sabe como encajarla en el garaje. p.d.: queda claro que si los textbox están vacíos es porque no ha encontrado nada relevante al número de cédula, queda a tu elección en dicho caso si mostrar el formulario o enviar un msgbox al caso. También podrías modificar el caption del formulario, para que indicara un texto parecido a : " Resultados para la búsqueda: 'número de cédula' (si los textos están vacíos no se encontraron coincidencias)".
179
« en: Domingo 4 de Octubre de 2009, 11:03 »
Si, así se hace. Insultar a quien no ha hecho otra cosa que darte consejos (no verás que te haya insultado en ninguna parte) y despreciar el tiempo que te he dedicado... y todo sólo porque tu orgullo es más grande que tu entusiasmo por la programación. La programación la tendrás a un nivel bajo, pero el orgullo está al tope. Una palabra que no te gusta y rebosas y eso que sólo te doy consejos y te lo pido por favor.
Sé noble e inteligente: toma la ayuda que te he ofrecido para resolver tu problema, tómate un manual y tiempo al menos para leerlo (siempre se queda algo) y no sueltes espumarajos por la boca... careces de ambas, ni nobleza ni inteligencia, pero lleno de ego ...Vaya lobo enfundado en piel de cordero...
Tú mismo...
180
« en: Sábado 3 de Octubre de 2009, 02:13 »
Filtrar una lista de modo que sólo quede una copia de cada elemento sin repetición... (este o algo parecido es la idea que debería ser el título del mensaje). Sea una lista de elementos (en el ejemplo consideramos números y más concretamente usaremos byte), dicha lista contiene una cantidad arbitraria de elementos, se desea obtener una lista que incluya única copia de cada eleemnto. Para crear elcódigo y una demostración visual, vamos a necesitar 2 listbox y 2 botones. * El lis1 contendrá un volcado con la lista de elementos original, * El lis2 tendrá un volcado cion la lista de elementos ya filtrados. * El btn1 crea una lista de elementos aleatoria (nos aseguramos que existan repetidos) y presenta la listas en lis1 * El btn2 lama a la función que filtra la lista y presenta el resultado en el lis2 Código previo: ' nótese la declaración de la matriz a nivel de formulario... Dim ElemOrig(0 To 9999) As Byte Private Sub Form_Load() ' queremos usar el generador aleatorio de números, implantamos una semilla diferente en cada uso. Randomize Timer End Sub
Código del boton 1: * El btn1 crea una lista de elementos aleatoria (nos aseguramos que existan repetidos) y presenta la listas en lis1 Private Sub Btn1_Click() Dim k As Integer Lis1.Clear ' vaciamos elcontenido actual ' un byte tiene un valor cuyo rango oscila entre 0 y 255, si proveemos más de 256 valores ' tenemos la certeza de que habrá repetidos. ' usamos 10.000 elemntos debe haber alrededor de 40 repeticiones de cada elemento For k = 0 To 9999 ElemOrig(k) = Int((255 + 1) * Rnd) Lis1.AddItem (ElemOrig(k)) Next End Sub
Debe notarse que el generador aleatorio, porporciona realmente valores pseudo-aleatorios. Código del botón 2: * El btn2 lama a la función que filtra la lista y presenta el resultado en el lis2 Private Sub Btn2_Click() Dim k As Integer, n As Integer Dim ElemFil() As Byte ' vaciamos elcontenido previo Lis2.Clear ' ejecutamos el filtrado ElemFil = FiltrarUnicos(n) ' volcamos la matriz obtenida en el listbox For k = 0 To n - 1 Lis2.AddItem (ElemFil(k)) Next Erase ElemFil ' liberamos memoria ... ' indicamos cuantos elementos únicos se ha hallado. MsgBox "se han encontrado " & n & " elementos únicos sonre los 10.000 originales." End Sub
Código de la función que realiza el filtrado. ' devuelve el número de elementos únicos Private Function FiltrarUnicos(ByRef n As Integer) As Byte() Dim k As Integer Dim ls() As Integer Dim ElemFil() As Byte ' Hacemos que la lista de devolución tenga el mismo tamaño que la original ReDim ls(0 To 9999) ReDim ElemFil(0 To 999) ' vamos recreando en una lista intermedia, la cantidad de apariciones de cada elemento For k = 0 To 9999 ls(ElemOrig(k)) = ls(ElemOrig(k)) + 1 Next ' si la lista intermedia tiene un valor mayor que 0 ese elemnto existe en la lista original, lo tomamos For k = 0 To 9999 If ls(k) > 0 Then ElemFil(n) = k n = n + 1 End If Next Erase ls ' la lista definitiva, tiene espacio no usado lo retiramos ReDim Preserve ElemFil(0 To n - 1) ' devolvemos la lista filtrada ordenada de menor a mayor. FiltrarUnicos = ElemFil Erase ElemFil End Function
Como verás sólo podrás aplicarlo a tu código si logras entenderlo y por tanto sabes como implementarlo... esto es, no te voy a dar el código exacto a tu problema, el objetivo es que aprendas para resolver por tí mismo los problemas que se presenten no resolver un problema puntual a base copiar y pegar. Esta solución tiene algunos inconvenientes que detallo. Utiliza memoria adicional por loque si una lista tiene pongamos 100 millones de elementos puede suponer un gran consumo de memoria en el equipo. Debe tenerse en cuenta... No vale para casos donde los elementos tienen valores decimales (se puede modificar para lograr que valga, pero la memoria a usar se dispara * 10 por cada decimal usado, es decir si tienes una matriz de 10.000 elementos la matriz intermedia para 4 decimales (por ejemplo un tipo de datos currency) usaría 10.000 * (10^4) elemntos es decir : 10.000.000 (10 millones). Este valor se duplica por que la matriz de salida inicalmente debe contemplar el mismo número de elemtnos que la matriz intermedia. En definitiva sólo es útil para números enteros y atendiendo al tamaño de la matriz para vigilar la memoria usada. Tiene a favor que es el más rápido. Este algoritmo es una implementación del algoritmo conocido como 'counting', que se utiliza para ordenar números (que tiene las mismas limitaciones descritas), sólo que en este caso hay una modificación dirigida a filtrar elementos repetidos.
181
« en: Sábado 3 de Octubre de 2009, 00:56 »
Tu problema es que quieres aprender a base de preguntas y ese modo de aprender puede costarte años, por qué, porque hasta que no te sueceda un problema no tendrás necesidad de preguntar, más aún si tienes un problema intentarás resolverlo por tu cuenta, si se soluciona ya no preguntarás por lo que no profundizas en la raíz de las cuestiones. Esto ocasiona un aprendizaje lleno de lagunas.
Respecto de los que estamos en la 'otra parte', nos fastidia que alguien 'basuree' el foro con preguntas que no son técnicas y cuyo respuesta es básica y se encuentra en los fundamentos sea de la programación o del lenguaje.
Entonces por favor, tómate un manual de programación y estúdialo, pero ya ni siquiera de VB si no de principios de la programación. Tienes carencias tales como que no sabes lo que es un bucle ni un condicional. El foro no está pensado para atender 'programadores de parvulario', hay que tener una base mínima, simepre se podrá responder a una cuestión tonta, pero sólo si es puntual, si se convierte en la norma será mejor emigrar a otro lugar...
Tu dices que quieres obtener 1 único valor pero que te aparecen varios, entonces porque le dices 'if g= 5 then exit do', en que quedamos quieres tomar 1 o 5 (si 'aparecen')... ???????
p.d.: Por otro lado tampoco sabes preguntar, releyendo tu pregunta me hace dudad de si lo que quieres no es un '1 sólo número', sino que '1 número sólo aparezca 1 vez'... Si no eres capaz de ordenar tus pensamientos para solicitar lo que necesitas será bastante más difícil que otros entiendan lo que tu quieres decir si tú mismo no sabes lo que quieres preguntar.
en el supuesto de que fuere este caso (tenemos que hablar con hipótesis en situaciones así), lo que tienes que hacer es primero recoger los elementos que cumplen el patrón y luego filtrarlos para que no haya repetidos. en este caso si se trataría de una cuestión técnica, pués aunque la solución es relativamente fácil, la gente no suele encontrarla. Trataré esta resolución en el siguiente mensaje...
Por favor aprende los principios básicos de programación, no puedes pretender correr sin saber primero andar.
182
« en: Sábado 3 de Octubre de 2009, 00:39 »
No hay nada malo en usar la declaración de tipos, quizás el problema sea que si la gente no está acostumbrada a usarlos no los reconozca. Más concretamente en los valores inmediatos numéricos es acertado hacerlo por que por defecto cualquier valor inmediato es de tipo long por tanto forzarlo a un single o double si el resultado va a contener ese tipode datos es acertado porque le ahorras a vb algunas comprobaciones de tipos. Por otro lado, vb utiliza como acumulador 'la variable de la izquerda' en una expresión, lo que nos indica en cierto modo que algunas declaraciones pueden ser superfluas y la expresión entonces debería diseñarse en base a velocidad, esto es, el tipo de datos integer que es el más rápido para vb, por tanto los de tipo byte promocionarlos a integer y los longs si no superan el límite de un integer también promocionarlos a integer. Si se requiere más info sobre esta cuestión, basta que avises... Tu error se da porque declaras Min como integer en el parámetro de la función pero al usarlo luego dentro de una expresión le dices que es un double, ya está declarado por tanto es una etiqueta duplicada, sólo que aquí nos indicará duplicación de etiqueta porque la declaración es explícita en tanto que utilizando el signo de tipo es implícito. si se declarara primeramente la variable con el signo implícito y luego se hiciera la declaración explícita, el error también se daría como etiqueta duplicada, como es el orden al revés se asume el error en el signo implícito, es decir en la 2ª declaración.... El error equivalente fácil de comprender serían estas combinaciones de código: ' duplicación de etiqueta: 1º 'valor' se declara explícitamente como integer luego como double dim Valor as integer dim Valor as double ' error aquí
' duplicación de etiqueta: 1º 'valor' se declara implícitamente como integer luego explícitamente como double Valor% = 20*5 dim Valor as double ' error aquí
' el signo de declaración no coincide con el tipo de la declaración: 1º 'valor' se declara explícitamente como double luego implícitamente como integer . este es tu caso dim Valor as double Valor% = 20*5 ' error aquí
' el signo de declaración no coincide con el tipo de la declaración: 1º 'valor' se declara implícitamente como double luego implícitamente como integer . Valor#= 45 Valor% = 20*5 ' error aquí
p.d.: para recrear esas ondas, no crees que sería mejor utlizar circle en vez de pset ?... Además de más rápido te permitiría más flexibilidad, con currentx y currenty desplazarías la 'onda' y con el aspecto del círculo definirías la elipse, con el inico y fin del arco definirías los grados del arco... resumiendo, podrías además, hacer sobre el bucle que la onda fuera variando a cada arco, mascorta, más larga, mas alta mas baja, no de 180º y no en línea recta... en vez de dibujar una sinusoide. Claro que depende de para qué lo quieras, pero sería mejer usar circle de todas maneras.
183
« en: Viernes 2 de Octubre de 2009, 00:44 »
Es aún más sencillo que el código que pone Jimbenit... Basta declarar la cadena con el tamaño deseado así queda fija: Dim Cadena As String * 80ejemplo con los 3 casos que se pueden dar... Private Sub Command1_Click() Dim Cadena As String * 80 Dim subC As String Dim k as byte ' caso 1 se asigna una cadena con menos caracteres, el resto queda relleno de espacios. Cadena = "Hola_" Cadena = Replace(Cadena, " ", "4") 'remplazamos los espacios libres por 4 para que se vea la longitud total MsgBox Len(Cadena) & " " & Cadena 'caso 2 cadena vacía: ahora está 'vacía' con 80 caracteres de espacio. Cadena = "" MsgBox Len(Cadena) & " " & Cadena ' caso 3 cadena más larga que el tamaño asignado a la cadena: se trunca al tamaño despreciando los caracteres de la derecha que exceden For k = 1 To 100 ' 100 * 5= caracteres subC = subC & "Hola_" Next Cadena = subC MsgBox Len(subC) & " " & subC & vbcrlf & vbcrlf & Len(Cadena) & " " & Cadena End Sub
184
« en: Sábado 26 de Septiembre de 2009, 05:18 »
vacio &= aux & ";" 'La idea es que aqui se concatenen los datos de aux----------->> un equivalante en php es .= para la concatenacion En vb6 tenemos Join, pero sólo se aplica a matrices de cadenas, por lo que para el resto de casos tienes que utilizar el método que indica F_Tanori Public Function EnvioDatosPruebas() Dim vacio As String Dim lista(0 To 9) As String, k As Byte ' recreamos datos para la matriz For k = 0 To 9 lista(k) = CStr(k) Next ' los unimos en una sola cadena con el separador especificado... vacio = Join(lista, ";") MsgBox vacio end sub
185
« en: Jueves 24 de Septiembre de 2009, 19:50 »
La calculadora (de visor) multilínea es más fácil de hacer y de entender... Como te comentaba en el apartadoanterior (me cito) es más útil un visor multilínea, es más útil porque no te obliga como usuario a memorizar el valor que acabas de introducir, puedes verificar que no te equivocaste al pulsarlo, y además al término de la operación puede servirte para una nueva operación sin tener que volver a introducir de nuevo los 2 operandos y la operación (si estos no cambian) . Por tanto el diseño en cuanto a botones de operaciones y a botones de cifras es igual pero para el visor disponemos 4 textbox, verticalmente, distribuídos así 1, TxtVisor(0) es el visor del operando1 2, txtVisor(1) es el visor de la operación 3, txtVisor(2) es el operando2 0, line1 es una línea de separación 4, txtVisor(3) es el visor del resultado 5 a la izquierda hay un control option sobre el visor de ambos operandos, sus indices son los mismos que el txtVisor correspondiente... Estos controles sirven para indicar a cual de ellos van las teclas decifras pulsadas... Cada visor tiene 2 botones adicionales a su derecha que pasamos a describir Sobre el visor del operando 0, el botón 1 pone a 0 el valor, el 2º boton pasa el valor del operando 1 al 2 Sobre el visor del operando 1 el boton 1º pone a 0 el operando 2, el 2º botón pasa el valor del operando 2 al 1 sobre el visor de operación, el botón 1 borra ambos operandos, el botón 2º intercambia los operandos el 1 por el 2 y el 2 por el 1 sobre el visor de resultado, el botón 1 transfiere el resultado al operando 1 el 2º botón transfiere el resultado al botón 2 Cuando se pulsa cualquiera de estos botones,(de cifras y de operaciones) el color del texto del visor del resultado se pone en naranja, queriendo indicar con ello que el resultado que aparece (si lo hay) no es el resultado de los datos actuales reflejados en el visor.... este vuelve a cambiar a verde cuando se pulsa la operación '=' , sólo cuando se pulsa ese botón se pone en verde. el código es más fácilmente deducible.... el código cuando se pulsan las cifras sería éste.. private const Naranja= rgb(255,128,64) ' naranja Private Sub ComCifras_Click(Index As Integer) ' está introduciendo valores numéricos , se añaden por la derecha como se escriben los números... TxtVisor(OperandoActivo).Text = TxtVisor(OperandoActivo).Text & CStr(Index) txtVisor(3).forecolor= Naranja End Sub
El código cuando se pulsa las teclas de operación, también es simple para las operaciones actuales que requieren 2 operandos... Private Sub ComOperacion_Click(Index As Integer) if index > 1 then TxtVisor(1).Text = ComOperacion(Index).Caption txtVisor(3).forecolor= Naranja Operacion=index else ' boton reset o boton de calcular ('=') If index=0 then txtVisor(3).Text = Calcular txtVisor(3).forecolor= vbgreen ' sólo en este caso y en este momento, el visor de resultado es verde else ' aquí hacemos como en el apartado anterior todo a 0 txtVisor(0).Text = "0" txtVisor(1).Text = "" txtVisor(2).Text = "0" txtVisor(3).Text = "" End If end if end sub
El código de calcular es casi idéntico, al del apartado anterior, de hecho es el mismo sólo que antecemos 2 líneas para tomar los operandos: Private Function Calcular() As String Dim resul As Double, k as byte operando1 = Val(txtVisor(0).Text ) operando2 = Val(txtVisor(2).Text ) Select Case Operacion Case 2 ' sumar Calcular = CStr(operando1 + operando2) Case 3 ' Restar Calcular = CStr(operando1 - operando2) Case 4 ' Multiplica Calcular = CStr(operando1 * operando2) Case 5 ' Dividir If operando2 <> 0 Then Calcular = CStr(operando1 / operando2) Else Calcular = "División por 0 - Error" End If Case 6 ' potencias resul = operando1 For k = 1 To operando2 resul = resul * operando1 Next Calcular = CStr(resul) ' ' si no quieres calcular con el bucle ' Calcular = CStr(operando1 ^ operando2) Case 7 ' raíces Calcular = CStr(operando1 ^ (1 / operando2)) Case 8 ''''' ' otros cálculos..... End Select End Function
Nos falta sólo el código de los botones a la derecha de cada visor, primero crearemos los controles y como tienen la misma funcionalidad nos será más cómodo tratarlos juntos como una matriz. Por tanto añadimos unnuevo botón le cambiamos el nombre a comVisor y le establecemos la propiedad index a 0 , copiamos pegamos 7 copias, los colocamos a la derecha de cada visor ordenadamente ante el visor 0 el boton comVisor(0) y ComVisor(1)...etc Private Sub ComVisor_Click(Index As Integer) dim s as string Select case Index Case 0 ' poner operando1 a 0 txtVisor(0).Text ="0" Case 1 ' poner operando1 en operando2 txtVisor(2).Text =txtVisor(0).Text Case 2 ' poner ambos operandos a 0 txtVisor(0).Text ="0" txtVisor(2).Text ="0" Case 3 ' intercambiar operandos s =txtVisor(0).Text txtVisor(0).Text =txtVisor(2).Text txtVisor(2).Text = s Case 4 ' poner operando2 a 0 txtVisor(2).Text ="0" Case 5 ' poner operando2 en operando1 txtVisor(0).Text =txtVisor(2).Text Case 6 ' poner resultado en operando1 txtVisor(0).Text = txtVisor(3).Text Case 7 ' poner resultado en operando2 txtVisor(2).Text = txtVisor(3).Text end Select txtVisor(3).forecolor= Naranja end sub
El código de los controles option es también muy sencillo (recuerda que estos 2 controles tienen el mismo índice que el visor al que representan, es decir 0 y 2 y están a su izquierda o donde tu prefieras encajarlos pero que se intuya: Private Sub OptOperando_Click(Index As Integer) OperandoActivo = Index ' declara la variable operandoActivo a nivel del formulario como byte ' cuando se carga el formulario el valor es 0, no está de más en el load añadir esta línea: ' en el load: call OptOperando_Click(0) End Sub
Sólo queda darle vistosidad a los visores, tal como se hizo en el apartado anterior con el visor, se deben preparar ahora los 4 visores, poner el papel (fondo) en negro, y la tinta ( el texto ) en verde... controlar los tamaños que han de tener, etc... Por último tenemos que bloquear los controles de visor, para que el usuario sólo pueda introducir valores a través de los botones... si se quiere manejar las teclas deberás manejar el contenido de cada visor en el evento validate de cada uno de ellos, para ti que eres principante quizás sea mejor resolverlo sin preocuparte del manejo del teclado, cuando adquieras más conocimientos ya modificarás el proyecto para añadir esa funcionalidad.... por tanto por tanto en el load añade este bucle dim k as byte For k= 0 to 3 txtVisor(k).locked=true next
Saludos....
186
« en: Jueves 24 de Septiembre de 2009, 06:50 »
No te he entendido apenas nada de lo que quieres... porque te explicas muy mal. El código es muy pobre pero como dices que eres principiante, se entiende... Veamos, la raíz es a la potencia lo que la división a la multiplicación y lo que la suma a la resta. Por lo tanto debería serte fácil encontrar una solución óptima con sólo una línea para calcularlo... Seguimos, el seno, coseno, tangente y demás funciones trigonómetricas, incluídas las derivadas (de estas) las tiene VB como funciones... ve a la ayuda de VB y escribe para buscar 'Funciones matemáticas' y 'Funciones matemáticas derivadas'. El visor no tiene porque ser de una sóla línea, de hecho es más útil un visor multilínea, es más útil porque no te obliga como usuario a memorizar el valor que acabas de introducir, puedes verificar que no te equivocaste al pulsarlo, y además al término de la operación puede servirte para una nueva operación sin tener que volver a introducir de nuevolos 2 operandos y la operación : Linea 1 operando 1 linea 2 operación que se quiere aplicar linea 3 operando 2 linea 4 resultado, entre la línea 3 y la 4 pones una rayita (control line, por ejemplo)
... y finalmente, para que todo lo hagas usando un único textbox, tienes que guardar el dato previo... repásate este hilo: http://foros.solocodigo.com/viewtopic.php?f=143&t=40556 pués lo que en él explico, se aplica a tu problema... puedes usar propiedades para retener los datos y verificar el valor introducido, no obstante como eres principante, seguiremos un método que no recurre a propiedades y es más directo y asequible para tí. Te lo resumo... de entrada tendremos varios botones de operaciones por ejemplo suma, resta, div.., mult... etc... y uno para borrar (suspender la operación en curso). Igualmente tenemos, otros botones que representan las cifras (no queremos que introduzcan k, si no números y mejor si les proveemos los números como botones...) La operativa es la siguiente: 1: introducir 1 número (operando1) 2: elegir una operación, sólo cuando el usuario elija una operación guardamos la cifra actual que hay en el visor (nuestro textbox) operando1= val(text1.text) y borramos el visor... o mejor desactivamos este visor y activamos uno justo debajo. 3: introducir otra cifra, sólo si el usuario elige una cifra en este paso, guardamos la operación que eligió el usuario, en el paso 2 (se hace ahora, porque el usuario puede pulsar varias teclas de operaciones, dejaremos como activa la última pulsada). 4: elegir otra operación..., sólo si es una operación guardamos la cifra actual del visor operando2=val( text1.text), la operación esperada es '=', pero no importa, si es un botón de operación, hacemos una llamada a calcular, acto seguido si la operación no era igual ('='), está se guarda como operación y la cifra calculada como operando1, por lo que estaríamos de nuevo a la espera del paso 3... ----------------- Para hacer la calculadora, cómodamente añades un botón le llamas comCifra en el caption pones el texto '0', y en la propiedad index le pones 0, copia ese botón y pégalo 9 veces, ya tienes una matriz de 10 botones llamados cifras, cuyo index coincide con el valor, cambia el caption de los nuevos botones al valor que indica la propiedad index... distribúyelos ordenadamente en el formulario... El código (del visor de 1 línea) sería, más o menos: Private Sub ComCifras_Click(Index As Integer) If paso Mod 2 = 0 Then ' si el paso es par (osea 0,2,4) If paso = 2 Then paso = 3 Else paso = 1 End If Text1.Text = "" ' se van a escribir cifras, borramos lo que halla End If ' está introduciendo valores numéricos Text1.Text = Text1.Text & CStr(Index) End Sub
Ahora coloca otro botón en el formulario llámalo ComOperacion, en el caption pon '=', y en la propiedad index el valor 0, copia este control y pégalo varias veces (1 por cada operación que quieras hacer)... tendrás una matriz de botones de operacion, cambia los caption para que reflejen lo que hacen '+' , '-', ... no importa la distribución ni el orden, pero ponlos que queden bien presentados en el formulario...(yo he reservado 'en este ejemplo' el 0 para '=' el 1 para borrar operaciones 2, para sumar, 3 para '-', 4 para '*', 5 para '/' 6 para potencioas y 7 para raíces, si los cambias deberás adecuar en el código dichos cambios) el código es más o menos éste: Private Sub ComOperacion_Click(Index As Integer) Dim t1 As Single ' para hacer una breve pausa si procede If Index = 1 Then ' operación de resetear la operación en curso (típicamente llamada Clear) paso = 1 operando1 = 0 operando2 = 0 Operacion = 0 Text1.Text = "" Else If paso = 1 Or paso = 3 Then If paso = 1 Then operando1 = Val(Text1.Text) Text1.Text = ComOperacion(Index).Caption paso = 2 Operacion = Index Else ' recogemos el operando 2 y calculamos Operando2 = Val(Text1.Text) Text1.Text = Calcular ' el resultado se toma como operand1, salvo que se borre. Operando1 = Val(Text1.Text) Paso = 2 Me.Enabled = False Text1.ForeColor = vbRed ' proporcionamos una espera de 3 segundos para ver el resultado t1 = Timer Do DoEvents Loop While (Timer - t1) < 2 If Index <> 0 Then ' miramos que operación se ha elegido Text1.Text = ComOperacion(Index).Caption ' después de la pausa colocamos el nuevo operador en el visor Operacion = Index End If Text1.ForeColor = vbGreen Me.Enabled = True End If Else Operacion = Index Text1.Text = ComOperacion(Index).Caption End If End If End Sub
Ya sólo falta el código de la función de cálculo, sólo te pongo las 6 operaciones más comunes el resto va de tu cuenta... (si, también te pongo las raíces) nota que los códigoos de operación 0 y 1 no son operaciones de cálculo si no de control... 0 solicita calcular el resultado y 1 limpia el visor y establece todos los valores a valores por defecto. Private Function Calcular() As String Dim resul As Double, k as byte Select Case Operacion Case 2 ' sumar Calcular = CStr(operando1 + operando2) Case 3 ' Restar Calcular = CStr(operando1 - operando2) Case 4 ' Multiplica Calcular = CStr(operando1 * operando2) Case 5 ' Dividir If operando2 <> 0 Then Calcular = CStr(operando1 / operando2) Else Calcular = "División por 0 - Error" End If Case 6 ' potencias resul = operando1 For k = 1 To operando2 resul = resul * operando1 Next Calcular = CStr(resul) ' ' si no quieres calcular con el bucle ' Calcular = CStr(operando1 ^ operando2) Case 7 ' raíces Calcular = CStr(operando1 ^ (1 / operando2)) Case 8 ''''' ' otros cálculos..... End Select End Function
Finalmente para que quede todo bien le damos vistosidad al visor.... No se si tendrás la fuente llamada LCDD Private Sub Form_Load() Me.ScaleMode = vbPixels Me.FontName = "LcdD" ' fuente que elegimos, Me.FontSize = 16 Me.FontBold = True ' negrita With Text1 .BackColor = vbBlack ' fondo del visor en negro, por supuesto .ForeColor = vbGreen ' texto en verde, por supuesto .FontBold = True .FontName = "LcdD" .FontSize = 16 .MaxLength = 10 ' sólo admite 10 cifras... .Alignment = vbRightJustify ' texto alineado a la derecha, por supuesto .Height = Me.TextHeight("9") + 4 ' damos tamaño al textbox, para que no quede desproporcionado con la feunte elegida. .Width = Me.TextWidth("División por cero - ERROR ") ' sólo admitimos 10 cifras, pero este texto es lo más largo que saldrá... End With End Sub
Nota que no he declarado las variables que usamos, pero conviene que las declares (paso, operando1, operando2 y operacion) El código no está probado por lo que no se descarta que haya algún error o no funcione del todo como se espera, ya es cosa tuya corregir y afinar detalles. También queda a tu esfuerzo añadir otras operaciones más complejas... añadir funcionalidad de decimales , errores de desbordamiento, etc... pero ya tienes un esqueleto sobre el que trabajar.... Saludos...
187
« en: Lunes 21 de Septiembre de 2009, 20:04 »
No puedes llamar algoritmo a todo lo que té la gana. 'For' es un bloque estructural de programación, sólo podrás llamarlo algoritmo a la implementación del mismo (por ejemplo cuando estés creando un lenguaje) pero nunca al hecho de usarlo desde el lenguaje. todavía al final te pondré códigos que al menos si pueden llamarse algoritmo, por implmentar el bucle for. Tu has hecho una cosa rara, el código es de visual basic, pero al momento me pareció que querías hacer un bucle de 'C' y al final ni una cosa ni la otra. la estructura del bucle for tiene este patrón: For W= X to Y step Z ' código next
donde x,y,z son variables, valores, ó expresiones (incluído el resultado de una llamada a una función) y W es el nombre de una variable a la que si es necesario puede insitu indicarse el tipo de variable que es, por ejemplo: x= 110 for W as byte = x to -102 step -(x/10) ' código next
En este pedazo de código W inicialmente valdrá 110, los incrementos será de -11 y al final el código del bucle se ejecuta por última vez cuando W llegará a alcanzar el valor -99 (-102 nunca se llega a alcanzar). W automáticamente adquiere el valor de X cuando se inicia el bucle, por tanto es x quien previamente puedes ponerle el valor deseado, nunca a w... Si lo entiendes mejor de esta manera, en pseudocódigo, ahí queda: Desde x ' w=x Repetir hasta Y ' w <= y ' codigo en pasos de z ' w= w + z fin repetir
Aquí recreamos un bucle (esto si es un algoritmo) sin usar la estructura for, fíjate que es una implementación del pseudocódigo anterior w=x Repetir: if w <= y then ' codigo w=w + z goto Repetir ' salto incondicional a la etiqueta Repetir end if
Para reproducir fielmente los mismos valores que devuelve VB al final de un bucle, sea cual sea el caso, debería modificarse el código así: w=x if w <= y then Repetir: ' codigo w=w + z if w <= y then goto Repetir ' vb siempre realiza una suma posterior a la última válida, siempre que al menos se ejecutara una vez el código. end if
Para terminar, indicarte que el mejor sitio para tu pregunta-duda no es este subforo si no el de 'vb .net', pero bueno, para hacerlo válido aquí es que al final te he puesto estos 2 algoritmos y el pseudocódigo, con la excusa del bucle for... Fíjate que usar un bucle for resulta más claro y limpio que el código del algoritmo (de cara al programador), y también correrá más rápido porque está diseñado a bajo nivel aunque estructuralmente haga exactamente lo mismo. No obstante el algoritmo tesirve cuando debas alterar ligeramente el comportamiento del bucle, por ejemplo la 1ª implementación nunca devolverá un valor de w mayor que y, VB siempre al final del bucle si se entró en él w valdrá el siguiente valor que habría de tomar el bucle. También te aclaro que las implementaciones están 'resumidas', la idea era que lo comprendas y para eso lo mejor es que quede sencillo, en realidad las implmentaciones sólo funcionarán si 'X' es menor que 'Y'... el código para todos los casos sería... ' if z= 0 then bucleInfinito... salvo que se use exit for (exit sub, etc..) dentro del código, el programador es responsable de que en alguna ocasión se ejecute dicha salida... w = x If x < y Then ' controla si inicio es mayor que final If (w + z) => x Then ' controla el paso If w <= y Then ' controla si puede ejecutarse el bucle RepetirA: ' codigo w = w + z If (w <= y) Then GoTo RepetirA ' controla si puede seguirse repitiendo el bucle End If End If Else If (w + z) <= x Then ' controla el paso If w >= y Then RepetirB: ' codigo w = w + z If (w >= y) Then GoTo RepetirB End If End If End If
Aunque el código ahora queda mayor, en realidad VB cuando compila pone sólo el caso que se aplica (el condicional) e ignora el resto, por tanto la aparente repetición de código nunca se produce. Esto es pone uno de los códigos que indico a continuación: ' al compilar se pone este código o el siguiente w = x If x < y Then If (w + z) > x Then If w <= y Then Repetir: ' codigo w = w + z If w <= y Then GoTo Repetir end if End If End if ' al compilar se pone este código o el anterior w = x If x > y Then If (w + z) < x Then If w >= y Then Repetir: ' codigo w = w + z If w >= y Then GoTo Repetir End If End If End If
Ojo la implementación de Vb no controla si el paso es 0 que puede producir un bucle infinito, porque se supone que el programador con ello quiere crear un bucle incondicional y dentro del mismo usará cuando proceda un exit for (exit sub, etc..) dentro del código, el programador es responsable de que en alguna ocasión se ejecute dicha salida... Un ejemplo de bucle incondicional con salida programada For w = 1 To 2 Step 0 y = y + 1 If y > 2000000 Then w = 120 ' cuando se cumpla esta condición se sale, el bucle en ningún momento a ejecutado sólo 2 ciclos.... sino 20 millones Next
Pese a la fama que se da a do..loop en detrimento del bucle for...next, estas particularidades hacen que un bucle for se comporte exactamente igual que un bucle do....loop, por lo que al menos a mi respecta un bucle for es el rey de los bucles, otra cosa son las limitaciones de los programadores que no exploran las capacidades del lenguaje.
188
« en: Sábado 19 de Septiembre de 2009, 17:57 »
Qué son para ti las cordenadas ????... Defines una escala en milímetros y luego le dices diviidimos el ancho del área en 30 partes y el alto en 49... imprimes un texto, lógicamente en 0,0 vuelves a decirle que repartes el área, ahora el ancho en 160 partes y el alto en 49... las cordenadas son las que quedaron tras imprimir lo anterior, no ha habido modificación. Esto se llama escala, como su nombre indica printer.ScaleX, printer.ScaleY, no cordenadas... Si el modo de escala lo usas en milimetro (6), entonces a lo sumo deberías definir las medidas del papel, por ejemplo si utilizas un 'DIN A4' with vb.printer .scalemode=6 call .scaleX( 210) call .scaleY( 297) end with
Las cordenadas del cursor se definen con: with vb.printer .scalemode=6 call .scaleX( 210) call .scaleY( 297) ' igual que un formulario, picture,etc... no has pintado nunca líneas sueltas o círculos...??? .CurrentX= 30: CurrentY= 49 : .Print Ucase(Txtemopresa.Text) .CurrentX= 160: CurrentY= 49 : .Print txtRuc.Text '. '. '. '. etc ... end with
No olvides que la impresora avanza hacia abajo, por lo que debes siempre imprimir en orden vertical de líneas (aunque no sea el 'orden lógico', del documento). Lo mismo sucede al avance horizontal.... De hecho en printer enddoc, lo que hace es readaptar las instrucciones para cumplir estos requisitos ( ordenando, subdividiendo una instrucción en varias, etc...)... Sin embargo le ahorras tiempo a esta fase cuanto mejor ordenado lo quedes tú, eso si, hay partes que entre un uso y otros puede ocupar una línea o varias (tiene un tamaño variable, no cuantificable a priori) en ese caso no le des más vueltas, que enddoc se las apañe... Otra cosa para evitar saltos raros entre distancias de líneas (sobre todo si son contínuas), dado un tipo de letra deberías preguntarle el alto del tipo y los CurrentY hacerlos múltiplos de este valor... por ejemplo: Dim Ac As single ' Alto de un Caracter con el tipo de letra actual Ac = VB.Printer.TextHeight("Wy") ' . ' . ' . división entera vb.printer.CurrentY= Cint((133Ac)*Ac) ' + Inicial si la 1ª línea no es múltiplo de Ac, en este caso: 'inicial = posY mod Ac'
Si es un documento 'complejo', con imágenes, cuadrículas, cambios de tipos de letra, etc... entonces esto debe aplicarse por bloques de texto contínuo, siendo para cada bloque un 'Ac' distinto y un 'inicial' distinto, según sea el caso... si un mismo párrafo utiliza diferentes fuentes, Toma el valor de 'Ac' con la fuente que dé mayor tamaño de alto de su texto. Esto lo puedes hacer haciendo un bucle y comprobar cual tiene el alto mayor, recuerda que el alto no sólo depende de 'fontsize' también del tipo de letra.... Un ejemplo de cálculo, imagina que las fuentes las tomamos de objetos del formulario, donde cada objeto es representativo de la fuente que el cliente quiere usar para imprimir (esto le permite definiri mejor el resultado final). dim fuentes() as Ifontdisp, k as byte , Actual as single redim fuentes(0 to 4) ' vamos a usar 5 fuentes diferentes... fuentes(0)= LabelCabecera.font ' texto y fuente que se utiliza para el título y/o cabecera del documento fuentes(1)= TxtParrafos.Font ' texto y fuente 'base' del documento. fuentes(2)= LabelResalte.Font ' texto y fuente que se resalta dentro de los párrafos, típicamente será negrita, pero puede que también cambie el tamaño de la fuente y el tipo fuente(3)= ........ etc.. '. '. vb.printer.font=Fuentes(1) ' fuentes(0) no se mezcla con los párrafos, por tanto se deja aparte Ac = VB.Printer.TextHeight("Wy") for k= 2 to ubound(fuentes) ' la fuente 1 se realiza fuera del bucle, para disponer del valor inicial de comparación, en el siguiente bloque, se muestra el caso dentro del bucle. vb.printer.font=Fuentes(k) Actual= VB.Printer.TextHeight("Wy") if Actual > Ac then Ac= Actual next ' fuentes(0) no se mezcla con los párrafos, por tanto se deja aparte ' caso donde queramos, meter el 1 dentro del bucle, conviene poner Ac a 0 por si tuviera un valor distinto de trabajo previo Ac=0 for k= 1 to ubound(fuentes) vb.printer.font=Fuentes(k) Actual= VB.Printer.TextHeight("Wy") if Actual > Ac then Ac= Actual next
Como es obvio, esto sólo se aplicará en auqellas líneas donde semezclan diferentes fuentes, o tamaños... ya que si un párrafo tiene pongamos 40 líneas y en la línea 33 hay un realte de letra de mayor tamaño, no vamos a guardar esa altura para todas las líneas... sólo para esa línea 33 y en aquellas en las que aparezca. Desde luego lo mejor es, en lo posible no mezclar fuentes que produzcan altos de línea diferente, esto te simplifica mucho trabajo, y en general endproc, resuelve muchos casos, por lo que sólo tendrás que ceñirte a casos concretos... con las explicaciones que he dado deberían servirte para resolver esos pequeños fallos que uno nunca sabe como resolver.... Finalmente te recuerdo que para pulir pequeños detalles, es muy acertado antes de imprimir hacer una vista previa para asegurarte que queda realmente como lo tienes pensado... Saludos...
189
« en: Viernes 18 de Septiembre de 2009, 04:01 »
Te aclaro.... Cuando declaras la variable Varo a nivel de formulario, de entrada tiene el valor 0, hasta que le asignes un valor distinto. Varo está disponible sólo para el formulario, es decir si ese procedimiento está en un módulo o en otro formulario 'aquí' (en el formulario que lo utilizas) no se vé, nadie lo conoce... Esto significa que puedes tener diferentes variables llamadas varo, una en un formulario, otra en un módulo e incluso una dentro de un procedimiento dentro del mismo formulario... Puedes preguntarte, cómo especifico la que quiero usar, y cómo sabe VB6 cuál uso...?, te lo explico con ejemplos comentados in situ... ' en un módulo (.bas) public Varo as integer ' Ahora en el formulario ' Ejemplo 1: una var aquí tiene el mismo nombre que en un módulo... Private Varo As Integer Private Sub Form_Load() Varo = 25 ' siempre se reconoce al de ámbito más reducido (como en la familia, juanito es el nuestro, otros juanitos serán... juanito el vecino, juanito el de China, juanito el loco... es decir se le califica para reconocerle, sino entendemos que es el más cercano, el más familiar Module1.Varo = 20 ' este varo debe calificarse, con module1, para que el entorno sepa que es ése, si se ignora creerá que es el de aquí, del formulario... Varo = 25 MsgBox Module1.Varo ' vemos cómo el valor no ha cambiado de 20 End Sub 'ejemplo 2: hay un varo (público) en un módulo , un varo en este formulario también público y un varo más dentro del procedimiento.... la regla es la misma, el más 'familiar' es el reconocido, los identificadores se lovcalizan en la tabla de 'dentro hacia afuera' si el procedimiento no tuviera una variable varo, buscaría en la tabla del formulario si no lo encontrara, lo buscaría en la tabla global, si tampoco lo localiza interpretará que es una variable nueva, no declarada (por loque al no saber su tipo lo interpreta como variant, pero además al tener que localizarlo en varias tablas para ver que no se encuentra declarado es más lento, por estas 2 razones siempre deben declararse todas las variables... Public Varo As Integer Private Sub Form_Load() Dim Varo As Integer ' cómo está en un procedimiento, varo sin calificadores dentro del procedimiento se refiere a esta variable, si existen otras del mismonombre Me.Varo = 25 ' para referirme al varo del formulario tengo que usar el calificador ME (sólo si es pública, si es privada, no se verá, se dice que la variable del procedimiento enmascara (ensombrece) a la del formulario Module1.Varo = 20 ' finalmente debo hacer lo mismo con la variable del módulo especificar el calificador... Varo = 12 ' soy varo, el de la familia, el que nace y muere dentro del procedmiento load MsgBox Module1.Varo End Sub private Function Añadir(byval a as integer) as long Añadir = varo + a ' aquí varo se refiere al varo del formulario, no es obligatorio el calificador Me, porque no hay otro varo, más familiar en este alcance... End Function
Igualmente, si tuviera un 2º formulario, con el código del ejemplo 1º... ' formulario 2 private sub Form_Load Varo=Varo + 5 ' se refiere alvaro del módulo, porque el formulario 2 no ve el varo del formulario 1 form1.Varo= Form1.varo +7 ' Esto dará error, como varo en elformulario 1 es privado (en el ejemplo 1), no puede 'verse' desde fuera del formulario, indicará error private sub Añadir(Byval a as integer) as long Static Varo as integer ' este varo es el familiar, el reconocido Añadir= Varo + a ' sumo elvalor recibido al que ya tenía Varo=a ' guardo el nuevo valor para sumarlo con el próximo que reciba msgbox module1.Varo ' el varo del módulo, debe calificarse, porque el de aquí tiene precedencia... end Sub
Sobre los manuales que me preguntaste el otro día... supongo que tienes instalado además el MSDN de VB6, bién auqnue la gente, no lo suele utilizar (desconozco por qué), lo cierto es que trae algo más de 200 libros para manejar en formato CHM, los hay específicos sobre temas muy concretos por ejemplo hay uno específico sobre el control Winsock.... los nombres no son muy aparentes ya que están abreviados, te indico los que interesan para empezar.... Se encuentran en la ruta equivalente en tu ordenador a: C:Archivos de programaMicrosoft Visual StudioMSDN9898VS3082 - VB98.CHM : Referencia de controles estándar de VB, los controles que por defecto te aparecen en la barra de herramientas.... - VBENLR98.CHM : Referencia del lenguaje de Visual Basic , contantes, tipos de datos, funciones, instrucciones, objetos... del lenguaje - VBREF98.CHM : Referencia de los controles de Visual Basic, éste es referencia del lenguaje por orden alfabético, de controles, de asistentes, errores y proyectos de ejemplo completos... éste manual invoca a los anteriores y a otros más, por ello es más completo, pero en los otros aparecen detalles no reflejados en éste... por esta razón de referencias cruzadas entre archivos no conviene cambiarles el nombre. - VBDEF98.CHM : Definiciones del lenguaje, por ejemplo que es un 'argumento con nombre', un 'procedimiento de evento', la 'negociación de interfaz de usuario', etc.... definiciones que podrán aparecer en cualquier manual y no sabes exactamente a que se refiere... para novatos es bueno tener esto al alcance. y el más importante para ti de momento es éste: - VBCON98.CHM : Manual del Programador... en los libros que trae, el último pone 'Usar Visual Basic', despliega ese y el primero es 'Manual del Programador'.... Importante: Si copias alguno de dichos manuales y lo pegas a otra carpeta, tienes también que copiar el archivo del mismo nombre que tiene por extensión CHI, lo mismo sucede si le cambias el nombre cuando lo pegues en otra carpeta, ambos deben tener el mismo nombre. Ojo: no cambies los nombres ni muevas de sitio los manuales de VB, sino al invocar la ayuda (pulsando F1 ) no lo encontraría (una base de datos mantiene las referencias por los nombres asociados a los temas, etc... El archivo Chi, contiene los temas y el índice de donde localizar los temas en el archivo CHM, que el visor de CHM utiliza (en la TOC, Tabla de Contenidos). Todavía: los mnauales que copies aparte si les cambias el nombre, al tener referencias cruzadas entre archivos puede no encontrar el tema solicitado, lo mismo sucederá si el archivo al que se llama no se localiza en la misma carpeta. A pesar de ello siempre es buena idea tener algunos donde uno pueda consultarlos en cualquier parte en cualquier momento... todos los libros de ayuda son unos 600 MB. por lo que hoy día tener una copia en una memoria USB, no supone el mismo problema que hace 10 años...
190
« en: Martes 15 de Septiembre de 2009, 21:12 »
Vamos por partes... - CreateObject y new son equivalentes... se utilizan para crear objetos activex (variables de tipo objeto). - Entiendes los de seccionar en procedimientos, luego no daremos más vueltas a ello salvo que se te quede alguna duda en el tintero. - Como dices que tomaste el ejemplo de una macro, evidentemente lo has copiado tal cual lo sacaste (lo entiendo así), como es tan largo que te da problemas, te ves obligado a repartirlo. Si lo haces sin más, sucedería que los valores se las variables se perderían, como dices que eres novato en VB6 (e intuyo que en programación), conviene que entiendas la 'vida de las variables', sobre ello hablamos un poco ahora... Una variable cobra vida normalmente cuando se declara, cobrar vida significa que se añade a una 'tabla' el nombre de la misma, el tipo y la dirección de memoria donde se alojará, deesto se encarga el entorno de programación... sin embargo esta tabla es un poco más compleja, un modo de definirla sería imaginarse varias tablas, (o sólo 1 con con el campo alcance) con las indicaciones siguientes... Cuando necesitemos que una variable esté disponible para todo el proyecto y durante toda la vida del mismo, la declaramos como global (o public) en un módulo (archivo .bas). Si en cambio queremos que una variable sólo esté disponible en un formulario y no en todo el proyecto la declaramos dentro del formulario (archivo .frm) si la declaramos public, implica que desde otro lugar que pueda acceder al formulario podrá accederse a ella, por ejmplo, tengo un formulario llamado FormExcel y tengo otro formulario llamado FormEjecuta dentro del primero tengo una variable declarada así: ' public Valor as long' entonces implica que desde formEjecuta podrá acceder a esa variable así dim v as long v= formExcel.Valor
Si en cambio la variable valor la hubiera declarado así: ' Private Valor as long (private y dim es lo mismo aplicado a las variables), entonces en este caso no puedo acceder a la variable valor desde fuera del formulario de forma directa... Los procedimientos es como si fueran 'mini módulos', todas las variables declaradas dentro de un procedimiento sólo son accesibles desde dentro de ese procedimiento, por tanto obligadamente son privadas pero puesto que no admiten ser públicas, sólo pueden declararse con dim no con private. El acceso de una variable se suele llamar 'alcance' y está directamente relacionado con la vida de una variable... me extiendo. Cuando se arranca un proyecto lo primero que se hace (de forma transparente al programador) es mirar si hay módulos y toma todas las variables (globales y públicas) y procedimientos públicos y los añade a una tabla, todas estas variables se crean las primeras y duran todo lo que dura el proyecto, es decir ocupan memoria todo el tiempo que el proyecto. Luego mira si hay formularios, todas las variables y procedmientos públicos 'nacen' cuando se crea el formulario y su vida se agota cuando se cierra el formulario 'call Unload( me)' ojo, no si se oculta 'Me.Hide' . Luego están las variables declaradas dentro de los procedimientos, realmente también están en una tabla, pero sólo se les asigna memoria cuando se accede al procedimiento y cuando salen del procedimiento se 'tiran', se libera la memoria que ocupan (hay una tabla que diríamos master que contiene un identificador por cada formulario, módulo, clase, diseñador, usercontrol, etc... y esa misma tabla tiene todos los identificadores de acceso global en el proyecto, luego si cargamos un formulario se añaden a esa tabla o a una específica (esos son detalles del compilador de vb6, que no se especifican en ninguna parte) todos los identificadores del formulario, pero sólo se asigna memoria a aquellos que son accesibles desde todo el formulario, luego si se entra en un prooocedimiento automáticamente a todos sus identiificadores se les asigna memoria. Cuando se sale de un procedmiento, toda la memoria donde está contenida el dato, de cada identificador declarado dentro del procedimiento se libera, cuando se descarga el formulario 'unload me', se libera toda la memoria donde está contenido el dato de cada identificador publico y privado (variables métodos y funciones) , pero invocar cualquiera de sus identificadores públicos carga el formulario nuevamente, es decir siguen constando en una tabla pero no tiene memoria asignada al dato que representa, y como sucede con el procedimiento al cargarse (después de descargarse), todos sus identificadores tienes valores por defecto (ya sabes, un long vale 0, un byte vale 0 un string vale "" y una constante el valor consignado, cuando se cierra el proyecto se descarga todo... Ahora vamos a hablar someramente de las 'cosas raras'... de entrada mencionamos las variables declaradas Static. En un procedimiento a veces puedes querer que una variable conseve su valor y al mismo tiempo que sea inaccesible desde fuera, es decir que sólo pueda modificarse desde el procedimiento pero que su valor no se pierda al salir del mismo... ese es el caso de las variables static, una variable static de un procedimiento tiene el alcance del procedimiento pero la vida de su contenedor (si es un formulario, la de éste, si es un módulo hasta que se cierre el proyecto, si es una clase mientras esta exista, etc... Para que entiendasesto te pongo un ejemplo... práctico.. abre un nuevo proyecto, coloca un botón y pega el siguiente código y ejecútalo.... después que veas el resultado, para el proyecto, cambia Static Total as long' por Dim Total as long' y ejecútalo nuevamente.... verás la diferencia... en un caso y otro... ' ejecuta el ejemplo: Private Function SumasParciales(byval Valor as long, optional totalizar as boolean=false, optional PonerACero as boolean =false) as long static Total as long if poneracero=true then Total=0 end if total=total + valor if totalizar=true then SumasParciales = total end if end function ' llamamos 32 veces a la función anterior Private Sub Command1_Click() call SumasParciales(10,,true) for k= 1 to 30 call SumasParciales(k) next msgbox sumasparciales(222,true) ' con dim devolverá 222, con static devolverá 10 + (el sumatorio de 1-30) + 222, porque la variable Total conserva su valor entre llamadas. end sub
A un formulario no se le puede hacer el harakiri, por ejemplo ahora tenemos 2 formulario paraeste nuevo proyecto, el 1 tiene 3 botones llamados: Abrir, Eliminar y Listar en el otro tenemos uno llamado Cerrar... Copia y ejecuta elcódigo: fíjate que el último código de botón serefiere al del formulario 2 ' en el formulario 1 ' abrir Private Sub Command1_Click() Form2.Show End Sub ' eliminar Private Sub Command2_Click() Set Form2 = Nothing End Sub ' listar Private Sub Command3_Click() Dim f As Form On Local Error Resume Next For Each f In Forms f.BackColor = vbYellow Next Form2.BackColor = vbRed End Sub ' en el formulario 2 Private Sub Command2_Click() Unload Me End Sub
Si le damos al botón abrir 5 veces seguidas, verás que sólo se abre una vez, pulsasobre él en el botón cerrar, repite, sólo se abre una vez... pero ahora lo eliminamos 'set form1= nothing', pulsandoen botón eliminar, ahora pulsa en el botón abrir, verás que se ha abierto '''otro''' form2, vuelve a pulsar eliminar y vuelve a pulsar abrir, aparecerá otronuevo formulario copia siempre del primero... si pulsas varias veces seguidas en abrir, verás que no se abren más, sólo cuando es eliminado... Qué ha pasado ? ... un formulario no se aniquila desde el exterior, sino es descargado previamente, sin embargo al eliminar, lo que sucede es que lo elimina de alguna tabla, el identificador desaparece, está fuera de proceso y no se ejecutan los eventos Terminate, ni Unload, pero sigue constando una referencia en la colección Forms del proyecto, al solicitar de nuevo abrir, como el no encuentra el identificador form2, carga una instancia de form2 (una instancia es una copia), sin siquiera haber declarado una nueva variable... esto se debe a que el proyecto siempre mantiene una referencia de cada formulario, ya que si se destruyera real y totalmente en un momento dado form2 no existiría y y en consecuencia, nunca más podríamos volverlo a abrir estando el proyecto en marcha, porque no sabría ni que objetos tiene ni cual es su código, etc... De hecho para comprobar que todo es correcto puedes pulsar (con varias copias de form2) en el botón listar del form1. En teoría la única manera de crear instancias de form2 sería el siguiente código: ' abrir Private Sub Command1_Click() dim f as form set f= form2 F.Show End Sub
De hecho este código funciona exactamente igual que el anterior, en cambio si hubiéramos puesto 'set f= new form2' cada vez que pulsemos se creará una NUEVA instancia distinta de la anterior... antes all listar form2 siempre se pintaba de rojo, ahora no hay form2 abierto son instancias (con NEW), por eso todos son amarillos. Ahora lo bueno, si hemos dicho que todas las variables creadas en un procedimiento se destryen al salir del mismo porqué no se cierra y elimina el formulario al salir del mismo... la respuesta es que sólo sucede con las variables intrínsecas del entorno, los objetos se almacenan en la Tabla de Objetos en Ejecución, estos deben específicamente ser eliminados con Nothing y aún así los formulario deben previamente ser descargados para que éste libere sus referencias y el proyecto lo retire de la colección forms. Un objeto sólo se descarga definitivamente cuando no quedan referencias al mismo. Es posible llamar a un objeto de diferentes maneras, a esto se llama referencia. Imagina una persona que se llama Juan, algunos amigos le pueden llamar 'El bruto' y quizás sus padres le llamen 'Juanillo' en todos casos es la misma persona... un código de ejemplo: ' añade a unnuevo proyecto 2 botones,copia el siguiente código y pulsa (sin prisas) varias veces el botón 1 verás como el botón 2 se va moviendo con la referencia c y cada 3 veces lo movemos con la referencia command2 Private Sub Command1_Click() Static izq As Long, tope As Long Dim c As CommandButton Set c = Me.Command2 izq = izq + 50: tope = tope + 150 Call c.Move(600 + izq, tope) c.Caption = "NuevoControl" If tope > 450 Then Command2.left = 330 Command2.Top = 1800 izq= 600: tope=0 End If End Sub
Bueno, es bastante para este mensaje... ya tienes algunos conceptos más que espero te sirvan... Si tienes más dudas, esponlas, pero mejor siconcretas ya que sino resulta una respuesta muy dispersa.... Sobre lo del manual ya en otro momento te respondo...
191
« en: Martes 15 de Septiembre de 2009, 18:08 »
Es un problema de tipos de variables... el error seguramente está en tu código, pero en determinadas situaciones pasa inadvertido... Por ejemplo observa este código, valor es un variant, le asignamos una cifra numérica y lo mostramos... ok luego le asignamos una matriz y la mostramos... ok (aunque no presenta nada) sin embargo cuando llamamos al procedimiento mostrar valor, espera un byte, como le pasamos un variant se lo traga, pero cuando procesa el variant ve que aunque el variant tiene una matriz de bytes, sólo acepta una variable byte no una matriz... de paso reproduzco otros 2 errores de la misma naturaleza... Private Sub Form_Load() Dim bytes(0 To 9) As Byte Dim Valor As Variant On Local Error GoTo showError ' OK.... Valor = 2009 Debug.Print Valor MsgBox Valor ' ERROR: 13 error de tipo de variable Valor = bytes Debug.Print Valor MsgBox Valor MostrarValor (Valor) ' ERROR: 6 error de desbordamiento Valor = 2009 Debug.Print Valor MsgBox Valor MostrarValor (Valor) ' ERROR: 450 error de tipo de argumentos... Set Valor = Form1 MsgBox Valor MostrarValor (Valor) Exit Sub showError: MsgBox "Error: Nº " & Err.Number & vbCrLf & Err.Description & vbCrLf & Err.Source Err.Clear Resume Next End Sub Private Sub MostrarValor(ByVal n As Byte) MsgBox n End Sub
... a veces canta un error 450 en vez del 13, si consigue identificar con más detalle el error y también el error 6 (desboradmiento), pero cuando no queda claro (donde más suele ocurrir son en las clases) y no hay un gestor de errores salta el 13. La variable que más se presta a este tipo de error son los variant, pero también los de tipo Date y el propio CVERR... (convertir a error). Si ejecutas el código que te he puesto paso a paso (F8) lo entenderás mejor, viendo donde se produce el mismo.
192
« en: Domingo 13 de Septiembre de 2009, 06:12 »
Este códiigo vale perfectamente... aunque mejor si lo hubieras metido entre dos etiquetas de 'code', para que quedara convenientemente indentado y fuera cómodo de leer... De entrada, tal que lo que dices el objeto ApExcel lo tienes definido a nivel de procedimiento y en tu caso conviene definirlo a nivel de formulario, supongamos que tu procedimiento se llamara TrabajarExcel .... ' Este es el código que tienes para definir el objeto private sub TrabajarExcel () Dim ApExcel As Excel.Application Set ApExcel = New Excel.Application ' ................. resto del código end Sub ' este es el código que debes usar ' a nivel de formulario private ApExcel As Excel.Application private sub TrabajarExcel () Set ApExcel = New Excel.Application ' ................. resto del código end Sub
Ahora ya estando declarado el objeto a nivel de formulario lo podrás usar desde el resto de procedimientos que son llamados desde el que crea el objeto.... Fíjate, ahora de entrada ya vamos a meter en u´n procedimiento el formateo de las casillas private ApExcel As Excel.Application private sub TrabajarExcel () Din Varo as long ' declaración de otras variables locales que utiliza el procedimiento Set ApExcel = New Excel.Application If Not (ApExcel Is Nothing) Then ' crear el resto de objetos que necesitamos.... ' ... código ' .... código ' .... código Call FormatearCasillas Call CodigoMateriales(Varo) ' .... código ' .... código ' .... código Else Call MsgBox("Ocurrió algún propblema al intentar crear la instancia de Excel..." & vbCrLf & "Has agregado una referencia al proyecto ?... está instalado Excel ?", vbCritical + vbOKOnly, "No se puede continuar...") End Ifend Sub Private Sub FormatearCasillas() ApExcel.Range("A16:N17").Borders(xlEdgeBottom).LineStyle = xlContinuous ApExcel.Range("A16:N17").Borders(xlEdgeBottom).Weight = xlThin ApExcel.Range("A16:N17").Borders(xlEdgeBottom).ColorIndex = xlAutomatic ApExcel.Range("A16:N17").Borders(xlEdgeRight).LineStyle = xlContinuous ApExcel.Range("A16:N17").Borders(xlEdgeRight).Weight = xlThin ApExcel.Range("A16:N17").Borders(xlEdgeRight).ColorIndex = xlAutomatic ApExcel.Range("A16:N17").Borders(xlInsideVertical).LineStyle = xlContinuous ApExcel.Range("A16:N17").Borders(xlInsideVertical).Weight = xlThin ApExcel.Range("A16:N17").Borders(xlInsideVertical).ColorIndex = xlAutomatic ApExcel.Range("A16:N17").Borders(xlInsideHorizontal).LineStyle = xlContinuous ApExcel.Range("A16:N17").Borders(xlInsideHorizontal).Weight = xlThin ApExcel.Range("A16:N17").Borders(xlInsideHorizontal).ColorIndex = xlAutomatic ApExcel.Range("A16:N17").Borders(xlDiagonalDown).LineStyle = xlNone ApExcel.Range("A16:N17").Borders(xlDiagonalUp).LineStyle = xlNone ApExcel.Range("A16:N17").Borders(xlEdgeLeft).LineStyle = xlContinuous ApExcel.Range("A16:N17").Borders(xlEdgeLeft).Weight = xlMedium ApExcel.Range("A16:N17").Borders(xlEdgeLeft).ColorIndex = xlAutomatic ApExcel.Range("A16:N17").Borders(xlEdgeTop).LineStyle = xlContinuous ApExcel.Range("A16:N17").Borders(xlEdgeTop).Weight = xlMedium ApExcel.Range("A16:N17").Borders(xlEdgeTop).ColorIndex = xlAutomatic ApExcel.Range("A16:N17").Borders(xlEdgeBottom).LineStyle = xlContinuous ApExcel.Range("A16:N17").Borders(xlEdgeBottom).Weight = xlMedium ApExcel.Range("A16:N17").Borders(xlEdgeBottom).ColorIndex = xlAutomatic ApExcel.Range("A16:N17").Borders(xlEdgeRight).LineStyle = xlContinuous ApExcel.Range("A16:N17").Borders(xlEdgeRight).Weight = xlMedium ApExcel.Range("A16:N17").Borders(xlEdgeRight).ColorIndex = xlAutomatic ApExcel.Range("A16:N17").Borders(xlInsideVertical).LineStyle = xlContinuous ApExcel.Range("A16:N17").Borders(xlInsideVertical).Weight = xlThin ApExcel.Range("A16:N17").Borders(xlInsideVertical).ColorIndex = xlAutomatic ApExcel.Range("A16:N17").Borders(xlInsideHorizontal).LineStyle = xlContinuous ApExcel.Range("A16:N17").Borders(xlInsideHorizontal).Weight = xlThin ApExcel.Range("A16:N17").Borders(xlInsideHorizontal).ColorIndex = xlAutomatic ApExcel.Range("A1:F17").Font.Bold = True End Sub 'CODIGO DE MATERIALES Private Sub CodigoMateriales(ByVal Varo As Long) Dim VarSumatoria As Double Dim VarSumatoria2 As Double Dim indi As String Dim i As Long, l As Long, S As Long, Z As Long, ex As Long Dim cor As Long, Valorcorte As Long, VarStop As Long, VarCantCortes As Long Dim NumeroCorte2 As Long, VarIdCorte As Long i = 1 VarSumatoria = 0 VarSumatoria2 = 0 VarStop = 0 VarCantCortes = 0 NumeroCorte2 = FrmCrearCotizacion.ObtenerNumero(TxtDescripcionCorte6) NumeroCorte2 = NumeroCorte2 - 1 VarIdCorte1 = TxtIdCorte Varo = 0 l = 7 S = 8 Z = 18 ex = 1 For cor = 1 To Valorcorte indi = cor Limpiavec Z = 18 Adodc6.Recordset.MoveFirst Do While Not Adodc6.Recordset.EOF If TxtIdCotizacion6 = FrmCrearCotizacion.TxtIdCotizacion Then If ItenExiste6 = False Then VectorItemAvance(Varo) = TxtItem6 ApExcel.Cells(Varo + 18, 1).Formula = Val(TxtItem6) 'TxtItem6 ApExcel.Cells(Varo + 18, 2).Formula = TxtNOmbreItem6 'TxtNOmbreItem6 ApExcel.Cells(Varo + 18, 3).Formula = TxtUnidadItem6 'TxtUnidadItem6 ApExcel.Cells(Varo + 18, 4).Formula = TxtCantidadPresupuestada6 'cant ApExcel.Cells(Varo + 18, 5).Formula = Val(TxtVrInitItem6) 'Precio unitario ApExcel.Cells(Varo + 18, 6).Formula = TxtVrInitItem6 * TxtCantidadPresupuestada6 'Vr total If indi = ex Then ApExcel.Cells(17, l).Formula = "CANT" ApExcel.Cells(17, S).Formula = "Vr,TOTAL" ApExcel.Cells(16, l).Formula = "AVANCE " + indi 'Unir Celdas ApExcel.Range("G16:N16").HorizontalAlignment = xlCenter ApExcel.Range("G16:N16").VerticalAlignment = xlBottom ApExcel.Range("G16:N16").WrapText = False ApExcel.Range("G16:N16").Orientation = 0 ApExcel.Range("G16:N16").AddIndent = False ApExcel.Range("G16:N16").IndentLevel = 0 ApExcel.Range("G16:N16").ShrinkToFit = False ApExcel.Range("G16:N16").ReadingOrder = xlContext ApExcel.Range("G16:N16").MergeCells = False ApExcel.Range("G16:H16").Merge ApExcel.Range("I16:J16").Merge ApExcel.Range("K16:L16").Merge ApExcel.Range("M16:N16").Merge ex = ex + 1 End If Do While Not Adodc1.Recordset.EOF If TxtItem6 = TxtItem1 And TxtIdCotizacion1 = TxtIdCotizacion6 And TxtDescripcionCorte1 = "Corte No " + indi Then If FrmCrearCotizacion.FechaMayor(TxtFecIngresoCant1, TxtFechaInicio1) = True Then If FrmCrearCotizacion.FechaMayor(TxtFechaFin1, TxtFecIngresoCant1) = True Then VarSumatoria = TxtCantidadEjecutada1 + VarSumatoria 'cant eje End If End If End If Adodc1.Recordset.MoveNext Loop Adodc1.Recordset.MoveFirst ApExcel.Cells(Varo + Z, l).Formula = VarSumatoria 'cant eje ApExcel.Cells(Varo + Z, S).Formula = TxtVrInitItem6 * VarSumatoria 'Precio unitario SUMATOAVA = TxtVrInitItem6 * VarSumatoria Varo = Varo + 1 End If End If Adodc6.Recordset.MoveNext VarSumatoria = 0 Loop Adodc6.Recordset.MoveFirst l = l + 2 S = S + 2 Z = Z + 1 Next cor End Sub
No es necesario que me extienda más... debe quedarte claro como se procede.... Simplemente se trata de cumplir el lema romano: 'Divide y vencerás'. Toma líneas de código que realicen una labor concreta y crea un procedimiento que con el nombre que define esa labor, mueve esas líneas a dicho procedimiento. Como has creado código espagueti, tendrás un montón de variables en el procedimiento, las variables que refieren objetos de excel declaralos a nivel de formulario y las variables que se usan en muchos procedimientos también (por ejemplo la variable Adodc6), en cambio las variables que sólo se usen en un procedimiento las declaras locales (por ejemplo en el procedimiento 'CodigoMateriales', la variable 'cor' se refiere al bucle se declara y muere en el procedimiento la variable) también podemos pasar variables declaradas en un procedimiento a otro mediante parámetros (por ejemplo, si te fijas la variable 'Varo' está declarada en el procedimiento original y se la hemos pasado como parámetro al procedimiento 'CodigoMateriales') Eso, si declara todas y cada una de las variables en el tipo que son, o de otro modo se asume que son variant y las variables variant consumen mucho más recursos y son más lentas Naturalmente cada escisión del procedimiento debe ser suplantado por la llamada al correspondiente procedimiento que contiene las líneas que hemos movido... La idea no es que tengas 8 procedimientos de 200 líneas sino tal vez 15 unos con 40 líneas y otros con 120 líneas... conviene que los separes de acuerdo a labores concretas, si las siguientes 80 líneas dan formato, pués creas un procedimiento llamado DarformatoA... y metes ahí el código que realiza esa labor, si luego tienes 30 líneas que leen datos de un archivo pues creas otro procedimiento llamado TomarDatosFicheroTemporal, mueves ahí elcódigo que realiza esa tarea y donde estaban colocas la llamada a dicho procedimiento y así sucesivamente. Si las líneas no las separas reunidas por tareas tendrás muchas variables compartidas entre diferentes procedimientos lo que te forzará a declararlas a nivel de formulario. en principio no hay nada malo en tener variables declaradas a nivel de formulario, simplemente que las variables declaradas a nivel de formulario utilizan memoria hasta que se destruye elformulario, las variables de un procedimiento (declaradas dentro de él) se descargan de memoria cuando se sale del procedimiento.... Si tienes dudas, ahora ya puedes hacer preguntas concretas...
193
« en: Sábado 12 de Septiembre de 2009, 05:06 »
Si no recuerdo mal los procedimientos están limitados a un tamaño de 64 kb (que se cargan en memoria de una sola vez)... tus 1600 líneas pueden ser aproximadamente 120 kb (depende del tamaño de las líneas)... sin embargo eso no significa que no se pueda ejecutar, todo lo que tienes que hacer es repartir el código en varios procedimientos... Es bastante posible que estés produciendo 'código espagueti'...
Es buen seguro que mucho código que tienes en ese procedimiento estaría mejor ubicado en su propio procedimiento...
Espón 30 ó 40 líneas y te muestro in situ como repartirlo (ya sería mala suerte que justo fueras a exponer un conjunto de líneas que precisamente deban ir juntas y seguidas)... básicamente un procedimiento de más de 200 líneas es seguro que parte de elló puede moverse a otro procedimiento.
194
« en: Viernes 11 de Septiembre de 2009, 17:16 »
Ok... Bien, veamos deberías tener como mínimo 5 tablas (el sistema que te propongo y que tienes que adaptarlo a tus necesidades), Las tablas serían DatosGenerales, Clientes, ClientesInactivos, Semanario, SemanarioInactivo La Tabla Clientes y clientesInactivos son idénticas, sólo cambia el contenido, sucede igual con Semanario y SemanarioInactivo Tabla tblDatosGenerales: contiene datos estadísticos, el registro 0 son los datos globales, la suma detodos los años, menos el que va en curso. + Campos: - Año: clave única, refiere el número de registro, el número 0 es el registro 0 que representaría la suma de todos los años, el global, los demás son los específicos del año referido. El años en curso son los datos actuales. El global tiene los datos de todos los años vencidos, excepto el que va en curso, si no se hace así, cada vez que se actualice el año actual debe actualizarse también el registro global(el 0). Y tenemos un registro global, porque de otro modo cada vez que quiesiéramos obtener dichos datos deberíamos hacer la suma correspondiente, de este modo sólo se hace 1 vez cuando cambia el año. - ClientesActivos: indica el número de clientes vigente - ClientesInactivos: indica el número de clientes - ClientesMorosos: indica el número de clientes que a la finalización del año tiene pendiente el pago de alguna factura - PagosMorosos: indica el número de facturas no cobradas... - PrimeraSemanaAñoCompleta: indica si la primera semana del año empieza el primer día de lasemana o no, del año en curso, para años anteriores este valor debe calcularse, para el año actual sólo se calcula cuando se crea la tabla Semanario y elresto de veces se consulta, este campo sólo esescribible 1 vez, cuando se crea la tabla Semanario. + Metodos: clientestotales, señala cuantos clientes totales tiene el gimnasio - clientestotales= clientesActivos + ClientesInactivos Tabla tblClientes (Activos): contiene los datos básicos de los clientes, además de la fecha de alta y el estado de pago de la semana actual y otro campo que refleja (no es imprescindible pero permite hacer consultas rápidas) la cantidad de semanas pendientes de pago. ...y esta debería tener los siguientes campos casi forzosos, otros datos ya a tu gusto y necesidades - IdCliente: clave autonumérica , esto deberías usarlo aparte de que uses el número del documento de identidad, en el gimnasio es mejor que traten con cliente 1, cliente 25 y no por un número que después de todo concierne a la privacidad... el número de cédula sólo debe exponerse cuando lo que se precise sea identificar a alguien, para operar con la base de datos debe usarse un id anónimo. Puedes si lo crees conveniente tener una tabla cuyos campos sean precisamente un par de claves, el idcliente y NumeroCedula, pero yo ese dato aunque sea obligatorio al darse de alta, lo dejaría semioculto en todas las operaciones que no sean exclusivamente modificar datos del cliente o especifiicamente consultar ese dato. Más aún este dato lo haría de modo que sólo admita una escritura y para evitar errores, al añadir el dato debería 'teclearse' 2 veces, sólo si coinciden se añade, si no borrarlo y solicitar reentrar... Todo esto es por seguridad, para evitar que alguien con acceso a la BD intercambie datos decliente y por tanto falsifique sus datos de pago por el de otro cliente sin deudas pendientes. - Nombre: nombre del cliente, - Apellido1: idem.... - Apellido2: idem.... - dirección: datos de donde reside, puede ser un único campo de texto o desglosarlo en varios(es lo emjor, pero cada país tiene sus propios términos de definición de divisiones del territorio, por lo que esto va según usos y costumbres de cada lugar). - FechaAlta: tipo date. - FechaCalculo: es necesario para consultar pagos pendientes y poder filtrar por fecha desde el alta, el primer año este valor tiene el mismo que fecha de alta, después del 31 de diciembre del añoen curso tiene fecha 1 de enero . Esto obliga a que cuando el cliente (el del gimnasio) cree un nuevo año, este dato sobre cada cliente del gimnasio se coloque la fecha 1 de enero. - PagosPendientes: contendrá el número de semanas que el cliente tiene sin pagar, no se considera la semana actual, esta tiene su propio campo. - SemanaActualPagada: Indica si la semana en curso está pagada o no. Cuando se inicia una nueva semana se recorre toda la tabla de clientes y este campo se pone a false (0), esto es por defecto vale 0. Tabla tblClientesInactivos: Esta tabla es idéntica en campos a la tabla clientes. En ella lo que tienes son las fichas de los clientes que se dieron de baja, la tabla no se utiliza excepto cuando un cliente vaya a ser añadido. El hecho de tener 2 tablas de esta manera hace que realizar consultas sobre los clientes activos (que será lo más común y frecuente) hace que sean más rápidas al contener menos registros. También sirve para no tener largas tablas de consultas sobre clientes cuyos datos no interesan precisamente en un momento dado. Si quieres omitir esta tabla, puedes hacerlo pero entonces en la tabla Clienhtes tendrás que añadir obligadamente un nuevo campo llamado: - ClienteActivo: tipo boolean y al hacer operaciones de actualización y/o consulta típicamente en las queries deberás añadir el condicional 'and tblCliente.ClienteActivo = True' (o false), a la mayoría de ellas... + Metodos: Ahora una somera descripción de los métodos que atañen a estas 2 tablas: - AñadirCliente: toma el numcedula y obtenemos el Idcliente en la tabla clientesinactivos usando el campo NumCedula, si existe (distinto de 0), se copia dicho registro a la tabla clientes y se eleimina de la tabla ClientesInactivos, luego se le solicita al cliente que revise si alguno de sus datos no es vigente (excepto el numcedula que no se puede cambiar). ... si el cliente no existe en la tabla clientesinactivos, entonces se introduce elresto de datos. sobre la tabla DatosGenerales se actualizan convenientemente los campos clientesActivos, clientesInactivos... clientesActivos se suma 1, si el cliente existía previamente a clientesInactivos se le resta 1. - DesactivarCliente: primero habrá que decidir que se desea hacer cuando elcliente tiene pagos pendientes, una vez sabido esto y hecho lo que se desee al respecto el registro se copia a la tabla ClientesInactivos y se borra de esta, en la tabla de datos generales ClientesActivos se resta 1 y clientesInactivos se suma 1. Si elegiste tener una sola tabla de clientes, entonces basta que marques el campo ClienteActivo=0 - EliminarCliente: Igual que al desactivar clientes, deberemos decidir si uncliente con deudas puede o no ser borrado y decidir que hacer con la deuda... Esta función debe estar restringida y sólo poderse usar con privilegios y advertencias, ya que eliminar definitivamente un cliente de la BD implica borrar todos sus datos definitivamente. Por seguridad antes de eliminar un cliente, debería tomar precauciones que de acuerdo al nivel de seguridad que se quiera imponer debería ir desde guardar un log de los datos del cliente eliminado hasta solicitar una clave se seguridad o enviar por email los datos del cliente a eliminar o el sólo intento de eliminación, incluyendoen todos los casos el identificador del usuario de la BD que está llevando a cabo dicha operación. La operación consistirá enlocalizar en la tabla Semanario (si es un clienteactivo, semanarioInactivo si es un clienteInactivo) y eliminar todos los registros cuyo idcliente coincidan... (where tblSemanario.Idcliente = tblCliente.IdCliente), si como decíamos al principio tiene facturas pendientes, de acuerdo a lo que hayamos decidido, antes de borrar estos registros, deberemos hacer lo deseado, y luego si procede actualizar los campos de ClientesMorosos y PagosMorosos de la tabla DatosGenerales, luego se elimina el registro de la tabla Clientes finalmentese actualiza los campos requeridos en la tabla Datosgenerales. ClientesActivos, clientesinactivos... - ListarClientes: busca sobre la tabla los clientes cuyo valor en el/los campos a buscar coincidan con el patrón de búsqueda, debería poder devolver una lista simple o completa, la lista simple sería un par idcliente- nombre completo usuario, en la lista completa aparecería el registro completo de cada usuario hallado. - ConsultarDatoscliente: Recoge de la tabla el registro cuyo idcliente se solicita y se exhibe en un formulario, debería añadir boton para modificar datos y si se pulsa copiar dichos datos a otro formulario para modificar datos, ese contiene 2 botones, aceptar_cambios y rechazar_cambios, rechazar campos cierra elformulario, aceptar cambios primero actualiza dichos datos en la tabla y cierra el formulario, al regresar al formulario de datos del usuario, si se hicieron cambios se relee el registro y se vuelca nuevamente en el formulario... En esta consulta también debería haber otrobotón para solicitar estadodepagos. - ModificarDatosCliente: (Como se especificaba en el apartadoanterior). Esta función recibe un registro y lo guarda en la tabla, los campos no modificables son el idcliente, numcedula,fechaalta. Tabla Semanario: Esta tabla cuando se crea la BD, recrea tantos registros como semanas tiene el año. Puede ser concebida de muchas maneras, yo te propongo el siguiente sistema, no es el más óptimo pero si el gimnasio no tiene miles de clientes (lo habitual suelen ser unas decenas), te ahorra una tabla y los métodos son más sencillo s de programar, para ser puristas, esto no es aceptable, pero para tu caso puede valer perfectamente. Latabla tiene sólo 2 campos, NumeroSemana y clientes. - NumeroSemana: es una clave cuyos límites van de 0 a 54 (el año tiene 52 semanas completas y 1 o 2 incompletas). Si el año empieza en una semana incompleta el primer registro de esta tabla es 0, y tiene los dato de la última semana del año anterior, si este año empieza en lunes (supuesto el caso que consideremos que elprimer día de la semana es lunes), el registro 0 está vacío y no deberá poderse editar. - Clientes: Es un campo tipo string, cuando se crea la tabla este campo es una cadena vacía, cuando un cliente paga la semana x, este campo se actualiza, el modo de actualizarse es añadir el Idcliente a dicha cadena... ejemplo: where tblsemanario.Numerosemana= semana ... tblSemanario.clientes= tblSemanario.clientes & " , " & idcliente Una función en el formulario podría ser Function SemanaPagada(byval Semana as integer, Idcliente as long) as boolean 'si el valor de la semana no es correcto o el idcliente no existe, ' devolver false 'en otro caso ' ... ejecuta la querie 'fin comprobaciones end function
Dado que los gimnasios notienen largas listas de clientes, saber si un cliente ha pagado o no es bastante fácil y rápido... todolo que necesitamos hacer es recoger el campo clientes de lasemana deseada en latabla semanario, volcar la cadena en una matriz y localizar si exise el idcliente. ejemplo. ' si pagó devolverá 1, si no pagó devolvera 0 si los datos de entrada no se validan devolverá -1 Function EstaPagadaSemanaPorCliente(byval Semana as integer, Idcliente as long) as integer dim clientes() as string, ClientesSemana as string , k as long 'si el valor de la semana no es correcto o el idcliente no existe, ' devolver -1 ' en otro caso clientesSemana= query(semana, idcliente) if clientesSemana <>"" then clientes= split(clientesSemana, " , ") for k= 0 to ubound(clientes) if clientes(k)= cstr(idcliente) then EstaPagadaSemanaPorCliente=1 exit for end if next end if 'fin comprobaciones end function
Si no quieres mantener este sistema, entonces esta tabla secomplica contendrá un varios campos llamado Idcliente Numerosemana, y habrá un registro por cada cliente que ha pagado cada semana (100 clientes al año supondrían más omenos 100*52= 5200 registros, la forma de arriba sólo tiene 50 y pocos registros) , cuando un cliente paga se añade un registro a esta tabla con su idcliente, Numerosemana, adicionalmente podría añadirse un campo con la cantidad pagada e incluso otro con la fecha (fecha y hora) de pago, e idUsuarioBD de la BD que registró el pago en la BD... este sietema es mejor, más profesional, pero también te dará más trabajo, si el número de clientes no es elevado, el sistema anterior es perfectamente válido, no estará normalizada, pero cumple su objetivo. La Tabla SemanarioInactivo, tiene el mismo propósito que la Tabla ClientesInactivos respecto de la tabla clientes, pero siendo un reflejo esta de la tabla Semanario... En fin creo que me extendido lo bastante como para que tengas por donde tomarlo... p.d.: Como el mensaje me ha salido largo en otro momento te comento otros detalles que se me han quedado en el tintero, ahora mismo no tengo tiempo...
195
« en: Jueves 10 de Septiembre de 2009, 15:10 »
Esto es un simple problema de combinatoria que puedes resolver en 3 fases. Sin entrar en asuntos de programación, sólo usando matemáticas deberías conocer como aplicarlo...
1 -A - Dados dos grupos de 4 elementos, hallar cuantas combinaciones de 3 elementos pueden formarse.
Este es el problema base, luego al resultado hay que restarle las combinaciones que se forman cuando los 3 elementos son de un único grupo, excepto que aparezca en ambos grupos.
2 - B - Esto es, hallar cuantas combinaciones de 3 elementos (incluso con repetición) puede formarse en ambos grupos.
3 - C - Hallar cuantas de las combinaciones de B, sin repeticion de elementos, tienen 1 elemento que pertenece a ambos grupos. El resultado será la suma de = A - B + C.
Si el algoritmo es matemático son 3 fórmulas, si no, todavía puedes resolverlo por programación a base de bucles y condicionales aunque el código será menos óptimo.
196
« en: Jueves 10 de Septiembre de 2009, 03:34 »
Crea una base de datos.
Si las bases de datos no es lo tuyo (que es loque se deduce de la pregunta que haces), siempre podrás hacer el mantenimiento sobre ficheros de texto plano usando estructuras. En este caso usa una jerarquía a modo de tablas.
..ya con la dirección que enfoques se podrá concretar ...
197
« en: Jueves 10 de Septiembre de 2009, 03:28 »
Te he ofrecido ayuda... al escribir: Esto es, en el código escribe put ,coloca el cursor encima de dicha palabra, pulsa la tecla F1, se mostrará la ayuda por ese tema, léelo y luego pulsa en el enlace 'ejemplo'... puedes copiar el ejemplo y pegarlo en tu código (de un nuevo proyecto), ejecútalo paso a paso, si fuera preciso para entender el código...
Se puede decir de muchas maneras y no creo haber sido brusco, pero pedir ayuda basada en la vagancia no es bienvenido. Repito, lo que solicitas es tan elemental que lo puedes encontrar sin problemas en la ayuda del programa fácilmente. Que sentido tiene repetir código que a fin de cuentas es totalmente similar al que allí viene ?. Sólo tienes que adaptarlo ligeramente a tus necesidades...
198
« en: Miércoles 9 de Septiembre de 2009, 21:48 »
Lo que pides es muy elemental y no es motivo para el foro (a juicio mío), de hecho si buscas en la ayuda de VB, 'Put' o 'Open' , "write", etc... podrás encontrar ejemplos sencillos a patadas para hacer cosas muy elementales como esto que pides de los que puedes aprender.
Esto es, en el código escribe put ,coloca el cursor encima de dicha palabra, pulsa la tecla F1, se mostrará la ayuda por ese tema, léelo y luego pulsa en el enlace 'ejemplo'... puedes copiar el ejemplo y pegarlo en tu código (de un nuevo proyecto), ejecútalo paso a paso, si fuera preciso para entender el código...
199
« en: Miércoles 9 de Septiembre de 2009, 21:21 »
Tu exposición ha quedado perfectamente clara. Te indico anticipadamente que no aporto una solución a tu problema, tan sólo ideas.
Dado que no domino en profundidad Powerpoint y que powerpoint no es parte del entorno de desarrolo de visual Basic, vamos a ver que se puede hacer...
Intenta abrir la presentación en powerpoint (lo que señalo ahora depende de la versión que tengas instalada, lo que refiero atañe a laversión 2000 (creo, hablo de memoria) y puede que enotra versión debas encontrarlo en otro apartado)... ve al menú presentación, pulsa en el submenú 'Configurar presentación', en la ventana que se abre en la parte inferior casi a la izquierda del todo hay una opción que a mi me aparece desmarcada, porque sólo tengo un monitor y que reza: 'Mostrar en:' ---- elige el monitor, si tu quieres trabajar en el principal pués las diapositivas deberán ir al monitor secundario.
Además en dicha ventana deberías elegir la opción: 'Presentación autoejecutable' , frente a la oopción 'Presentación con un orador en directo'
Por defecto todas las presentaciones tienen activado el monitor principal, y powerpoint, Intuyo que, debe interpretar que en un momento dado en el que el programa está activo (tiene el foco) se considera 'monitor principal', digamos virtual, de tal modo que cuando pierde el foco por serlo 'virutal' cesa.
En fin creo que es más alguna opción no muy visible en powerpoint la que debería activarse. En última instancia quizás podrías hacerlo funcionar a las bravas, si arrancas el powerpoint desde tu propio programa con un 'shelllexecute' y manteniendo una referencia del proceso en tu programa para arrancarlo o deternerlo a voluntad (mediante menú, etc..), de este modo arrancado en segundo plano es bastante problable que el comportamiento no sea el mismo que si el parent es el explorer...
También puedes intentar a probar con el 'Asistente para presentaciones portátiles', Este asistente se utiliza en casos en los que no está instalado powerpoint en un equipo, pero no necesariamente esto es imprescindible, el ejecutable se llama powerpoint veiwer ( Ppview32.exe ) y deberías encontrarlo en algun lugar dentro de office, o en el CD-ROM de office si no está instalado. acepta como línea de comandos la ruta de la presentación... A este efecto, ya de paso te indico como crear lista de presentaciones... basta editar un archivo (a mano o por código), el contenido de este fichero no es otro que rutas completas a archivos de presentaciones (una por línea) entrecomilla las rutas para evitar problemas si hay un espacio en blanco en el nombre o la ruta . Cuando termines el archivo debes renombrarlo con la extensión .lst (de lista). ejemplo de una lista: "c:WindowsPresentacion1.ppt" "d:Mis PresentacionesFebrerofebrero.pps" /v "c:2020Marzo.ppt"
Opciones de la línea de comando de ppview32 Para aplicar una opción a varias presentaciones, las presentaciones deben ir en la misma línea separadas por un espacio. /v muestra la advertencia de que una presentación con macros puede ocultar un virus (interesa cuando envias algo a alguien por correo, que se asegure de pasarle previamente el antivirus). /a es una opción de la línea de comandos de ppview32 para pasar automáticamente de una diapositiva a otra... /k se ejecutan en el modo exposición... /l cíclico, se repite en bucle hasta que se pulse la tecla escape. /r=inicio-fin especifica que la presentación empieza en la diapositiva numerada como inicio y finaliza en la indice fin, por ejemplo si la presentación tiene 200 diapositivas pero lo que vamos a hacer interesa sólo desde la 80 a la 91, escribiríamos la línea deejemplo: /r=80-91 "C:la presentacion.pps". Dependiendo de las versiones puede que las opciones sean otras, por ejemplo en este ordenador /k especifica una contraseña y no acepta en la lista las opciones, pero si como una línea de un archivo bat: c:ppview32.exe /r=3-8 "c:ejemplo de prueba.ppt"
ejemplo: supongamos que (para no hacer la ruta muy larga) el programa ppview32.exe y el archivo lista.lst están ubicados en 'C:' , una línea de comando para un bat podría ser como está: c:ppview32.exe "lista.lst" Se ejecutará powerpoint (versión portable), tomará el contenido del archivo lista.lst y ejecutará una a una las presentaciones... Eso si puedes optar por ejecutar el bat o hacer un shellexecute por código, ya dependiendo del objetivo final...
También recuerdoque en la ayuda de powerpoint, hay un apartado enfocado a los programadores para integrar powerpoint con Visual Basic, quizás allí encuentres más info. Si tu versión de ayuda noincluye dicho apartado me das un aviso por privado y veré de mandártelo de alguna manera.
Siento no haberte podido dar una respuesta clara y definitiva, pero creo que te he ofrecido alternativas e ideas para probar por tí mismo.
Prueba y nos cuentas si el problema se solucionó...
Incluyo una imagen (comprimida) que muestra los objeto powerpoint que se pueden usar con Visual Basic
200
« en: Sábado 29 de Agosto de 2009, 09:36 »
Ahora ha quedado más claro... Te comento. Efectivamente el datetimepicker no puede quedar vacío, puesto que maneja fechas al dejarlo vacío correría el riesgo de introducirse algo no válido con lo que devolvería datos erróneos, elmodo de asegurarse que siempre contiene un dato válido de fecha es que siempre tenga una fecha. Pero esto no significa que no pueda 'quedar vacío'. quedar vacío desde el punto de vista de un programador es más que nada conocer con exactitud el contenido de un dato, el cual puede considerarse descartable... Por ejemplo supongamos que se ha de introducir una fecha del año que viene, entonces podrías poner la fecha mínima del datetimepicker al 31 de diciembre de esteaño, la cual puede considerarse el dato vacío, de modo que cuando el usuario acepte el dato comprobamos la fecha si es el 31 de diiiciembre de éste año 'sabemos' que el usuario ha dejado el campo vacío. Ahora eres tú quien debe decidir que fecha se coloca cuando el usuario deja el campo vacío, le podemos o bien en el evento validate del dtpicker, cancelar, lo cual regresa el foco al control, mostrándole antes que debe elegir una fecha en el rango que entiedes debe ser aceptado. El datetimepicker, permite formatear completamente sus campos, de hecho si ejecutaste el ejemplo de más arriba verás en el load del formulario que le doyformato al control con estas 2 líneas: .Format = dtpCustom ' personalizado ' HH formato 24 horas, hh formato 12 horas... .CustomFormat = "'Fecha elegida: ' dddd dd MMMM yyy ' Hora: 'HH:mmtt" 'cadena personalizada dentro del formato entre comillas simples
El control maskedtext, va pien para otros tipos de datos, normalmente cadenas de texto complejas, pero aquí dado que además de poder formatear el texto, el dtpicker permite al usuario elegir una fecha con una gran comodidad, no está justificada la presencia del control Maskedtext. Otra gran ventaja del control Datetimepicker respecto del maskedtext es que el datetimepicker, permite fijar una fecha mínima y una fecha máxima, esto significa 2 cosas, para empezar el calendario no avanzará ni más allá de la fecha max ni más acá dela fecha min, y en el supuesto que el mes entrado, la fecha elegida (el día) sea menor que el mínimo muestra un mensaje de fecha no permitida. Esto te ahorra bastante trabajo sobre el maskedtext, pués con éste tendrías que verificar que la fecha que el usuario introduce a mano no es de cuanto Cristo se fue al desierto, o de cuando Deckard atrapa al último replicante. El cóodigo que te mostré arriba, tiene bastante 'sustancia' por lo que yo te aconsejo que lo estudies hasta que lo compendas totalmente.... lo único que no hacemos y que se podría añadir es derivar el foco nuevamente cuando el cambio de propiedad no se produce... Haciendo esos cambios mencionados (sólo te lo pongo en la propiedad fecha sería equivalente el cambio en las otras 2 propiedades) el código quedaría: Private Property Let Fecha(ByVal f As Date) If DateDiff("d", Now, f) >= 1 Then p_Fecha = f s_Cambio = True Else ' no se acepta el cambio MsgBox "La fecha tiene que ser posterior a hoy." DTPicker1.setfocus ' <==================================================== éste esel cambio introducido =================================== End If End Property
Páginas: 1 ... 6 7 [8] 9 10 ... 29
|
|
|