How to implement WPF-like MVVM in Qt/C++/QML?
Solution 1
I've posted a rather complete example in another answer.
The general procedure is:
Create a model that derives from
QAbstractItemModel
. You can reuse any of the models already provided by Qt, for exampleQStringListModel
.Expose it to QML. E.g. use
setContextProperty()
of QML Engine'srootContext()
.-
The model's roles are visible in the context of the delegate in QML. Qt provides default mapping between names and roles for the
DisplayRole
(display
) andEditRole
(edit
) in a default implementation ofroleNames()
.delegate: Component { TextInput { width: view.width // assuming that view is the id of the view object text: edit // "edit" role of the model, to break the binding loop onTextChanged: model.display = text // "display" role of the model } }
You can create intermediate viewmodels, if needed, by attaching proxy models between the views and the backend models. You can derive from
QAbstractProxyModel
or one of its subclasses.
Solution 2
The accepted answer is correct save for one detail. In MVVM you'd expose the ViewModel to QML, not the Model.
Related videos on Youtube
johnildergleidisson
Updated on October 10, 2022Comments
-
johnildergleidisson over 1 year
I'm writing a proof of concept application, that is very simple. Basically it's composed of a UI where a list of "Note" type objects is displayed in a QML ListView.
I then have a few classes which is something along the lines:
#ifndef NOTE_H #define NOTE_H #include <string> using namespace std; class Note { public: Note(QObject* parent = 0) : QObject(parent) { } Note(const int id, const string& text) : _id(id), _text(text) { } int id() { return _id; } const string text() { return _text; } void setText(string newText) { _text = newText; } private: int _id; string _text; }; #endif // NOTE_H
Then a repository:
class NoteRepository : public Repository<Note> { public: NoteRepository(); ~NoteRepository(); virtual shared_ptr<Note> getOne(const int id); virtual const unique_ptr<vector<Note>> getAll(); virtual void add(shared_ptr<Note> item); private: map<int, shared_ptr<Note>> _cachedObjects; };
Finally a ViewModel that exposes Note to QML
class MainViewModel : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty<Note> notes READ notes NOTIFY notesChanged) Q_PROPERTY(int count READ count() NOTIFY countChanged) public: MainViewModel(QObject *newParent = 0); int count(); QQmlListProperty<Note> notes(); signals: void notesChanged(); void countChanged(); public slots: private: std::shared_ptr<UnitOfWork> _unitOfWork; static void appendNote(QQmlListProperty<Note> *list, Note *note); QList<Note*> _notes; };
PLEASE DON'T MIND ANY C++ MISTAKES HERE and mind they are incomplete, it's not the point at this moment as I'm constantly adapting this as I learn.
The point where I'm struggling is, how to expose a list-like object to QML? The requirement is this list must be dynamic, one should be able to add, delete and modify the text of a note. When the list is modified by C++, it should also notify the UI (signal).
I tried QQmlListProperty, but couldn't figure a way of exposing it to QML. Then I read on another SO post this type can't be modified by QML (??), and I stumbled upon QAbstractItemModel.
Anyhow, can anyone point me to the right direction?
-
koopajah
QAbstractItemModel
should be the proper direction for this. You can find more info here : qt-project.org/doc/qt-5.1/qtquick/…
-