Search code examples
qtdata-bindingqml2-way-object-databinding

Change property value without destroying binding to support two-way binding


I want to create a custom component, which allows two-way binding with the following (traditional) setup:

// main.qml
property var someStoredValue: someInitialValue // may be C++ property in real application
MyCustomComponent
{
    myProperty: someStoredValue // this binding will be destroyed on user input
    onMyPropertyChanged: someStoredValue = myProperty
}

// MyCustomComponent.qml
Item
{
    property var myProperty: null
    MouseArea
    {
        anchors.fill: parent
        onClicked: myProperty = "some text or other value" // breaks the binding set in main.qml
    }
}

MyCustomComponent should be able to update myProperty (programmatically), without destroying the binding between someStoredValue and myProperty. How can I change the implementation of MyCustomComponent to achieve my goal?


Solution

  • One solution would be to use QObject::setProperty to update the myProperty without breaking the binding.

    First, we should expose the QObject::setProperty to QML, f.ex. using a singleton object:

    #include <QObject>
    
    class QmlHelper : public QObject
    {
      Q_OBJECT
    public:
      Q_INVOKABLE void setValue (QObject *pObject, const QString& name, QVariant v)
      {
        QByteArray n = name.toUtf8();
        if (v.isNull())
          v = QVariant(pObject->property(n.data()).userType(), nullptr); // ensure v is of correct type
        pObject->setProperty(n.data(), v);
      }
    };
    

    You should register the singleton type:

    qmlRegisterSingletonType<QmlHelper>("MyUri", 1, 0, "QmlHelper", [](QQmlEngine *, QJSEngine *) -> QObject* {return new QmlHelper();});
    

    MyCustomComponent could now be implemented as:

    Item
    {
        id: myComponent
        property var myProperty: null
        MouseArea
        {
            anchors.fill: parent
            onClicked: QmlHelper.setValue(myComponent, "myProperty", "some text or other value"); // updates myProperty without breaking the binding
        }
    }
    

    DISCLAIMER: Changing QML properties from C++ is a rather tricky solution and not well documented. Use at your own risk.