[前言]

制作完元信息DLL后可以解锁【工具箱组件支持拖放】、【手工右键添加窗口的组件类】

 


<以超级列表框为例演示说明>

最佳扩展实践案例:BEModSrc\扩展界面库一(含超级列表框与树型框的源码实现与DLL插件UI元信息实现)

当然下面我会一步步阐述元信息UI库的开发流程:

 

[准备你已经开发好的普通白易模块下的控件]

假如现在已经有依据《窗口UI控件模块开发规范.html》开发好了这个超级列表框控件

但IDE插件中的右键往窗口中添加新建组件类并不认,还需要把它再开发为白易的VSUIDll

 

[准备好超级列表框的内部::参数类]

这个静态_df必须仅声明,但不可自己在该设计的源文件中定义,定义的工作是由之后的uidllmeta.exe所生成的hpp去做的。

 

[uidllmeta.exe工具生成元信息源文件]

这个工具是在白易安装目录内,同样如果你绿化过了,此路径下的该工具可直接在任意路径中使用(直接运行,无需命令行)

 

当然对于不想再次用工具参与生成的,你还可以在生成的dllmeta文件夹内#ignore.txt这个配置文件进行排除

排除的好处是,只希望第一次让工具生成后续该元信息文件若有特化的地方由自己单独处理维护。

 

[开始进行VS配置]

1. 在配置管理器中新建一个VSUIDll的配置(当然名字可随意,一般由release工程继承而来(但必须是32位工程,VS2017/19的IDE都是32位的,所以必须加载的DLL也是32位),当然你为了调试也可先进行debug继承,这个倒没有强制或过多性能要求,但最终的uidll装进白易插件中必须是只有一份)

 

2. 然后main.cpp这里要参与编译(原先白易模块工程会默认排除)

然后把启动窗口这些源文件给排除编译

 

3. 把预处理器定义中的"_LIB"宏给去掉(必须得去否则引入的白易模块会因不引入lib而编译报错,当然白易这里为什么要在常规lib静态库作为模块化时要把 #ifndef _LIB 可以自己思考一下)

 

4. 接着在main.cpp中加入以下模板

#include <BEWin32UI/runtime.h>

int main(int nCShow = SW_SHOWNORMAL, char** argVec = nullptr)
{
全局初始化配置(GetModuleHandle(0), true);

_启动窗口.初显(nCShow).载入();
return Win32消息循环();
}

int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPTSTR, int nCShow)
{
return main(nCShow);
}



#if defined(_WINDLL)
#define _BE_FB //若当前控件项目依赖FB库则该宏阻止它二次引入定义(因为在VSUISDK中有内联)
#include "dllmeta/超级列表框.hpp"
#include "dllmeta/树型框.hpp"

BOOL APIENTRY DllMain(HMODULE hModule,
DWORD  ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
全局初始化配置(hModule, true);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}


struct UI库信息 : _UI库信息 {
int 组件个数 = sizeof(interfaceUI) / sizeof(interfaceUI[0]);
UI库组件信息 interfaceUI[2]{}; //你当前工程制作的控件个数

constexpr UI库信息() {
模块名 = "扩展界面库一"; //必须与当前工程中所开发的模块名一致
//如果你的模块工程一开始打算制作多控件那么模块取名上可不与具体控件名一致

分组名 = "扩展组件"; //可随意,白易不做约束,会将已有组名的控件一同加入进去
SDK主版本 = 1; //声明所需最低要求的SDK版本(主版本不一致时将直接不支持)
SDK次版本 = 0; //声明所需最低要求的SDK版本(次版本可作自由更动)
作者 = "小白菜软件(xbcsoft)";
日期 = "2026-10-01";
备注 = "https://wiki.xbcsoft.com/BEVCpp/"; //这个就随意了

#define _____________________________________________________________________
int i = 0;
interfaceUI[i].名称 = L"超级列表框";
interfaceUI[i].图标 = ICON::超列;
interfaceUI[i].图标大小 = sizeof(ICON::超列);
interfaceUI[i].属性个数 = sizeof(META_超级列表框) / sizeof(UI属性元信息);
interfaceUI[i].属性元信息 = META_超级列表框;
interfaceUI[i].fn创建 = UI_超级列表框_创建;
interfaceUI[i].fn置属性 = UI_超级列表框_置属性;

//如果一个DLL有多个控件的话
i = 1;
interfaceUI[i].名称 = L"树型框";
interfaceUI[i].图标 = ICON::树型;
interfaceUI[i].图标大小 = sizeof(ICON::树型);
interfaceUI[i].属性个数 = sizeof(META_树型框) / sizeof(UI属性元信息);
interfaceUI[i].属性元信息 = META_树型框;
interfaceUI[i].fn创建 = UI_树型框_创建;
interfaceUI[i].fn置属性 = UI_树型框_置属性;

fn全局属性面板 = [](LPWSTR 组件名称, LPWSTR 属性名,
LPWSTR 当前属性值, LPWSTR 即将改变到属性值, BEIDEFN ide方法) -> bool
{
StrW 属性名W = 属性名, 组件名称W = 组件名称;
using namespace IDE方法;
// 注:即将改变到属性值在设计上有二义性(需优先判断当前是否为UI事件)

if (即将改变到属性值 == UI事件类型::响应属性) {
if (组件名称W == L"超级列表框") {
Bytes* 类型值 = ide方法(取属性值, FB(L"类型"));
int 类型 = 到整数((charW*)类型值->buf);
bool 图标模式 = 类型 == 0 || 类型 == 1;
bool 报表模式 = 类型 == 3;
delete 类型值; //注意必须对IDE方法()所有的返回值进行内存释放
ide方法(启用禁用属性, FB(L"图标对齐方式", 图标模式));
ide方法(启用禁用属性, FB(L"自动排列图标", 图标模式));
ide方法(启用禁用属性, FB(L"标题自动换行", 图标模式));
ide方法(启用禁用属性, FB(L"报表列", 报表模式));
ide方法(启用禁用属性, FB(L"无表头", 报表模式));
ide方法(启用禁用属性, FB(L"表头图片组", 报表模式));
ide方法(启用禁用属性, FB(L"表头可单击", 报表模式));
ide方法(启用禁用属性, FB(L"整行选择", 报表模式));
ide方法(启用禁用属性, FB(L"显示表格线", 报表模式));
ide方法(启用禁用属性, FB(L"表列可拖动", 报表模式));
}
return true;
}
if (即将改变到属性值 != UI事件类型::自定数据) return true;

// 若取自定义数据则需要动态调用IDE方法
Bytes* 当前数据 = ide方法(取属性值, FB(属性名W));
Bytes* 编辑结果 = NULL;
if (组件名称W == L"超级列表框" && 属性名W == L"报表列") {
编辑结果 = UI超级列表框二维编辑器实现::编辑报表列(当前数据, ide方法);
} else if (组件名称W == L"超级列表框" && 属性名W == L"表项") {
编辑结果 = UI超级列表框二维编辑器实现::编辑表项(当前数据, ide方法);
} else if (组件名称W == L"超级列表框"
&& (属性名W == L"图片组" || 属性名W == L"状态图片组"
|| 属性名W == L"表头图片组")) {
编辑结果 = UI_图片组_编辑窗口(当前数据, 属性名W);
} else if (组件名称W == L"树型框" && 属性名W == L"项目") {
编辑结果 = UI树型框二维编辑器实现::编辑项目(当前数据);
} else if (组件名称W == L"树型框"
&& (属性名W == L"图片组" || 属性名W == L"检查框图片组")) {
编辑结果 = UI_图片组_编辑窗口(当前数据, 属性名W);
}
delete 当前数据;
if (!编辑结果) return false;
ide方法(置属性值, FB(属性名W, *编辑结果));
delete 编辑结果;
return true;
};
}
};

constexpr UI库信息 g_UI库信息;

EXPC UI库信息* UI_Info() {
return (UI库信息*)&g_UI库信息;
}

#endif

这样在这个里边就生成dll了

把它放进:白易安装目录/VSUIDll文件夹内,重启VS即可

 

[演示引入超级列表框]

 

由于没有安装至系统模块(也可自行安装或者在制作模块时用installG安装(参考MakeBEC模块制作器.mht),总之插件会根据是否找得到系统模块而进行引入,不是系统模块就完全不产生任何引入代码),

所以这里我们需要手动引入用户模块版的

引入后重写虚函数的功能也没问题了: