模型绑定
这篇讲解,如何使用模型绑定 MVVM 功能。工具端目前仅简单实现模型的双向绑定。对于表达式、规则等高级功能,后续会逐步完善。
- 1. 支持的消息类型
- 2. 创建模型
- 3. 模型绑定
- 4. 仿真
- 5. 模型调试
- 6. 模型绑定实现原理
- 7. 实现系统时间绑定更新
- 8. 实现图片绑定更新
- 9. 实现控件状态绑定更新
- 10. 实现控件的隐藏显示
- 11. 手动更新消息
- 12. 控件绑定模型顺序
支持的消息类型
- int32_t
- lv_state_t
- char *
- char **
- lv_calendar_date_t
- lv_color_t
- bool
- int32_t *
- lv_point_t
- lv_point_t *
- lv_coord_t
- lv_coord_t *
- tm
- res image id
创建模型
定义一个名字叫“model”的模型,并有一个int32_t value和一个char * str的属性。

模型绑定
数字绑定
创建一个slider和一个textprogress控件,将控件的初始值都绑定到消息model 属性value


文本绑定
新建一个edit文本输入控件,一个qrcode二维码控件。将控件都绑定到消息model属性str上。

仿真
编译、仿真后,可以拖动slider来改变textprogress控件的值。修改edit的值,旁边的二维码会跟着变。

模型调试
- 启用模型调试框

- 启用工具右下角的
调试器

- 重新仿真、在模型调试框中查看、修改消息属性

模型绑定实现原理
每创建一个消息模型,都会生成一个消息ID和一个消息动作函数,当控件绑定到该消息模型上时,会为控件注册该消息ID的消息事件回调函数,在回调函数中,会将控件的值和模型的值利用消息动作函数来进行双向绑定。
在上面的例子中,可以看到生成的文件和代码如下,会创建gui_msg.h和gui_msg.c文件,以及模型gui_model_msg.h和gui_model_msg.c文件。

工具自动生成的消息动作函数是一个弱函数,当用户没有实现该函数时,会使用默认的消息动作函数,该函数只在仿真时有效,用户需要根据自己的需求来实现该函数。


比如value消息的数据来源是一个传感器的值,只能读取,其他动作不处理,那么可以在custom上这么实现:
void gui_model_msg_value_cb(gui_msg_action_t access, gui_msg_data_t * data)
{
if (access == GUI_MSG_ACCESS_GET) {
data->value_int = sensor_get_value();
}
}
实现系统时间绑定更新
- 创建一个消息,类型为tm,名字为time,启用定时更新,每秒更新一次。

- 创建一个时钟控件,将时钟控件的时间绑定到消息time上。

- 在custom中实现消息动作函数。
void gui_model_msg_time_cb(gui_msg_action_t access, gui_msg_data_t * data)
{
time_t now = time(NULL);
struct tm *t = localtime(&now);
if (data == NULL) {
data = &guider_msg_data;
}
memcpy(&data->value_time, t, sizeof(struct tm));
}
- 编译仿真后,可以看到时钟控件的时间会每秒更新一次。

实现图片绑定更新
- 在
编译资源管理
中,添加需要的图片资源。

- 创建一个消息,类型为res image id,名字为image,启用定时更新,每秒更新一次。

- 创建一个图片控件,将图片控件的图片绑定到消息image上。

- 在实际项目中,可能需要一个天气预报的图片,每隔一段时间更新一次,这里模拟一个图片更新的例子。
// 这里是刚才编译资源管理中添加的图片资源,已经在gui_res/res_common.h中定义了
// typedef enum {
// RES_WEATHER_001 = 0xD0000000,
// RES_WEATHER_002 = 0xD0000001,
// RES_WEATHER_003 = 0xD0000002,
// RES_WEATHER_004 = 0xD0000003,
// RES_WEATHER_005 = 0xD0000004,
// RES_WEATHER_006 = 0xD0000005,
// RES_WEATHER_007 = 0xD0000006,
// } RES_ID;
int get_weather_image_id()
{
static int index = 0;
index++;
if (index > 6) {
index = 0;
}
return RES_WEATHER_001 + index;
}
void gui_model_msg_image_cb(gui_msg_action_t access, gui_msg_data_t * data)
{
static int index = 0;
if (data == NULL) {
data = &guider_msg_data;
}
if (access == GUI_MSG_ACCESS_GET) {
data->value_int = get_weather_image_id();
}
}
- 编译仿真后,可以看到图片控件的图片会每秒更新一次。

实现控件状态绑定更新
- 添加一个消息,类型为lv_state_t,名字为music_state

- 添加一个图片按钮控件,将图片按钮控件的状态绑定到消息music_state上,同时设置图片按钮
释放后
和选中释放
的图片。

- 增加两个按钮,一个用于播放,一个用于暂停,为两个按钮添加Clicked事件,在
自定义代码
上实现发送消息的逻辑。
播放按钮:
gui_msg_send(GUI_MODEL_1_MSG_ID_MUSIC_STATE, (void *)LV_STATE_CHECKED, 1);
暂停按钮:
gui_msg_send(GUI_MODEL_1_MSG_ID_MUSIC_STATE, (void *)LV_STATE_DEFAULT, 1);

- 编译仿真后,可以看到图片按钮控件的状态会随着按钮的点击而改变,同时图片也会随着状态的改变而改变。

实现控件的隐藏显示
- 添加两个消息,类型为lv_obj_flag_t,名字为add_flag和clear_flag。

- 添加一个图片控件,将图片控件的
添加标识
和清除标识
绑定到消息add_flag和clear_flag上。

- 添加两个按钮,一个用于显示,一个用于隐藏,为两个按钮添加Clicked事件,在
自定义代码
上实现发送消息的逻辑。
显示按钮:
gui_msg_send(GUI_MODEL_1_MSG_ID_CLEAR_FLAG, (void *)LV_OBJ_FLAG_HIDDEN, 1);
隐藏按钮:
gui_msg_send(GUI_MODEL_1_MSG_ID_ADD_FLAG, (void *)LV_OBJ_FLAG_HIDDEN, 1);

- 编译仿真后,可以看到图片控件的显示状态会随着按钮的点击而改变。

手动更新消息
除了上面的定时更新外,还可以手动更新消息,比如在某个事件发生时,需要更新消息。
void on_button_click()
{
gui_msg_send(GUI_MODEL_MSG_ID_TIME, NULL, 0);
gui_msg_send(GUI_MODEL_MSG_ID_WEATHER_STR, "sunny", 0);
}
控件绑定模型顺序
某些情况下,控件模型绑定的代码生成顺序,会影响到控件的正常使用。比如:给 下拉框
控件的 选项
、 选中选项
绑定模型时,如果代码生成顺序是先绑定 选中选项
,再绑定 选项
,那么 选中选项
在控件设置 选项
之前就设置了,会导致 选中选项
无法正常工作。
在 2025.04.09
之后的 UI工具
和 代码生成工具
版本中,控件绑定模型顺序为用户给控件绑定模型的先后顺序。
