'How can I give a Qt QML item a property that doesn't notify when it changes?

I (very) occasionally find myself needing to add a property to an item that isn't bindable in other expressions, that is to say that when its value changes, it doesn't notify anything that uses its value and thus doesn't cause expressions to be re-evaluated. The point of this is to have essentially what is a pure state variable that can't get itself involved in binding loops.

As a concrete example:

Item
{
    property string state: ""

    visible:
    {
        if(some_condition)
        {
            state = "foo";
            return true;
        }

        if(state === some_other_external_state)
            return true;

        state = "";
        return false;
    }
}

In this case where some_condition becomes true the visible property sets state and a binding loop occurs since visible depends on the value of state. If the state property didn't bind and was, as its name implies, purely a state variable, this loop is avoided.



Solution 1:[1]

One hacky solution that I've used before is:

Item
{
    property var state: [""]

    visible:
    {
        if(some_condition)
        {
            state[0] = "foo";
            return true;
        }

        if(state[0] === some_other_external_state)
            return true;

        state[0] = "";
        return false;
    }
}

This works because array properties don't notify when the contents of the array change, they only notify when the array itself changes. It's not pretty, but it works.

Solution 2:[2]

You cannot create non-bindable property in QML. But you can control what bindings will be created.

By design property bindings should not change values of other properties. This is the source of problem in code sample you provided. There is not enough details to provide full solution but consider this pseudo code:

Item {
   // Item has state property already - you should not redefine it
   // property string state: ""

   visible: state !== "" // or whatever else is correct, but keep it simple
   // Optionally:
   onVisibleChanged: {
      // Any extra operations that should be done
      // when item is being shown or hidden
      // But you should not change state variable here!
   }
   
   // Monitor all events that should change state and keep it up to date
   // Usually Qt will already have a signal notifying about particular change 
   // or you can define signals you need and emit in proper places 
   Connections {
      target: other_object
      onSignalForSomeSpecificEvent: state = "bar"
   }   

   // Some conditions you can define in declarative form
   // Suitable for condition depending on other properties
   property bool someCondition 
   onSomeConditionChaged: state = 'foo'
}

A lot depends on particular design but 2 rules of thumb:

  • bindings should not alter other properties
  • use signal handlers to avoid unwanted bindings

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tim Angus
Solution 2 Łukasz Korbel