登录  | 立即注册

游客您好!登录后享受更多精彩

查看: 357|回复: 1

Duilib入门教程02_制作一个简单的界面

[复制链接]

78

主题

-6

回帖

71

积分

网站编辑

积分
71
发表于 2024-12-12 22:53:41 | 显示全部楼层 |阅读模式
本帖最后由 大理寺少卿 于 2024-12-12 22:55 编辑

在实现这个基本窗口之前,我们首先根据自己的习惯设置一下 DuiLib 编译后生成的文件路径和项目依赖的头文件目录。你不一定要按着我的修改,符合你自己的使用习惯即可。要修改的位置主要有一下几个。修改过程比较繁琐,主要还是因为 DuiLib 是从 VC6 升级上来的,很多属性需要删除和修改,也有些属性是 DuiLib 团队自己的目录结构风格,改与不改不影响使用。
  • 常规->输出路径
  • 常规->中间目录(DLL 和 LIB 不能有冲突)
  • 常规->输出文件名(32 位和 64 位不一样,Debug 和 Release 也不同)
  • C/C++->预编译头->预编译头输出文件
  • 连接器->常规 删除 DuiLib 项目原有的输出文件选项
  • 连接器->系统->子系统 设置子系统为窗口(负责构建过程中会有警告)
  • 连接器->高级->到入库 删除默认到入库
  • 常规->平台工具集 设置 EXE 的平台工具集与 DuiLib 一致为 VS2013(如果是 2017 需要改一些因新标准导致的编译错误)
  • VC++ 目录->包含目录 设置 EXE 依赖的头文件目录,如果使用静态库那么要设置附加库目录和附加库文件
  • C/C++->代码生成->运行库 设置 EXE 项目的C/C++代码生成->运行库为 /MTd 和 /MT 与 DuiLib 保持一致否则链接时报错
  • 连接器->输入 设置 EXE 项目依赖项
DuiLib 的具体结构这里我们先不说,目前我们仅需要了解,如何使用动态库或静态库来创建一个基于 DuiLib 的简单界面就可以了,然后再循序渐进的往深入去挖一挖。DuiLib 实现了一个窗口基类,我们自己的窗口只需要继承这个类,实现三个必须要实现的纯虚函数,然后设置一下窗口使用的配置文件、窗口配置文件的路径和窗口的名称就可以了。
  • 继承 WindowImplBase 类(DuiLib 窗口管理的一个基类)
  • 实现 GetWindowClassName 接口(描述窗口唯一名称的方法)
  • 实现 GetSkinFile 接口(描述窗口样式的 xml 文件名称方法)
  • 实现 GetSkinFolder 接口(描述窗口样式文件路径的方法)
  • 创建一个窗口描述配置文件(描述窗口的 xml 样式文件)
根据上面的方法,我们把向导自动生成的项目代码删一删,修改 duilib_tutoral.cpp 文件,仅留下一个 main 函数,如下所示:
//duilib_tutorial.cpp: 定义应用程序的入口点。
//

#include "stdafx.h"
#include "duilib_tutorial.h"

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    return 0;
}
打开 stdafx.h 添加 ATL 依赖(创建项目时选择了 ATL 支持,但是没有导入 ATL 的库文件,可能是 VS2017 的 Bug),再添加上 DuiLib 的统一入口头文件和引入整个命名空间(因为是做示例,大型项目不建议引入整个命名空间)
然后我们在 duilib_tutoral.cpp 中创建一个自己的窗口,来继承 WindowImplBase,并实现 GetSkinFolder GetSkinFile GetWindowClassName 三个接口,代码如下:
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//

#pragma once

#include "targetver.h"

#define WIN32_LEAN_AND_MEAN             // 从 Windows 头中排除极少使用的资料
// Windows 头文件: 
#include <windows.h>

// C 运行时头文件
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>

// ATL
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS
#include <atlbase.h>
#include <atlstr.h>

// TODO: 在此处引用程序需要的其他头文件
#include "UIlib.h"
using namespace DuiLib;
class MainWndFrame : public WindowImplBase
{
protected:
        virtual CDuiString GetSkinFolder() override;                                                        // 获取皮肤文件的目录,如果有多层目录这里可以设置
        virtual CDuiString GetSkinFile() override;                                                                // 设置皮肤文件名字
        virtual LPCTSTR GetWindowClassName(void) const override;        // 设置当前窗口的 class name

public:
        static const LPCTSTR kClassName;
        static const LPCTSTR kMainWndFrame;
};

DuiLib::CDuiString MainWndFrame::GetSkinFolder()
{
        // GetInstancePath 接口返回默认的皮肤文件位置
        // 在 main 函数中我们可以通过 SetResourcePath 来设置路径
        return m_PaintManager.GetInstancePath();
}

DuiLib::CDuiString MainWndFrame::GetSkinFile()
{
        // 成员变量定义的皮肤文件名
        return kMainWndFrame;
}

LPCTSTR MainWndFrame::GetWindowClassName(void) const
{
        // 成员变量定义的窗口 class name
        return kClassName;
}

const LPCTSTR MainWndFrame::kClassName = _T("main_wnd_frame");
const LPCTSTR MainWndFrame::kMainWndFrame = _T("main_wnd_frame.xml");
仔细分析代码,我们可以看到我们指定了这个窗口的皮肤路径是默认路径(取决于我们如何设置,稍后就能看到),并指定了这个窗口的皮肤文件 main_wnd_frame.xml,最后还指定了一下窗口的类名。这样这个窗口就创建好了,我们还需要在 mian 函数中把这个窗口 new 出来,其次还需要创建一个 xml 文件来描述一下这个窗口的样子。先来写 main 函数。
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);

        // 设置窗口关联的实例
        CPaintManagerUI::SetInstance(hInstance);

        // 设置皮肤的默认路径
        CPaintManagerUI::SetCurrentPath(CPaintManagerUI::GetInstancePath());
        CPaintManagerUI::SetResourcePath(_T("theme"));

        // 创建窗口
        MainWndFrame* pMainWndFrame = new MainWndFrame;
        pMainWndFrame->Create(nullptr, MainWndFrame::kClassName, UI_WNDSTYLE_DIALOG, 0);
        pMainWndFrame->CenterWindow();
        pMainWndFrame->ShowWindow();

        CPaintManagerUI::MessageLoop();

        if (nullptr != pMainWndFrame)
        {
                delete pMainWndFrame;
        }

        return 0;
}
Create(nullptr, MainWndFrame::kClassName, UI_WNDSTYLE_DIALOG, 0);        pMainWndFrame->CenterWindow();        pMainWndFrame->ShowWindow();        CPaintManagerUI::MessageLoop();        if (nullptr != pMainWndFrame)        {                delete pMainWndFrame;        }        return 0;}" tabindex="0" role="button" style="box-sizing: border-box; position: relative; line-height: 20px; vertical-align: middle; cursor: pointer; user-select: none; border: 0px; border-radius: 6px; appearance: none; box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color, box-shadow, border-color; width: var(--control-small-size, 28px); height: var(--control-small-size, 28px); display: flex !important; justify-content: center !important; align-items: center !important; margin: var(--base-size-8, 8px) !important;">

通过 CPaintManagerUI 的一些静态设置了当前关联的窗口实例、皮肤文件的路径,接下来 new 了一个我们继承 WindowImplBase 所产生的窗口。调用 Create 方法创建了窗口,使用 CenterWindow 让窗口居中显示,再调用 ShowWindow 显示窗口。最后我们使用了 CPaintManagerUIMessageLoop 启动消息循环的监听,保证程序不被退出。并且在退出前我们要 delete 掉 new 出来的窗口。这样创建窗口的过程就完事儿了,但是现在还是不能运行的,我们还需要完善一下这个窗口的 xml 文件。
代码中设置了皮肤文件路径是 EXE 目录下的 theme 文件夹,所以要在 EXE 生成的文件夹创建一个 theme 文件夹,把 main_wnd_frame.xml 放到这个里面。
把如下代码添加到 xml 文件中(先暂时不需要关注 xml 的内容,后面会详细的讲解)
<?xml version="1.0" encoding="UTF-8"?>
<Window size="640,480" caption="0,0,0,35">
        <VerticalLayout>
                <HorizontalLayout bkcolor="#FFFFFFFF"/>
                <HorizontalLayout bkcolor="#FFFFFFFF">
                        <Button text="Hello DuiLib" bkcolor="#FF1296DB"/>
                </HorizontalLayout>
                <HorizontalLayout bkcolor="#FFFFFFFF"/>
        </VerticalLayout>
</Window>
<clipboard-copy aria-label="Copy" class="ClipboardButton btn btn-invisible js-clipboard-copy m-2 p-0 d-flex flex-justify-center flex-items-center" data-copy-feedback="Copied!" data-tooltip-direction="w" value="                                                                                                        " tabindex="0" role="button" style="box-sizing: border-box; position: relative; line-height: 20px; vertical-align: middle; cursor: pointer; user-select: none; border: 0px; border-radius: 6px; appearance: none; box-shadow: none; transition: color 80ms cubic-bezier(0.33, 1, 0.68, 1), background-color, box-shadow, border-color; width: var(--control-small-size, 28px); height: var(--control-small-size, 28px); display: flex !important; justify-content: center !important; align-items: center !important; margin: var(--base-size-8, 8px) !important;">

所有准备工作就绪,我们编译一下程序,但你会发现报了一大堆的错误,如下所示
很明显,程序不知道到哪里去找我们用到的这些函数,换句话说还没告诉程序要用 DuiLib 的动态库还是静态库,这个好解决。如果你想使用动态库,那么首先保证 EXE 目录下有动态库的文件,其次在项目的 属性->C/C++->预处理器 中增加 UILIB_EXPORTS 的预定义宏,这是告诉 DuiLib 你需要把我们用到的接口按动态库的方式导出。其实搜索一下 UILIB_EXPORTS 就可以看到具体的定义了。
如果你想使用静态库,同样,定义一个 UILIB_STATIC 的预定义宏然后在项目 属性->连接器->输入 中,输入附加依赖库的 lib 文件名字就可以啦(在之前我们已经在项目属性->VC++目录 设置中添加了附加库的目录,所以直接添加附加库就可以了 )。当你定义完预定义宏后再次编译就可以编译通过了,运行程序后窗口就显示出来了。如下所示
但看起来这个窗口有点简陋,只有中间一个蓝条,没有标题栏、没有状态栏,也不能关闭。先不着急,在接下来的教程中一点点循序渐进的往界面中添加内容。

本帖被以下淘专辑推荐:

0

主题

189

回帖

145

积分

注册会员

积分
145
发表于 2025-3-11 09:03:09 | 显示全部楼层
支持一下!!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|断点社区 |网站地图

GMT+8, 2025-4-5 06:34 , Processed in 0.120261 second(s), 29 queries , Yac On.

Powered by XiunoBBS

Copyright © 2001-2025, 断点社区.

快速回复 返回顶部 返回列表