Qml.Net - Qt/QML integration/support for .NET
Find a file
2018-07-24 20:10:45 -04:00
build Bump version [skip ci] 2018-07-24 20:10:45 -04:00
src Don't need to log this anymore. 2018-07-24 19:57:48 -04:00
tmp/Test Added a rough test to verify we can load qml files. 2017-05-31 15:31:57 -04:00
.gitignore Added a step to deploy nuget package. 2018-07-22 23:45:03 -04:00
.gitmodules Adding build project. 2018-07-12 21:00:05 -04:00
.travis.yml Got travis build working. 2018-07-14 14:25:31 -04:00
appveyor.yml Added an appveyor build. 2018-07-21 20:57:39 -04:00
build.bat Added bat/ps1 file for building on Windows. 2018-07-21 17:30:52 -04:00
build.ps1 Added bat/ps1 file for building on Windows. 2018-07-21 17:30:52 -04:00
build.sh Adding build project. 2018-07-12 21:00:05 -04:00
LICENSE Initial commit 2017-05-31 14:04:37 -04:00
README.md [skip ci] 2018-07-23 00:54:58 -04:00

Qml.Net

A Qml integration with .NET

Qml.Net Build status Build status

Supported platforms/runtimes:

  • Runtimes:
    • .NET Framework
    • .NET Core
    • Mono
  • Operating systems
    • Linux
    • OSX
    • Windows

The idea

Define a .NET type (POCO)

[Signal("CustomSignal", NetVariantType.String)] // You can define signals that Qml can listen to.
public class QmlType
{
    /// <summary>
    /// Properties are exposed to Qml.
    /// </summary>
    [NotifySignal("StringPropertyChanged")] // For Qml binding/MVVM.
    public string StringProperty { get; set; }

    /// <summary>
    /// Methods can return .NET types.
    /// The returned type can be invoked from Qml (properties/methods/events/etc).
    /// </summary>
    /// <returns></returns>
    public QmlType CreateNetObject()
    {
        return new QmlType();
    }

    /// <summary>
    /// Qml can pass .NET types to .NET methods.
    /// </summary>
    /// <param name="parameter"></param>
    public void TestMethod(QmlType parameter)
    {
    }

    /// <summary>
    /// Qml can also pass Qml/C++ objects that can be invoked from .NET
    /// </summary>
    /// <param name="qObject"></param>
    public void TestMethodWithQObject(dynamic qObject)
    {
        string result = qObject.PropertyDefinedInCpp;
        qObject.MethodDefinedInCpp(result);
    }
    
    /// <summary>
    /// Async methods can be invoked with continuations happening on Qt's main thread.
    /// </summary>
    public async Task TestAsync()
    {
        Console.WriteLine("Hello from UI thread!");
        await Task.Run(() =>
        {
            Console.WriteLine("Hello from background thread!");
        });
        Console.WriteLine("Welcome back to the UI thread!");
    }
    
    /// <summary>
    /// .NET can activate signals to send notifications to Qml.
    /// </summary>
    public void ActivateCustomSignal(string message)
    {
        this.ActivateSignal("CustomSignal", message)
    }
}

Register your new type with Qml.

using (var app = new QGuiApplication(r))
{
    using (var engine = new QQmlApplicationEngine())
    {
        // Register our new type to be used in Qml
        QQmlApplicationEngine.RegisterType<QmlType>("test", 1, 1);
        engine.loadFile("main.qml");
        return app.exec();
    }
}

Use the .NET type in Qml

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import test 1.1

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    QmlType {
      id: test
      Component.onCompleted: function() {
          // We can read/set properties
          console.log(test.StringProperty)
          test.StringPropertyChanged.connect(function() {
              console.log("The property was changed!")
          })
          test.StringProperty = "New value!"
          
          // We can return .NET types (even ones not registered with Qml).
          var netObject = test.CreateNetObject();
          
          // All properties/methods/signals can be invoked on "netObject"
          // We can also pass the .NET object back to .NET
          netObject.TestMethod(netObject)
          
          // We can trigger signals from .NET
          test.CustomSignal.connect(function(message) {
              console.log("message: " + message)
          })
          test.ActivateCustomSignal("test message!")
      }
      function testHandler(message) {
          console.log("Message - " message)
      }
    }
}

Getting started

Step 1: Install NuGet package

dotnet add package Qml.Net

Step 2: Compile the native bindings

The native libraries aren't shipped with the NuGet package considering the complexity of a Qt/Qml installation, but there are discussions to do so (see here)

  1. Install Qt (at least 5.9) and Qt Creator.
  2. Build and deploy src/native/QmlNet/QmlNet.pro.
  3. Massage your PATH/LD_LIBRARY_PATH/DYLD_LIBRARY_PATH to contain the path that you've deployed the native QmlNet library.

Currently implemented

  • Support for all the basic Qml types and the back-and-forth between them (DateTime, string, etc).
  • Reading/setting properties on .NET objects.
  • Invoking methods on .NET obejcts.
  • Declaring and activating signals on .NET objects.
  • async and await (no return types yet).

Not implemented (but planned)

  • Compiling Qml resource files and bundling them within .NET.
  • Passing dynamic javascript objects to .NET as dynamic. They will be either a live mutable instance, or as a JSON serialized snapshot of the object.
  • Passing QObject types to .NET with support for interacting with signals/slots/properties on them.
  • Cancellable async tasks.
  • Return value from async tasks.
  • INotifyPropertyChanged support for signal notification of property changes in Qml. This will allow Qml to bind to .NET properties.
  • .NET Events to signals
  • Custom V8 type that looks like an array, but wraps a .NET IList<T> instance, for modification of list in Qml, and performance.
  • General perf improvements (particularly with reflection).
  • NuGet/MyGet feed.