# 快速上手 此示例将引导你快速部署一个基于 duilib 的基本应用,此示例与 `samples` 中的 `basic` 项目一致,如果你更喜欢查看代码可以参考示例代码而无需多花费时间。 ## 克隆项目 ```bash git clone https://github.com/netease-im/NIM_Duilib_Framework ``` ## 创建基础工程 使用 Visual Studio 打开克隆后目录中 `samples\\samples.sln` 解决方案, 解决方案中包含了一些简单示例作为参考,你可以参考示例或新建一个 Windows 桌面应用,来一步一步完成第一个 duilib 窗口。 当新建一个 Windows 桌面程序(VS2017)后,将生成的代码清理一下只保留 wWinMain 函数。 ```cpp int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); return 0; } ``` ## 配置项目属性 将 tool_kits 目录下的 base、shared、duilib 工程加入到解决方案中。 项目属性->常规,修改输出目录为克隆后项目的 bin 目录下 项目属性->常规,修改平台工具集与你编译的 duilib 保持一致 项目属性->VC++ 目录->包含目录中,添加 `tool_kits` 目录到包含目录中。如果你需要使用 CEF 模块请记得引入 `third_party\cef_wrapper` 目录 项目属性->C/C++->代码生成,将 Debug 模式的运行库修改为 `/MTd`,将 Release 模式的运行库修改为 `/MT` 项目右键->添加->引用,将 base、shared、duilib 作为引用项目,这样就不需要手动引入静态库文件了。 ## 引入头文件 在 stdafx.h 中引入所需的头文件 ```cpp // base header #include "base/base.h" // shared project #include "shared/shared.h" // duilib #include "duilib/UIlib.h" ``` ## 引入线程库 在创建的项目中 wWinMain 所属的头文件中,增加自定义的主线程对象。 ```cpp class MainThread : public nbase::FrameworkThread { public: MainThread() : nbase::FrameworkThread("MainThread") {} virtual ~MainThread() {} private: /** * 虚函数,初始化主线程 * @return void 无返回值 */ virtual void Init() override; /** * 虚函数,主线程退出时,做一些清理工作 * @return void 无返回值 */ virtual void Cleanup() override; private: std::unique_ptr misc_thread_; }; ``` 在 wWinMain 的下面增加主线程对象的方法实现,在这里初始化 duilib ```cpp void MainThread::Init() { nbase::ThreadManager::RegisterThread(kThreadUI); // 获取资源路径,初始化全局参数 // 默认皮肤使用 resources\\themes\\default // 默认语言使用 resources\\lang\\zh_CN // 如需修改请指定 Startup 最后两个参数 std::wstring theme_dir = QPath::GetAppPath(); ui::GlobalManager::Startup(theme_dir + L"resources\\", ui::CreateControlCallback(), false); } void MainThread::Cleanup() { ui::GlobalManager::Shutdown(); SetThreadWasQuitProperly(true); nbase::ThreadManager::UnregisterThread(); } ``` 在 wWinMain 实例化主线程对象,并调用执行主线程循环,添加后 wWinMain 函数修改如下: ```cpp int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // 创建主线程 MainThread thread; // 执行主线程循环 thread.RunOnCurrentThreadWithLoop(nbase::MessageLoop::kUIMessageLoop); return 0; } ``` ## 创建一个简单窗口 创建一个窗口类,继承 `ui::WindowImplBase` 类,并覆写 `GetSkinFolder` `GetSkinFile` `GetWindowClassName` 三个方法。 ```cpp class BasicForm : public ui::WindowImplBase { public: BasicForm(); ~BasicForm(); /** * 一下三个接口是必须要覆写的接口,父类会调用这三个接口来构建窗口 * GetSkinFolder 接口设置你要绘制的窗口皮肤资源路径 * GetSkinFile 接口设置你要绘制的窗口的 xml 描述文件 * GetWindowClassName 接口设置窗口唯一的类名称 */ virtual std::wstring GetSkinFolder() override; virtual std::wstring GetSkinFile() override; virtual std::wstring GetWindowClassName() const override; /** * 收到 WM_CREATE 消息时该函数会被调用,通常做一些控件初始化的操作 */ virtual void InitWindow() override; /** * 收到 WM_CLOSE 消息时该函数会被调用 */ virtual LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); static const std::wstring kClassName; }; ``` ```cpp #include "stdafx.h" #include "basic_form.h" const std::wstring BasicForm::kClassName = L"Basic"; BasicForm::BasicForm() { } BasicForm::~BasicForm() { } std::wstring BasicForm::GetSkinFolder() { return L"basic"; } std::wstring BasicForm::GetSkinFile() { return L"basic.xml"; } std::wstring BasicForm::GetWindowClassName() const { return kClassName; } void BasicForm::InitWindow() { // 做一些初始化工作 } LRESULT BasicForm::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { PostQuitMessage(0L); return __super::OnClose(uMsg, wParam, lParam, bHandled); } ``` ## 创建窗口描述 XML 文件 在我们创建的窗口类中,指定了窗口描述文件目录是 `basic`,指定窗口的描述文件为 `basic.xml`。 接下来在 `resources\\themes\\default` 目录下创建 `basic` 文件夹并新建一个 `basic.xml` 文件,写下如下内容。 ```xml