6

I'm creating a custom QML component (a specialization of ListView that allows multiple selection). I'd like to provide attached properties to objects provided to my component. I see how to create attached properties using C++. However, I cannot find information on adding custom properties in pure QML. Is this possible using QML?

Phrogz
  • 296,393
  • 112
  • 651
  • 745

2 Answers2

11

Is this possible using QML?

No.

Mitch
  • 23,716
  • 9
  • 83
  • 122
  • Thanks for the authoritative answer. – Phrogz Sep 02 '16 at 12:40
  • 3
    Oh, and it might be nice to mention this in documentation somewhere in [this section](http://doc.qt.io/qt-5/qtqml-syntax-objectattributes.html#attached-properties-and-attached-signal-handlers). – Phrogz Sep 02 '16 at 12:42
6

There is an alternative, easy and clean way to this in QML - just use an adapter object that implements the desired properties. Then instead of attaching just nest into the adapter - use it as as a parent / container. You can also nest objects into the adapter, getting another C++ exclusive - grouped properties. A possible way to minimize the overhead of this is to use JS objects and properties, with a downside - no change notifications, which you can somewhat mitigate by emitting manually.

An example:

// Adapter.qml - interface with attached properties
Item {
  id: adapter
  property int customInt : Math.random() * 1000
  property var group : {"a" : Math.random(), "b" : Math.random() }
  default property Component delegate
  width: childrenRect.width
  height: childrenRect.height
  Component.onCompleted: delegate.createObject(adapter)
}

// usage
ListView {
  width: 100
  height: 300
  model: 5
  delegate: Adapter {
    Row {
      spacing: 10
      Text { text: index }
      Text { text: customInt }
      Text { text: group.a }
      Text { text: group.a }
    }
  }
}

It is fairly painless and convenient compared to some other QML workarounds. You don't even have to do parent.parent.customInt - the properties are directly accessible as if they are attached, this works because of dynamic scoping. The default property allows to avoid setting the inner delegate as a property you just nest the delegate you want directly in the adapter.

In many cases those acrobatics are overkill, you can just wrap in place:

ListView {
  width: 100
  height: 300
  model: 5
  delegate: Item {
    width: childrenRect.width
    height: childrenRect.height
    property string custom1: "another"
    property string custom2: "set of"
    property string custom3: "properties"
    Row {
      spacing: 10
      Text { text: index }
      Text { text: custom1 }
      Text { text: custom2 }
      Text { text: custom3 }
    }
  }
}

The only key part really is the binding for the size of the adapter object so that the view can properly layout the objects. I routinely use a Wrap element which essentially does the same but is implemented in C++, which is much more efficient than a QML binding.

dtech
  • 47,916
  • 17
  • 112
  • 190