GridViewを内部的にホストする再利用可能なQMLコンポーネントを構築しようとしています。 GridViewの各要素には、アプリケーション全体で共通の一連の動作(主に表示およびマウスベースのもの)があります。ただし、GridView要素内に表示される内容は、ユースケースによって異なります。 (つまり、1つのGridViewに対して同じカプセル化されたコンポーネントですが、アプリケーションの他の場所では異なるコンポーネントを使用する場合があります。)
したがって、私がやりたいのは、各呼び出しに、すでにデリゲートであるGridViewの各要素に追加されるデリゲートを提供させることです。言い換えれば、次のようなものです。
MyGrid.qml
import QtQuick 1.1
Rectangle {
id: myGrid
objectName: "myGrid"
property Component internalDelegate
property variant internalModel
GridView {
anchors.fill: parent
delegate: Row {
Loader {
sourceComponent: myGrid.internalDelegate
}
}
model: parent.internalModel
}
}
アイデアは、GridViewデリゲート内のローダーがユーザー提供のデリゲートをロードするというものです。これは次のようになります。
Main.qml
import QtQuick 1.1
Rectangle {
anchors.fill: parent
width: 300
height: 200
MyGrid {
anchors.fill: parent
internalDelegate: Text {
text: name
}
internalModel: ListModel {
ListElement {
name: "a"
}
ListElement {
name: "b"
}
}
}
}
ただし、これは機能しません。 QMLは、「name」がText要素内の不明な変数であると報告します。 name変数を文字列(つまり「hello」)に置き換えると、期待どおりに機能します。
私の質問は、「name」変数をinternalDelegateに渡す方法、またはさらに良いことに、internalDelegateがそれらすべてにアクセスできるようにモデル全体を使用可能にする方法です(呼び出し元もモデルを定義しているため)。
副次的な質問は次のとおりです。これを行うためのより良い方法はありますか?
私はそれを次のように解決しました:
MyGrid.qml
import QtQuick 1.1
Rectangle {
id: myGrid
objectName: "myGrid"
property Component internalDelegate
property variant internalModel
GridView {
id: grid
anchors.fill: parent
delegate: Row {
Loader {
sourceComponent: myGrid.internalDelegate
property variant modelData: grid.model.get(index)
}
}
model: parent.internalModel
}
}
main.qml
import QtQuick 1.1
Rectangle {
anchors.fill: parent
width: 300
height: 200
MyGrid {
anchors.fill: parent
internalDelegate: Text {
text: modelData.name
}
internalModel: ListModel {
ListElement {
name: "a"
}
ListElement {
name: "b"
}
}
}
}
それが「より良い」かどうかはわかりませんが、コードは少なくて単純です。リストモデルは少し扱いにくく、QMLでは実際には一貫性がありません。また、モデル全体がGridViewによって所有されているため、model.get(index)はGridViewによって所有されていることに注意してください。したがって、リストが破棄された後にそのオブジェクトを使用する場合は、親を変更するか、コピーする必要があります。
ここでその「grid.model.get(model.index)」を指摘したいだけです:
delegate: Row {
Loader {
sourceComponent: myGrid.internalDelegate
property QtObject modelData: grid.model.get(index) //<
}
}
現在のコンテキストでは「モデル」と同等です。
delegate: Row {
Loader {
sourceComponent: myGrid.internalDelegate
property QtObject modelData: model //<
}
}
私は問題に取り組み続け、許容できる解決策を見つけたので、私は自分の質問に答えています。より簡単な解決策やより効率的な解決策があるかどうかを知りたいので、この質問を(まだ)回答済みとしてマークしていません。
この問題を解決した方法は次のとおりです。ローダーでBindingコンポーネントを使用し、モデルの現在のListElementのバリアントプロパティを持つカスタムMyDelegateコンポーネントを作成しました。これは、コメントでマークされた重要な変更を加えた変更されたコードです。
Main.qml
import QtQuick 1.1
Rectangle {
anchors.fill: parent
width: 300
height: 200
MyGrid {
anchors.fill: parent
// *** Use MyDelegate rather than generic Component and refer to
// *** model's properties via MyDelegate's model property
internalDelegate: MyDelegate {
Text {
text: model.index + model.name
}
}
internalModel: ListModel {
ListElement {
name: "a"
}
ListElement {
name: "b"
}
}
}
}
MyGrid.qml
import QtQuick 1.1
Rectangle {
id: myGrid
objectName: "myGrid"
property Component internalDelegate
property variant internalModel
GridView {
anchors.fill: parent
delegate: Row {
Loader {
id: loader
sourceComponent: myGrid.internalDelegate
// *** Bind current model element to the component
// *** when it's loaded
Binding {
target: loader.item
property: "model"
value: model
when: loader.status == Loader.Ready
}
}
}
model: parent.internalModel
}
}
重要な追加は、モデルプロパティをバリアントとして定義するカスタムMyDelegateコンポーネントです。これは解釈時にバインドされます。つまり、モデルのnameプロパティが参照されているときにMain.qmlにエラーはありません。
MyDelegate.qml
import QtQuick 1.1
Rectangle {
property variant model
}
これを行うにはさらに簡単な方法があるとのことですが、今のところこれは機能します。