BB10 Cascades: Populate a DropDown from an XML File
A question was asked about how to populate a Cascades DropDown control from an XML File. Since I've populated DropDowns from SQLite data I decided to try my hand at pulling from a different datasource. The question was spawned from a discussion about creating a custom control.
A Cascades sample project is available for download at the bottom of the page.
Note: I don't have a good code highlighting plugin so the inline code samples don't look that great.
References
- QXmlStreamReader to parse XML in Qt [developer.nokia.com]
- Casting a list as QVariant or QVariant List [stackoverflow.com]
- Why can't I parse a XML file using QXmlStreamReader from Qt? [stackoverflow.com]
- How to Parse an XML file Using C++ or Cascades [supportforums.blackberry.com]
- QXmlStreamReader Class Reference [doc.qt.digia.com]
Overview
To populate a DropDown from an XML File you will need to do the following things:
- Create a C++ Class that can read
- Make an instance of the class and bind it so it can be accessed from QML Pages
- Create a QML Page that has
- DropDown control with an id
- attachedObjects with a definition for Option{}
- Event Binding to load the DropDown control
C++ XML Reader Class
The first thing you need to do is create a class that uses QT's QXmlStreamReader class to read your desired XML data into a QList. There is plenty of documentation available for the QXmlStreamReader, but I'd recommend taking a look at this comprehensive sample application.
I ended up with a class file that looks like this:
const QString XmlBasePath = QDir::currentPath() + "/app/native/assets/XML/"; // Base path to the XML folder in your app QVariantList XmlReader::LoadXML(QString xmlPath, QString rowType, QString attribute){ // Setup the full path to the XML file // And open a QFile for use by the XmlStreamReader QString XmlPath = XmlBasePath + xmlPath; QFile* XFile = new QFile(XmlPath); XFile->open(QIODevice::ReadOnly | QIODevice::Text); // Initialize the XML reader with the XML file QXmlStreamReader Xml(XFile); // // This is where the magic happens: Read the Attributes from each entry into a QList QList< QVariantMap > Entries; while(!Xml.atEnd()){ QXmlStreamReader::TokenType Token = Xml.readNext(); if(Token == QXmlStreamReader::StartElement){ if(Xml.name() == rowType) { // If this is an xml element that is named correctly, try to read the attributes QXmlStreamAttributes attributes = Xml.attributes(); QVariantMap Entry; Entry[attribute] = attributes.value(attribute).toString(); // pull off the specified attribute Entries.append(Entry); } } } // Cleanup file handle XFile->close(); // Debug view. Look at slog2info to see what this 'looks' like qDebug() << "stuff coming" << Entries; // Convert to QVariantList for transfer to QML QVariantList QVList; for(int i = 0; i < Entries.length(); i++){ QVariantMap map; QVList << Entries[i]; } return QVList; }
Configure Cascades to let QML use the Class
Once you have your class, you'll need to instantiate and bind an instance of it for use by QML. That is pretty straight-forward and only involves adding a few lines to your applicationName.cpp file. In this case the sample application that I used to create this is named DropDownFromXmlData. Lines to add:
- #include "XmlReader.h" // This is the name of my XML reader class (top of file)
- XmlReader *xml = new XmlReader(); // Create and instance and bind to QML
- qml->setContextProperty("XML", xml); // Add this after your QmlDocument declaration
Complete code listing of DropDownFromXmlData.app. The relevant lines have been Bolded:
#include "DropDownFromXmlData.hpp" #include "XmlReader.h" #include #include #include using namespace bb::cascades; DropDownFromXmlData::DropDownFromXmlData(bb::cascades::Application *app) : QObject(app) { // create scene document from main.qml asset // set parent to created document to ensure it exists for the whole application lifetime QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this); // Make an instance of the XML Reader class so it is available to QML pages
XmlReader *xml = new XmlReader(); qml->setContextProperty("XML", xml); // create root object for the UI AbstractPane *root = qml->createRootObject(); // set created root object as a scene app->setScene(root); }
Create a QML page that can use the Class
Now for the fun part: Creating a QML page that calls the XMLReader and populates the dropdown. Here is the main.qml file from this sample project. The relevant lines have been Bolded:
import bb.cascades 1.0 Page { onCreationCompleted: { // Call the C++ Method to load the XML data. 3 arguments: XML File, Row name, attribute name var xmlContents = XML.LoadXML("Information.xml", "object", "description"); // Clear the contents of the DropDown exampleDropDown.removeAll(); // Add the options from the XML to the DropDown for(var x = 0; x < xmlContents.length; x++){
console.log("entry: " + x);
console.log(xmlContents[x].description);
var opt = option.createObject(); //
opt.text = xmlContents[x].description; // Needs to be an 'Option' before the DropDown will accept it
exampleDropDown.add(opt);
} } Container { layout: StackLayout {} Label { text: qsTr("Hello XML!") textStyle.base: SystemDefaults.TextStyles.BigText horizontalAlignment: HorizontalAlignment.Center } Divider { } DropDown { // id: exampleDropDown // DropDown that we'll add options to } // } //
// BBTEN-302: I need to specify a new ComponentDefinition in the attachedObjects section
// In order to add options to a DropDown
// https://7thzero.com/blog/blackberry-10-cascades-create-a-custom-control-dropdownlist/
// attachedObjects: [
ComponentDefinition {
id: option Option{}
}
] }
Sample code download
Since I only show the highlights above, I've created a working Cascades application that implements what I've discussed above and made it available for download here. This was built with the BB10 Gold SDK and works on my Dev Alpha device. Should also work on the simulator
Enjoy!