Data binding
Funciones
dbindst_t | dbind (...) |
dbindst_t | dbind_enum (...) |
dbindst_t | dbind_binary (...) |
dbindst_t | dbind_alias (...) |
dbindst_t | dbind_unreg (...) |
type* | dbind_create (...) |
type* | dbind_copy (...) |
void | dbind_init (...) |
void | dbind_remove (...) |
void | dbind_destroy (...) |
void | dbind_destopt (...) |
int | dbind_cmp (...) |
bool_t | dbind_equ (...) |
type* | dbind_read (...) |
void | dbind_write (...) |
void | dbind_default (...) |
void | dbind_range (...) |
void | dbind_precision (...) |
void | dbind_increment (...) |
void | dbind_suffix (...) |
- 1. Registrar tipos de datos
- 2. Creación de objetos
- 2.1. Inicialización de objetos
- 2.2. Copia de objetos
- 2.3. Edición de objetos
- 2.4. Tipos básicos
- 2.5. Objetos anidados
- 2.6. Objetos binarios
- 2.7. Uso de arrays
- 2.8. Valores por defecto
- 2.9. Rangos numéricos
- 3. Comparación de objetos con DBind
- 4. Serialización con DBind
- 5. Importar y exportar a JSON
- 6. Sincronización con interfaces gráficas
En lenguajes de alto nivel, como .NET o Javascript, data binding (o vinculación de datos) es una técnica que permite establecer una conexión automática entre los datos de una aplicación y sus elementos de interfaz de usuario. El módulo DBind de NAppGUI implementa y extiende este concepto en lenguaje C, ya que posibilita la automatización de ciertas tareas sobre las estructuras y objetos de nuestra aplicación (Figura 1). Gracias a ello evitaremos generar código redundante y problemático de mantener, proporcionando una interfaz general para:
- Creación, destrucción y copia de objetos.
- Comparación de objetos.
- Serialización: Lectura y escritura en streams.
- Importación/exportación en diferentes formatos, como JSON.
- Vinculación con interfaces de usuario.
1. Registrar tipos de datos
- Utiliza dbind para registrar estructuras.
- Utiliza dbind_enum para registrar enumerados.
El primer paso para utilizar data binding es registrar en DBind los tipos definidos por el usuario (struct
y enum
). Los tipos básicos con conocidos de antemano, ya que se añaden automáticamente al iniciar el programa. Partimos de nuestro sencillo modelo de datos (Listado 1):
Product
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Lo añadiremos a DBind al iniciar la aplicación (Listado 2). Esto creará una suerte de "base de datos" que albergará el nombre, tipo y desplazamiento de los campos de cada estructura (Figura 2). Gracias a esta información será posible manipular objetos de forma totalmente automática y sin la necesidad de crear código adicional por parte del programador.
1 2 3 4 5 6 7 8 9 |
dbind_enum(type_t, ekCPU, ""); dbind_enum(type_t, ekGPU, ""); dbind_enum(type_t, ekHDD, ""); dbind_enum(type_t, ekSCD, ""); dbind(Product, type_t, type); dbind(Product, String*, code); dbind(Product, String*, desc); dbind(Product, Image*, image); dbind(Product, real32_t, price); |
1.1. Alias de tipos
- Utiliza dbind_alias para registrar alias (
typedef
).
dbind()
utiliza el nombre del tipo de cada campo de la estructura para localizarlo dentro de su registro interno. Utilizar tipos no registrados conllevará un error de tipo ekDBIND_TYPE_UNKNOWN
. Por ejemplo, en (Listado 3), DBind no conoce que el tipo color_t
es, en realidad, un uint32_t
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Para soportar tipos equivalentes declarados mediante el typedef
de C, tan solo deberemos añadirlos como 'alias' en DBind (Listado 4):
typedef
mediante alias en DBind.
1 2 3 4 5 6 |
2. Creación de objetos
- Utiliza dbind_create para crear objetos.
- Utiliza dbind_destroy para destruir objetos.
Una de las primeras utilidades de DBind es la creación, inicialización, copia y destrucción de objetos sin tener que programar explícitamente constructores y destructores. Esta operativa puede llegar a ser engorrosa cuando existen objetos anidados o contenedores como parte del objeto principal. En (Listado 5) tenemos un sencillo ejemplo de construcción y destrucción de un objeto de tipo Product
sin haber definido explícitamente funciones para ello. Al estar registrado, DBind sabe como reservar memoria e inicializar cada campo de acuerdo a unos Valores por defecto.
1 2 3 4 5 |
Product *prod = dbind_create(Product); // 'prod' correctly initialized by default ... dbind_destroy(&prod, Product); // 'prod' correctly destroyed including all its fields |
2.1. Inicialización de objetos
- Utiliza dbind_init para inicializar objetos.
- Utiliza dbind_remove para liberar objetos.
dbind_create()
y dbind_destroy()
actúan sobre el Segmento Heap, es decir, reservan y liberan la memoria dinámica necesaria por el propio objeto. Pero en ocasiones es posible que los objetos residan en un espacio de memoria gestionada automáticamente, bien porque se alojen en el Segmento Stack o bien en un contenedor tipo ArrSt
o SetSt
. En estos casos utilizaremos los inicializadores y liberadores que trabajarán sobre los campos internos del objeto sin preocuparse por la memoria del propio objeto (Listado 6). Evidentemente, los campos internos de una estructura inicializada con dbind_init()
pueden reservar memoria dinámica que será liberada por dbind_remove()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Object in stack Product prod1; // Object in container Product *prod2 = arrst_new(arrst, Product); dbind_init(&prod1, Product); dbind_init(prod2, Product); // 'prod1', 'prod2' correctly initialized by default ... dbind_remove(&prod1, Product); dbind_remove(prod2, Product); // ONLY 'prod1', 'prod2' fields destroyed // The object itself memory will be managed automatically // Because lives in stack or container |
2.2. Copia de objetos
- Utiliza dbind_copy para copiar objetos.
La duplicación de objetos también está automatizada, permitiendo una copia "profunda" y recursiva de todos los campos y objetos anidados, sin necesidad de definir ninguna función de copia (Listado 7).
1 2 3 |
Product *nprod = dbind_copy(prod, Product); ... dbind_destroy(&nprod, Product); |
2.3. Edición de objetos
Una vez creado un objeto de un tipo registrado, este podrá ser editado y manipulado como cualquier objeto de C ya que, en realidad, no deja de ser una instancia de un tipo struct
(Listado 8).
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Product *prod1 = dbind_create(Product); Product prod2; dbind_init(&prod2, Product); // 'prod1', 'prod2' are really struct instances ... str_upd(&pr1->desc, "Another desc"); ... pr2.price = 100.23f; ... bstd_printf("Product name: %s with price: %.2f\n", tc(pr2.desc), pr2.price); ... dbind_destroy(&prod1, Product); dbind_remove(&prod2, Product); |
2.4. Tipos básicos
Como ya comentamos al principio, únicamente tenemos que registrar las estructuras y enumerados propios de nuestra aplicación. DBind ya conoce de antemano los tipos básicos y las cadenas de caracteres (String
), por lo que serán aceptados como tipos de campo en struct
:
- Booleano:
bool_t
. - Enteros:
uint8_t
,uint16_t
,uint32_t
,uint64_t
,int8_t
,int16_t
,int32_t
,int64_t
. - Reales:
real32_t
,real64_t
. - Cadenas de texto dinámicas:
String
.
El uso de tipos no registrados será ignorado por dbind()
. Utiliza dbind_alias() si quieres utilizar tipos básicos equivalentes.
2.5. Objetos anidados
Un objeto registrado puede formar parte de otro objeto registrado, utilizando reserva de memoria estática o dinámica (Listado 9). En este caso, los objetos anidados stock1
y stock2
de tipo Stock
serán inicializados con sus valores por defecto al crear el objeto principal mediante dbind_create(Product)
.
Stock
anidados en Product
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
typedef struct _stock_t { uint32_t min_units; uint32_t max_units; uint32_t cur_units; String *location; bool_t required; } Stock; typedef struct _product_t { ... Stock stock1; // Static alloc Stock *stock2; // Dynamic alloc } Product; // Stock struct to DBind dbind(Stock, uint32_t, min_units); dbind(Stock, uint32_t, max_units); dbind(Stock, uint32_t, cur_units); dbind(Stock, String*, location); dbind(Stock, bool_t, required); // Stock fields in Product dbind(Product, Stock, stock1); dbind(Product, Stock*, stock2); ... Product *prod = dbind_create(Product); // 'stock1', 'stock2' instances correctly initialized bstd_printf("Product locations: %s, %s\n", tc(prod->stock1.location), tc(prod->stock2->location)); dbind_destroy(&prod, Product); |
2.6. Objetos binarios
- Utiliza dbind_binary para declarar tipos binarios.
Un objeto binario (u opaco) es aquel cuya declaración está oculta, es decir, no tenemos acceso a (o no queremos registrar en DBind) su tipo struct
. Este tipo de objetos serán manejados como bloques de bytes indivisibles, sin entrar en detalles sobre la naturaleza o origen de su contenido. Un ejemplo claro lo tenemos con el tipo Image, declarado automáticamente por NAppGUI. Gracias a ello podremos utilizar imágenes dentro de nuestro modelo de datos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
typedef struct _product_t { ... Image *image; } Product; dbind(Product, Image*, image); Product *prod = dbind_create(Product); if (prod->image != NULL) { // Exists a default image draw_image(prod->image); } // product->image will be destroyed if exists. dbind_destroy(&prod, Product); |
Si queramos registrar nuestros propios tipos binarios, deberemos proveer a DBind de funciones para copiar, serializar y destruir objetos de dicho tipo (Listado 11):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
typedef _mytype_t MyType; // Definition is hidden static MyType *mytype_copy(const MyType *obj) { // Return a copy of 'obj' } static MyType *mytype_read(Stream *stm) { // Read the object from stream data and return it } static void mytype_write(Stream *stm, const MyType *obj) { // Write the object data into the stream } static void mytype_destroy(MyType **obj) { // Destroy the object here } // Register 'MyType' objects in DBind dbind_binary(MyType, mytype_copy, mytype_read, mytype_write, mytype_destroy); // Now we can use 'MyType' objects with DBind typedef struct _product_t { ... MyType *mytype; } Product; dbind(Product, MyType*, mytype); Product *prod = dbind_create(Product); if (prod->mytype != NULL) { // Exists a default 'MyType' object } // 'prod->mytype' will be destroyed if non-NULL. dbind_destroy(&prod, Product); |
2.7. Uso de arrays
Los contenedores tipo ArrSt
y ArrPt
también son reconocidos por DBind y, por tanto, pueden formar parte de los campos en una estructura registrada (Listado 12).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
typedef struct _product_t { ... ArrPt(Image) *images; ArrSt(Stock) *stocks; } Product; dbind(Product, ArrPt(Image)*, images); dbind(Product, ArrSt(Stock)*, stocks); // Create an object with inner arrays Product *prod = dbind_create(Product); // Create an array of registered objects ArrSt(Product) *products = dbind_create(ArrSt(Product)); // Will destroy 'images' and 'stocks' arrays and its elements. dbind_destroy(&prod, Product); // Will destroy 'products' array and its elements. dbind_destroy(&products, ArrSt(Product)); |
Un dato importante, que no debemos pasar por alto, es que los contenedores de tipo ArrSt
solo pueden ser utilizados para tipos "abiertos", donde es conocida su definición y, por tanto, la memoria que necesita reservar el contenedor para cada elemento. Para tipos binarios u opacos (String
, Image
, MyType
, etc) deberemos utilizar contenedores ArrPt
que contienen punteros a objetos.
2.8. Valores por defecto
- Utiliza dbind_default para establecer los valores por defecto de los campos de un objeto.
Hemos comentado anteriormente que, cuando creamos un objeto registrado, sus campos se inicializan con los valores por defecto, que mostramos en defaultval
.
Tipo | Valor |
Booleanos | FALSE |
Enteros | 0 |
Reales | 0.0 |
Enumerados | El valor mínimo (no tiene porqué ser 0). |
String | Cadena vacía "" , (no NULL ). |
Objetos | Valores por defecto de cada campo. |
Objetos (punteros) | Reserva de memoria y valores por defecto de cada campo. |
Binarios | NULL |
Contenedores | Se crea contenedor con 0 elementos. |
Es posible cambiar estos valores para cada campo de un objeto (Listado 13). Además de valores por defecto para tipos básicos, podemos establecer "objetos anidados por defecto" o "contenedores por defecto" para cada nueva instancia que se cree o inicialice con DBind.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// Defaults of basic types dbind_default(Product, type_t, type, ekHDD); dbind_default(Product, real32_t, price, 100.0f); // Defaults of strings // NULL is allowed dbind_default(Product, String*, desc, "Empty-desc"); dbind_default(Product, String*, desc, NULL); // Defaults of binaries // NULL is allowed Image *empty_icon = get_image("empty"); dbind_default(Product, Image*, image, empty_icon); dbind_default(Product, Image*, image, NULL); dbind_destroy(&empty_icon, Image); // Defaults of static nested objects // NULL is NOT allowed Stock *defstock = get_default_stock(); dbind_default(Product, Stock, stock1, defstock); dbind_destroy(&defstock, Stock); // Defaults of dynamic nested objects // NULL is allowed dbind_default(Product, Stock, stock2, defstock); dbind_default(Product, Stock, stock2, NULL); // Defaults of containers // NULL is allowed ArrSt(Stock) *defstocks = get_3_locations_stocks(); dbind_default(Product, ArrSt(Stock)*, stocks, defstocks); dbind_destroy(&defstocks, ArrSt(Stock)); |
2.9. Rangos numéricos
- Utiliza dbind_range para establecer un máximo y mínimo en valores numéricos.
- Utiliza dbind_precision para establecer la precisión en valores reales.
- Utiliza dbind_increment para establecer el valor de incrementos discretos.
- Utiliza dbind_suffix para establecer un sufijo que será añadido al convertir los números a texto.
Para concluir con las opciones de inicialización, DBind nos permite filtrar y acotar automáticamente los valores relacionados con campos numéricos uint32_t
, int8_t
, real64_t
, etc (Listado 14). Internamente se encargará de validar los datos cada vez que se lean valores desde cualquier origen de datos (GUI, JSON, Streams, etc).
price
.
1 2 3 4 |
dbind_range(Product, real32_t, price, .50f, 10000f); dbind_precision(Product, real32_t, price, .01f); dbind_increment(Product, real32_t, price, 5.f); dbind_suffix(Product, real32_t, price, "€"); |
3. Comparación de objetos con DBind
- Utiliza dbind_cmp para comparar dos objetos.
- Utiliza dbind_equ para comprobar si dos objetos son iguales.
Realizar una comparación "profunda" de objetos puede tener asociada gran cantidad de trabajo, sobre todo en objetos grandes con anidaciones o contenedores. DBind proporciona esta función para cualquier tipo registrado (Listado 15). Ver Comparadores y claves.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
static int i_cmp(const Product *pr1, const Product *pr2) { return dbind_cmp(pr1, pr2, Product); } ArrPt(Product) *products = create_products(); ... arrpt_sort(product, i_cmp, Product); ... const Product *pr1 = get_product1(); const Product *pr2 = get_product1(); if (dbind_equ(pr1, pr2, Product) == TRUE) { // 'pr1' and 'pr2' are equals } |
La relación de orden establecida por dbind_cmp()
es de menor a mayor, lo que se traduce en:
- Para tipos numéricos devolverá
-1
si el primer elemento es menor,1
si el primer elemento es mayor y0
si son iguales. - Para cadenas de texto, realizará una comparación alfabética carácter a carácter, devolviendo
-1
,1
al encontrar la primera discrepancia, o0
si ambas cadenas son totalmente iguales. - Para arrays, primero comparará el número de elementos de cada contenedor, considerando "menor" el que menos elementos tenga. En caso de que este número coincida, se realizará una comparación elemento a elemento hasta encontrar el primer "no igual".
- Para objetos anidados, realizará una comparación recursiva campo a campo en el orden en que se declaran en el
struct
. Devolverá0
solo en el caso de que todos los campos sean iguales.
4. Serialización con DBind
- Utiliza dbind_read para leer objeto desde un stream.
- Utiliza dbind_write para escribir un objeto en un stream.
Otra gran ventaja de ofrece DBind en la serialización automática de objetos registrados, al conocer la composición detallada de cada tipo de dato. Por lo tanto, es posible acceder a los canales de E/S sin que sea necesario programar explícitamente funciones de escritura y lectura, como hacíamos en Serialización de arrays (Listado 16) (Figura 3).
1 2 3 |
ArrPt(Product) *products = dbind_read(stream, ArrPt(Product)); ... dbind_write(stream, products, ArrPt(Product)); |
5. Importar y exportar a JSON
DBind ofrece un API privado para que módulos externos accedan a la información del registro y aprovechen toda la potencia que ofrece la vinculación de datos. Uno de estos módulos es JSON (Figura 4) que permite exportar (Listado 17) e importar (Listado 18) objetos de tipos registrados de forma automática sin ningún esfuerzo adicional. En (Listado 19) vemos un fragmento del archivo JSON generado.
1 2 3 4 |
ArrSt(Product) *products = dbind_create(ArrSt(Product)); ... Stream *stream = stm_to_file("data.json", NULL); json_write(stream, products, NULL, ArrSt(Product)); |
1 2 3 4 5 |
ArrSt(Product)
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ [ { "code":"i7-8700K", "desc":"Intel BX80684I78700K 8th Gen Core i7-8700K Processor", "type":0, "price":374.8899999999999863575794734060764312744140625, "image":"\/9j\/4AAQSkZJRgABAQ.... }, { "code":"G3900", ... } ... } |
6. Sincronización con interfaces gráficas
Y, por último, el uso principal que tradicionalmente se le ha dado a la vinculación de datos: La posibilidad de sincronizar la interfaz gráfica con los objetos que forman el modelo de datos. Este paradigma es conocido como MVVM (Model-View-ViewModel) (Figura 5) y utiliza los tipos Layout y Cell para asociar instancias y campos de struct
respectivamente. Más información en GUI Data binding.
dbind ()
Añade un campo de una estructura a su tabla interna dentro de DBind.
dbindst_t dbind(type, mtype, name);
type | Tipo de la estructura. |
mtype | Tipo del campo a registrar. |
name | Nombre del campo dentro de la estructura. |
Retorna
Resultado del registro.
dbind_enum ()
Registra un valor tipo enum
.
dbindst_t dbind_enum(type, value, const char_t *alias);
type | Tipo del enum. |
value | Valor. |
alias | Alias para el valor. |
Retorna
Resultado del registro.
Observaciones
dbind_enum(mode_t, ekIMAGE_ANALISYS, "Image Analisys")
utilizará la cadena "Image Analisys" en lugar de "ekIMAGE_ANALISYS" para aquellas operaciones E/S o de interfaz que requieran mostrar los literales del enumerado. Por ejemplo, para rellenar los campos de un PopUp vinculado con un campo de datos.
dbind_binary ()
Registra un tipo binario (opaco).
dbindst_t dbind_binary(type, FPtr_copy func_copy, FPtr_read func_read, FPtr_write func_write, FPtr_destroy func_destroy);
type | Tipo del objeto. |
func_copy | Función de copia. |
func_read | Función de lectura. |
func_write | Función de escritura. |
func_destroy | Función de destrucción. |
Retorna
Resultado del registro.
Observaciones
Ver Objetos binarios.
dbind_alias ()
Registra un alias para un tipo de datos (typedef
).
dbindst_t dbind_alias(type, alias);
type | Tipo del objeto. |
alias | Nombre del alias. |
Retorna
Resultado del registro.
Observaciones
Ver Alias de tipos.
dbind_unreg ()
Elimina un tipo de datos del registro DBind.
dbindst_t dbind_unreg(type);
type | Tipo del objeto. |
Retorna
Resultado de la eliminación.
dbind_create ()
Crea un objeto de tipo registrado, inicializando sus campos con los valores por defecto.
type* dbind_create(type);
type | Tipo de objeto. |
Retorna
Objeto recién creado o NULL
si DBind no reconoce el tipo de dato.
Observaciones
Ver Creación de objetos.
dbind_copy ()
Copia un objeto de tipo registrado.
type* dbind_copy(const type *obj, type);
obj | Objeto a copiar. |
type | Tipo de objeto. |
Retorna
Copia del objeto o NULL
si DBind no reconoce el tipo de dato.
Observaciones
Ver Copia de objetos.
dbind_init ()
Inicializa los campos de un objeto de tipo registrado con los valores por defecto.
void dbind_init(type *obj, type);
obj | Objeto cuya memoria ha sido reservada, pero no inicializada. |
type | Tipo de objeto. |
Observaciones
Ver Inicialización de objetos.
dbind_remove ()
Libera la memoria reservada por los campos de un objeto de tipo registrado, pero no destruye el objeto en sí.
void dbind_remove(type *obj, type);
obj | Objeto. |
type | Tipo de objeto. |
Observaciones
Ver Inicialización de objetos.
dbind_destroy ()
Destruye un objeto de tipo registrado. La memoria asignada a los campos y sub-objetos también será liberada de forma recursiva.
void dbind_destroy(type **obj, type);
obj | Objeto. Será puesto a |
type | Tipo de objeto. |
Observaciones
Ver Creación de objetos.
dbind_destopt ()
Destructor opcional. Igual que dbind_destroy, pero aceptando que el objeto sea NULL
.
void dbind_destopt(type **obj, type);
obj | Objeto. |
type | Tipo de objeto. |
dbind_cmp ()
Compara dos objetos de tipo registrado.
int dbind_cmp(const type *obj1, const type *obj2, type);
obj1 | Primer objeto a comparar. |
obj2 | Segundo objeto a comparar. |
type | Tipo de objeto. |
Retorna
-1
, 1
o 0
si obj1
es menor, mayor o igual que obj2
.
Observaciones
Ver Comparación de objetos con DBind.
dbind_equ ()
Comprueba si dos objetos de tipo registrado son iguales.
bool_t dbind_equ(const type *obj1, const type *obj2, type);
obj1 | Primer objeto a comparar. |
obj2 | Segundo objeto a comparar. |
type | Tipo de objeto. |
Retorna
TRUE
si son iguales.
Observaciones
Ver Comparación de objetos con DBind.
dbind_read ()
Crea un objeto de tipo registrado a partir de los datos leídos desde un stream.
type* dbind_read(Stream *stm, type);
stm | Stream de lectura. |
type | Tipo del objeto a leer. |
Retorna
Objeto recién creado o NULL
si ha habido algún error.
Observaciones
dbind_write ()
Escribe el contenido de un objeto de tipo registrado en un stream de escritura.
void dbind_write(Stream *stm, const type *obj, type);
stm | Stream de escritura. |
obj | Objeto a escribir. |
type | Tipo del objeto a escribir. |
Observaciones
dbind_default ()
Establece el valor por defecto de un campo.
void dbind_default(type, mtype, name, mtype value);
type | Tipo de la estructura. |
mtype | Tipo del campo. |
name | Nombre del campo dentro de la estructura. |
value | Valor por defecto a partir de ahora. |
Observaciones
Ver Valores por defecto.
dbind_range ()
Establece el valor máximo y mínimo en campos numéricos.
void dbind_range(type, mtype, name, mtype min, mtype max);
type | Tipo de la estructura. |
mtype | Tipo del campo. |
name | Nombre del campo dentro de la estructura. |
min | Valor mínimo. |
max | Valor máximo. |
Observaciones
Ver Rangos numéricos.
dbind_precision ()
Establece el salto entre dos valores reales consecutivos.
void dbind_precision(type, mtype, name, mtype prec);
type | Tipo de la estructura. |
mtype | Tipo del campo. |
name | Nombre del campo dentro de la estructura. |
prec | Precisión (p.e. |
Observaciones
Ver Rangos numéricos.
dbind_increment ()
Establece el incremento de un valor numérico, por ejemplo, al pulsar en un control UpDown.
void dbind_increment(type, mtype, name, mtype incr);
type | Tipo de la estructura. |
mtype | Tipo del campo. |
name | Nombre del campo dentro de la estructura. |
incr | Incremento. |
Observaciones
Ver Rangos numéricos.
dbind_suffix ()
Establece un sufijo que será añadido al valor numérico al convertirlo a texto.
void dbind_suffix(type, mtype, name, const char_t *suffix);
type | Tipo de la estructura. |
mtype | Tipo del campo. |
name | Nombre del campo dentro de la estructura. |
suffix | Sufijo. |
Observaciones
Ver Rangos numéricos.