zlgopen / awtk-mvvm

Model-View-ViewModel for AWTK
GNU Lesser General Public License v2.1
88 stars 27 forks source link

自定义控件的自定义属性的数据绑定不成功 #27

Closed ufbycd closed 3 years ago

ufbycd commented 3 years ago

自定义控件gesture_lock有个自定义属性password,要跟model gesture_login的属性password绑定时,发现gesture_lock发出EVT_VALUE_CHANGED事件后model的属性没有变化。

widget:

typedef struct _gesture_lock_t {
  widget_t widget;

  /**
   * @property {char*} password
   * @annotation ["set_prop", "get_prop","readable","persitent","design","scriptable"]
   * 输入的密码
   */
  char* password;
  ...
} gesture_lock_t;

model:

typedef struct _gesture_login_t {
  /**
   * @property {str_t} name
   * @annotation ["readable", "writable"]
   * 用户名。
   */
  str_t name;
  /**
   * @property {str_t} password
   * @annotation ["readable", "writable"]
   * 密码。
   */
  str_t password;
} gesture_login_t;

gesture_login.xml:

<window v-model="gesture_login" >
            ...
            <gesture_lock name="lock" h="80%" v-data:password="{password}" />
</window>

运行时的log输出:

gesture_lock_set_prop(self_layout)                                                    
gesture_lock_set_prop(name)                                                           
gesture_lock_set_prop(v-data:password)                                                
view_model_on_will_mount                                                              
gesture_lock_get_prop(type)                                                           
gesture_lock_get_prop(v-model)                                                        
view_model_on_mount                                                                   
gesture_lock_set_prop(password)                                                       
window system_bar open                                                                
gesture_lock_get_prop(state_for_style)                                                
gesture_lock_get_prop(type)                                                           
window gesture_login open                                                             
gesture_lock_get_prop(is_keyboard)                                                    
gesture_lock_get_prop(is_keyboard)                                                    
gesture_lock_get_prop(state_for_style)                                                
gesture_lock_get_prop(type)                                                           
gesture_lock: EVT_VALUE_CHANGED: 1243

自定义属性的绑定要怎么做?

xianjimli commented 3 years ago

你在微信群里吗,把完整的demo发个我看看吧

ufbycd commented 3 years ago

不在微信群,demo在这里:https://gitee.com/ufbycd/miniv2.git 基于awtk-mvvm-c-hello,并添加了awtk-widget-gesture-lock控件

xianjimli commented 3 years ago

好的,谢谢。明天我看看。

xianjimli commented 3 years ago

需要按下面的方式绑定:

<gesture_lock name="lock" h="80%" v-data:value="{password, Mode=OneWayToModel}" />

控件本身也有点问题,你可以等作者更新,或者先打上下面的补丁:

diff --git a/src/gesture_lock/gesture_lock.c b/src/gesture_lock/gesture_lock.c
index ba77b96..b15c189 100644
--- a/src/gesture_lock/gesture_lock.c
+++ b/src/gesture_lock/gesture_lock.c
@@ -30,7 +30,7 @@ static ret_t gesture_lock_get_prop(widget_t* widget, const char* name, value_t*
   ret_t ret = RET_NOT_FOUND;

   log_debug("%s(%s)\n", __func__, name);
-  if (tk_str_eq(name, GESTURE_LOCK_PROP_PASSWORD)) {
+  if (tk_str_eq(name, GESTURE_LOCK_PROP_PASSWORD) || tk_str_eq(name, WIDGET_PROP_VALUE)) {
     value_set_str(v, gesture_lock->password);
     ret = RET_OK;
   }else if (tk_str_eq(name, WIDGET_PROP_TYPE)) {
@@ -47,7 +47,7 @@ static ret_t gesture_lock_set_prop(widget_t* widget, const char* name, const val
   ret_t ret = RET_NOT_FOUND;

   log_debug("%s(%s)\n", __func__, name);
-  if (tk_str_eq(name, GESTURE_LOCK_PROP_PASSWORD)) {
+  if (tk_str_eq(name, GESTURE_LOCK_PROP_PASSWORD) || tk_str_eq(name, WIDGET_PROP_VALUE)) {
       const char *password = value_str(v);
       return_value_if_fail(password != NULL, RET_BAD_PARAMS);
       gesture_lock->password = tk_str_copy(gesture_lock->password, password);
@@ -258,7 +258,7 @@ static ret_t gesture_lock_on_pointer_up(widget_t* widget, pointer_event_t* evt)
     value_change_event_init(&event, EVT_VALUE_CHANGED, widget);
     value_set_str(&event.old_value, gesture_lock->password);
     value_set_str(&event.new_value, gesture_lock->input_password.str);
-    widget_dispatch(widget, (event_t*)&evt);
+    widget_dispatch(widget, (event_t*)&event);
     gesture_lock->password = gesture_lock->input_password.str;

     log_debug("gesture_lock: EVT_VALUE_CHANGED: %s\n", gesture_lock->password);
ufbycd commented 3 years ago

按上面修改后数据绑定成功了。 但在程序改进时又遇到一个问题:我想在EVT_VALUE_CHANGED事件发生后就立刻执行 auth于是控件UI改成这样:

<gesture_lock name="lock" h="80%" v-data:value="{password, Mode=OneWayToModel}" v-on:value_changed="{auth}" />

然而此时数据绑定就又没有成功了,似乎value_changed的命令绑定会跟其数据绑定冲突,两者不能同时绑定。 怎么解决这个问题?

ufbycd commented 3 years ago

我找到一种解决办法: 数据绑定在Trigger=Changing之上:

<gesture_lock name="lock" h="80%" v-data:value="{password, Mode=OneWayToModel, Trigger=Changing}" v-on:value_changed="{auth}" />

控件先发EVT_VALUE_CHANGING事件,再发EVT_VALUE_CHANGED事件:

    value_change_event_init(&event, EVT_VALUE_CHANGING, widget);
    gesture_lock->password = gesture_lock->input_password.str;
    value_set_str(&event.old_value, gesture_lock->password);
    value_set_str(&event.new_value, gesture_lock->password);
    widget_dispatch(widget, (event_t*)&event);

    event.e.type = EVT_VALUE_CHANGED;
    widget_dispatch(widget, (event_t*)&event);

问题虽然解决了,但感觉绑定value_changed有些坑,解决办法也有点拙劣。能否优化下?

ufbycd commented 3 years ago

看了下awtk-mvvm的代码后,找到更好的实现方式了: 将数据绑定到password属性名之上:

<gesture_lock v-data:password="{password, Mode=OneWayToModel}" v-on:value_changed="{auth}" />

控制先发EVT_PROP_CHANGED来更新model里的属性,再发EVT_VALUE_CHANGED事件来触发绑定的命令:

    value_change_event_t vce;
    prop_change_event_t pce;
    value_t value;

    gesture_lock->password = gesture_lock->input_password.str;
    value_set_str(&value, gesture_lock->password);
    prop_change_event_init(&pce, EVT_PROP_CHANGED, GESTURE_LOCK_PROP_PASSWORD, &value);
    widget_dispatch(widget, (event_t*)&pce);

    value_change_event_init(&vce, EVT_VALUE_CHANGED, widget);
    value_set_str(&vce.old_value, gesture_lock->password);
    value_set_str(&vce.new_value, gesture_lock->password);
    widget_dispatch(widget, (event_t*)&vce);