“Why Not”使用QML实现一个MVVM框架

weixin_38052215 2019-09-19 02:15:25
一.前言            最近几年最热门的技术之一就是前端技术了,各种前端框架,前端标准和前端设计风格层出不穷,而在众多前端框架中具有MVC,MVVM功能的框架成为耀眼新星,比如GitHub关注度很高的Vue.js ,由于是国人作品,其设计风格和文档友好度对国人而言更胜一筹。二.认识MVVM1.MVVM的结构      MVVM是Model-View-ViewModel的简写。其结构如下图:      View绑定到ViewModel,然后执行一些命令在向它请求一个动作。而反过来,ViewModel跟Model通讯,告诉它更新来响应UI。这样便使得为应用构建UI非常的容易。往一个应用程序上贴一个界面越容易,外观设计师就越容易使用Blend来创建一个漂亮的界面。同时,当UI和功能越来越松耦合的时候,功能的可测试性就越来越强。2.MVVM的优点      MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大优点      1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。      2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。      3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xml代码。      4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。3.模块职责      Model: 负责数据存储和业务逻辑。      View: 负责展示数据。并将自身的变化同步到ViewModel中      ViewModel: 对Model的封装,通过一系列属性暴露Model的状态,提供给View进行显示三.怎么把QML和MVVM结合起来。      我们知道VIew和ViewModel的双向绑定是MVVM架构的关键。WPF中使用data template、commands、data binding这些微软提供的技术进行数据绑定。那么我们的QT又有什么样的武器呢?没错就是Single/slots.下面用一个简单的实例来说明怎么实现QML版的MVVM。1.我们有一个登陆界面,用户输入账号和密码然后点击登陆按钮,并在界面上显示登陆信息。描述:1QML页面代码复制代码import QtQuick 2.7import QtQuick.Controls 2.0import QtQuick.Layouts 1.0ApplicationWindow {    visible: true    width: 320    height: 480    title: qsTr("MVVM")    header: ToolBar {        Text {            anchors.centerIn: parent            font.family: "微软雅黑"            font.pointSize: 16            color: "lightgreen"            text: qsTr("QML MVVM")        }    }    Column {        anchors.centerIn: parent        spacing: 5        TextArea {            id: nameTextArea            anchors.horizontalCenter: parent.horizontalCenter            placeholderText: qsTr("username")            text: mainViewModel.name            onTextChanged: mainViewModel.name = text        }        TextArea {            id: passwordTextArea            anchors.horizontalCenter: parent.horizontalCenter            placeholderText: qsTr("password")            text: mainViewModel.password            onTextChanged: mainViewModel.password = text        }        Button {            text: "Login"            anchors.right: parent.right            anchors.rightMargin: 5            onClicked: mainViewModel.loginButtonClicked()        }        Text {            id:stateText            anchors.horizontalCenter: parent.horizontalCenter            text: mainViewModel.state        }    }}从代码可以看出:QML中控件的值和ViewModel中的属性主要通过这两句实现双向绑定的:           text: mainViewModel.name      onTextChanged: mainViewModel.name = texta.根据页面中的元素去设计对应的ViewModel层需要的那些属性:name : QStringpassword : QStringstate : QStringloginButtonClicked : signal复制代码class MainViewModel : public QObject{    Q_OBJECT    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)    Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)    Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged)public:    explicit MainViewModel(QObject *parent = 0);    MainModel* model();    void setModel(MainModel *model);    QString name();    void setName(const QString &name);    QString password();    void setPassword(const QString &password);    QString state();    void setState(const QString &state);    Q_INVOKABLE void loginButtonClicked();signals:    void nameChanged(const QString &);    void passwordChanged(const QString &);    void stateChanged(const QString &);private:    QString m_name;    QString m_password;    QString m_state;    MainModel *m_model;};复制代码MainViewModel::MainViewModel(QObject *parent) : QObject(parent),    m_model(NULL){}MainModel *MainViewModel::model(){    return m_model;}void MainViewModel::setModel(MainModel *model){    m_model = model;    if (m_model)    {        connect(this, &MainViewModel::nameChanged, m_model, &MainModel::setName);        connect(this, &MainViewModel::passwordChanged, m_model, &MainModel::setPassword);    }}QString MainViewModel::name(){    return m_name;}void MainViewModel::setName(const QString &name){    m_name = name;    emit nameChanged(m_name);}QString MainViewModel::password(){    return m_password;}void MainViewModel::setPassword(const QString &password){    m_password = password;    emit passwordChanged(m_password);}QString MainViewModel::state(){    return m_state;}void MainViewModel::setState(const QString &state){    m_state = state;    emit stateChanged(state);}void MainViewModel::loginButtonClicked(){    if (m_model)    {        QString errorCode;        if (!m_model->login(&errorCode))        {            setState("Failed:" + errorCode);        }        else        {            setState("Successed");        }    }}      我们通过在setModel函数中调用connect将ViewModel中属性的改变同步到Model中b.在ViewModel层的loginButtonClicked函数中调用Model层的Login函数进行逻辑处理。Model层看起来是这样的:name : QStringpassword : QStringbool login();复制代码class MainModel : public QObject{    Q_OBJECTpublic:    explicit MainModel(QObject *parent = 0);    bool login(QString *error);    QString name();    void setName(const QString &name);    QString password();    void setPassword(const QString &password);private:    QString m_name;    QString m_password;};最终登陆效果:描述:2感谢你的阅读。谢谢。by汏家的诚2017.4.12
...全文
333 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

435

社区成员

发帖
与我相关
我的任务
社区描述
其他技术讨论专区
其他 技术论坛(原bbs)
社区管理员
  • 其他技术讨论专区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧