web-dev-qa-db-ja.com

QML:GridView(またはListView)デリゲート内にロードされたデリゲートにモデルプロパティを渡すにはどうすればよいですか?

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がそれらすべてにアクセスできるようにモデル全体を使用可能にする方法です(呼び出し元もモデルを定義しているため)。

副次的な質問は次のとおりです。これを行うためのより良い方法はありますか?

17
Jim Nelson

私はそれを次のように解決しました:

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によって所有されていることに注意してください。したがって、リストが破棄された後にそのオブジェクトを使用する場合は、親を変更するか、コピーする必要があります。

16
blakharaz

ここでその「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 //< 
        }
    }
12
jichi

私は問題に取り組み続け、許容できる解決策を見つけたので、私は自分の質問に答えています。より簡単な解決策やより効率的な解決策があるかどうかを知りたいので、この質問を(まだ)回答済みとしてマークしていません。

この問題を解決した方法は次のとおりです。ローダーで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
}

これを行うにはさらに簡単な方法があるとのことですが、今のところこれは機能します。

4
Jim Nelson