Custom QML components

Even though Cascades comes with lots of useful controls right out of the box, you will likely come to a point where you must create your own custom components.

Custom components are reusable UI components that can be used just like any other component that comes with the framework.

A common way of creating custom components is by joining together existing Cascades controls. For example, in a messaging application, you might want to create a ContactView control that displays a user's name and profile picture and a DetailsView control that lists a user's contact information.

Another way of creating custom controls is by subclassing the  CustomControl class in C++. To read more about subclassing CustomControl, see Using C++ classes in QML.

Custom components in QML

A custom component is essentially a QML document that defines a single QML component that you can use in an application. During runtime, QML components are interpreted to create objects according to the behaviors defined by the document.

To create a QML component, you create a file called <Name>.qml, where <Name> is the new component name that begins with an uppercase letter.

Here's an example of a simple custom component that combines a CheckBox and a Label. The component is defined in a file called CustomCheckBox.qml.

Screen showing a simple custom component with a check box and a label.
import bb.cascades 1.2
 
Container {
    layout: StackLayout {
        orientation: LayoutOrientation.LeftToRight
    }
    CheckBox {
        id: checkBox
        checked: false
    }
    Label {
        id: label
        leftMargin: 10
        text: "Check Box"
    }
}

Using a custom component

To use a custom component in an application, you use the component name the way that you would with any core component.

Below is an example of how you could use the CustomCheckBox component in another QML document. The component is assigned an id and has layout properties set on it. The CustomCheckBox.qml file must be located in the same directory as this file.

import bb.cascades 1.2
 
Page {
    Container {
        layout: DockLayout {}
        CustomCheckBox {
            id: customCheckBox
            verticalAlignment: VerticalAlignment.Center
            horizontalAlignment: HorizontalAlignment.Center
        }
    }
}

Custom properties

When you create a custom component, you will often need to expose the properties of its subcomponents. In the example above, CustomCheckBox contains a  CheckBox and a  Label, and each control has its own set of properties. However, in the custom component's current state, these inner properties can't be accessed from outside the component. And what good is this CustomCheckBox if you can't change its text?

To expose these properties, you must define new properties at the root of your custom component and bind the inner properties that you want to expose to them.

In the example below, the text property for the Label and the checked property for the CheckBox are exposed for CustomCheckBox.

import bb.cascades 1.2
 
Container {
    property string text: "Hello"
    property bool checked: false   
     
    CheckBox {
        checked: customCheckBoxRoot.checked
    }
    Label {
        text: customCheckBoxRoot.text
    }
}

Property aliasing

To create a direct reference from one property to another, you can use the alias keyword. When you use the alias keyword, any read or write operations on that property actually results in operations on the aliased property. Because the aliasing property refers to the aliased property directly, new memory isn't allocated for the aliasing property value.

import bb.cascades 1.2
 
Container {
    property alias text: label.text
    property alias checked: checkBox.checked
     
    CheckBox {
        id: checkBox
        checked: false
    }
    Label {
        id: label
        text: "Hello"
    }
}

Default properties

You can also create your own default properties using the default keyword. Here's a custom container called Cell.qml that has a default property called cellContent, which is bound to an inner container.

// Cell.qml

import bb.cascades 1.2

Container {
    default property alias cellContent: content.controls
    layout: DockLayout {}
    horizontalAlignment: HorizontalAlignment.Fill
    verticalAlignment: VerticalAlignment.Fill

    ImageView {
        imageSource: "asset:///images/frame6.amd"
        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Fill
    }
    Container {
        id: content
        layout: DockLayout {}
        leftPadding: 20
        rightPadding: 20
        bottomPadding: 20
        topPadding: 20
        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Fill
    }
}

Any controls added to the custom container are automatically added to its inner container. Here's how you could add a Label to the inner container of Cell.qml.

Container {
    Cell {
        Label {
            text: "Some text"
        }
    }
}

Custom signals

When you create custom components you might need to be able to define signals within the component that are accessible outside its scope. Using the same example as above, here's how to create a signal called cellActive(bool active) that gets triggered when a ToggleButton is checked.

// Cell.qml

import bb.cascades 1.2

Container {
    default property alias cellContent: content.controls
    layout: DockLayout {}
    horizontalAlignment: HorizontalAlignment.Fill
    verticalAlignment: VerticalAlignment.Fill

    ImageView {
        imageSource: "asset:///images/frame6.amd"
        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Fill
    }
    
    signal cellActive(bool active)
    
    ToggleButton {
        onCheckedChanged: {
            cellActive(checked)
        }
    }
    Container {
        id: content
        layout: DockLayout {}
        leftPadding: 20
        rightPadding: 20
        bottomPadding: 20
        topPadding: 20
        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Fill
    }
}

From outside the component, you can capture the signal just as you would any other signal. In this example, when the signal is captured, the text for the Label gets updated to indicate whether or not the inner container is active.

Container {
    Cell {
        onCellActive: {
            activeText.text = "active: " + active
        }
        Label {
            id: activeText 
            text: "Some text"
        }
    }
}

Last modified: 2014-05-14



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus