Model/View With BlackBerry 10 Cascades

Model/View With BlackBerry 10 Cascades

By ICS Development Team

In this post we'll look briefly at the Model View support from QML in BlackBerry 10's Cascades Framework, including an example program.

The Cascades framework lets you create UIs for the BlackBerry Application Platform with relative ease. This Qt based framework can be used to develop native applications for the BlackBerry 10 mobile platform. It supports development in C++ and/or QML.

Recently, I have been using Cascades for development of some mobile applications and I wanted to show an example of the support it offers for data management using Model/View from QML.

Providing a powerful construct for data management under the Cascades UI framework, ListView follows a Model/View architecture to provide a scrollable container with list items. Support for accessing data from external data sources, such as XML, JSON and SQL, is provided by the classes JsonDataAccess, XmlDataAccess and SqlDataAccess. These classes convert data into Qt C++ objects that can be added to a data model and displayed using the Cascades User Interface.

Let's look at an example of loading the data from the above data sources and displaying it using the ListView QML component of the Cascades UI.

Our example model data can be contained in a JSON or XML file or a database file as follows:

The file grocerylists.json, below, provides a list of name/value pairs that represent an object or record. For more detailed information about JSON and its structure, see www.json.org.

[
{"item":"Banana","grouping":"Fruits","image":"asset:///images/banana.png"},
{"item":"Orange","grouping":"Fruits","image":"asset:///images/orange.png"},
{"item":"Peach","grouping":"Fruits","image":"asset:///images/peach.png"},
...
{"item":"Red Wine","grouping":"Alcohol","image":"asset:///images/redwine.png"},
{"item":"White Chocolate","grouping":"Chocolate Factory","image":"asset:///images/whitechocolate.png"},
{"item":"Dark Chocolate","grouping":"Chocolate Factory","image":"asset:///images/darkchocolate.png"},
{"item":"Cookies","grouping":"Chocolate Factory","image":"asset:///images/cookie.png"}
]

File grocerylists.xml, below, provides a tree structure that starts at the root and has branches to provide the data storage in XML format. For information about XML and its structure, see http://www.w3.org/XML

<root>
    <header category="Fruits">
        <item icon="images/banana.png" title="Banana" />
        <item icon="images/orange.png" title="Orange" />
        <item icon="images/peach.png" title="Peach" />
        <item icon="images/pear.png" title="Pear" />
        <item icon="images/strawberries.png" title="Strawberries" />
    </header>
    <header category="Vegetables">
        <item icon="images/broccoli.png" title="Broccoli" />
        <item icon="images/yellowpepper.png" title="Yellow Pepper" />
...
        <item icon="images/whitewine.png" title="White Wine" />
        <item icon="images/redwine.png" title="Red Wine" />
    </header>
    <header category="Chocolate Factory">
        <item icon="images/whitechocolate.png" title="White Chocolate" />
        <item icon="images/darkchocolate.png" title="Dark Chocolate" />
        <item icon="images/cookie.png" title="Cookies" />
    </header>
</root>Query 

In addition to the above simple data storage, the Cascades framework can use an SQL database to store data. The code below creates a SQLite database called grocerycatalog.db and inserts sample grocery items into the database.

    // Create an instance of QSqlDataBase with the driver QSQLITE database
    QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE");

    // Define the path where database would be located
    QDir home = QDir::home();
    QString filePath = home.absoluteFilePath("grocerycatalog.db");

    // Delete file to recreate the database
    QFile::remove(filePath);
    QFile file(filePath);

    // Set the database path name for reading and writing data
    database.setDatabaseName(filePath);
    if (database.open()) {
        if (file.open(QIODevice::ReadWrite)) {
            SqlDataAccess sda(home.absoluteFilePath("grocerycatalog.db"));
            sda.execute("CREATE TABLE GroceryCatalog( item VARCHAR(50), category VARCHAR(100), image VARCHAR(100) );");
            sda.execute("INSERT INTO GroceryCatalog(item, category, image) VALUES(\"Banana\", \"Fruits\", \"asset:///images/banana.png\");");
            sda.execute("INSERT INTO GroceryCatalog(item, category, image) VALUES(\"Orange\", \"Fruits\", \"asset:///images/orange.png\");");
            sda.execute("INSERT INTO GroceryCatalog(item, category, image) VALUES(\"Peach\", \"Fruits\", \"asset:///images/peach.png\");");
            sda.execute("INSERT INTO GroceryCatalog(item, category, image) VALUES(\"Pear\", \"Fruits\", \"asset:///images/pear.png\");");
            ...

Having listed out the various data structure provided by Cascades, here is the QML code that presents the data in graphical view for the JSON data storage.

import bb.cascades 1.0
import bb.data 1.0

Page {
    content: Container {
        ListView {
            id: listView
            objectName: "listView"
            dataModel: MyListModel {
                id: myListModel
            }
            listItemComponents: [
                // Define delegates for different item types here
                ListItemComponent {
                    // StandardListItem is a convenience component for lists with default Cascades look and feel
                    StandardListItem {
                        title: ListItemData.text
                        description: ListItemData.description
                        imageSource: ListItemData.image
                    }
                }
            ]
            // More code as needed...
       }
    }
    onCreationCompleted: {
        // Populate list view model with the sample data
        myListModel.load("mydata.json")
    }
}

ListView visuals are managed by the listItemComponents property. It defines the delegates for different item types and ListItemComponents are used to provide the item visuals. Standard list items consist of sets of common properties to be displayed, such as an image, bold title text, description text and status text. Each of the properties is optional.

The data model myListModel will load/append the data using the JsonDataAccess class, which provides an array of QVariantList. The root element of the JSON data should be either an array or an object, so the corresponding Qt value types are QVariantList and QVariantMap.

The relevant code is shown below:

void MyListModel::load(const QString& file_name)
{
    bb::data::JsonDataAccess jda;
    QVariantList lst = jda.load(file_name).value();
    if (jda.hasError()) {
        bb::data::DataAccessError error = jda.error();
        qDebug() << file_name << "JSON loading error: " << error.errorType() << ": " << error.errorMessage();
    }
    else {
        qDebug() << file_name << "JSON data loaded okay.";
        append(lst);
    }
}

XmlDataModel reads the data from the XML file and creates the data model. The list item component's delegate presents the data in a grid view creating list items containing an image and the title. The Header item component is the category section of the various list items, where each element in the XML file is shown as an item in the ListView.

        Container {
       
            ListView {
                id: foodList
                objectName: "foodList"
            
                layout: GridListLayout {
                    columnCount: 2
                    headerMode: ListHeaderMode.Standard
                    cellAspectRatio: 1.1
                    spacingAfterHeader: 40
                    verticalCellSpacing: 10
                }
            
                dataModel: XmlDataModel {
                    source: "models/fooditem.xml"
                }
            
                listItemComponents: [
                    // Define delegates for different item types here
                    ListItemComponent {
                        type: "header"
                    
                        Header {
                            title: {
                                ListItemData.category
                            }
                        }
                    },
                    ListItemComponent { // Custom list item
                        type: "item"
                        FoodItem {
                        }
                }
                ] // listItemComponents
            } // ListView
        } // Container

Similarly, we can use an SQL database with GroupDataModel to represent the data in a list view. It is sorted with the grouping item category, compared to the list view that is used for the XML data storage.

               ...
               attachedObjects: [
                
                GroupDataModel {
                    id: dataModel
                    
                    sortingKeys: [ "category"]
                    grouping: ItemGrouping.ByFullValue
                },
                MySqlDataSource {
                    id: dataSource

                   // Load the data from an SQL database based on a specific query
                   source: "producecatalog.db"
                   query: "select * from ProduceCatalog"
                    
                    onDataLoaded: {
                        // After the data is loaded, insert it into the data model
                       dataModel.insertList(data);
                    }
                } // end of DataSource
            ] // attachedObjects
            
            onCreationCompleted: {
                // When the list view has been created load the data.
                dataSource.loadData();
            }
        } // ListView
    } // Container
} // Page

Screen shots of the example application using each of the three data sources are shown below.

  

I hope this small example has illustrated some of the QML features supported by BlackBerry 10 Cascades. You can download the complete source code for the example from here. You will need to have the BlackBerry 10 Cascades SDK installed to run it.