ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

[DevExpress]TreeListLookUpEdit带checkbox之经典运用:TaoToken统一Key接入配置实战

[DevExpress]TreeListLookUpEdit带checkbox之经典运用:TaoToken统一Key接入配置实战 1. DevExpress TreeListLookUpEdit 带 checkbox 的真实开发场景如果你正在做 DevExpress WinForms 的后台管理系统大概率会遇到这样一个需求一个下拉框里展示的是树形结构部门、菜单、角色权限每个节点前面带一个 checkbox用户勾选多个节点后输入框里要显示勾选节点的名称拼接串同时后台要拿到对应的 ID 集合去写库。这个控件就是TreeListLookUpEdit它把LookUpEdit的下拉能力和TreeList的层级 勾选能力合在了一起。听起来很美好但真正落地时会踩几个坑勾选父节点后子节点状态怎么联动、GetDataRecordByNode拿到的对象怎么转成DataRowView、CustomDisplayText里怎么把勾选结果回显到输入框、关闭下拉后EditValue为什么还是空的。这些问题在官方文档里讲得很散网上搜到的代码又经常缺胳膊少腿。这篇内容我会把TreeListLookUpEdit带 checkbox 的经典用法完整走一遍同时结合一个实际场景在项目里接入 TaoToken 统一 Key 通道让 AI 辅助编码工具Cline、CC Switch 这类能稳定调用模型。也就是说前半段解决 DevExpress 控件的勾选回传问题后半段解决 AI 工具的 Key 配置问题两者在同一个 WinForms 项目里是并存的——你写业务代码时需要 AI 帮你补全AI 工具又需要一套统一的 Key 管理。适合谁看正在用 DevExpress WinForms 做管理系统的开发者、需要给树形下拉加多选勾选的人、以及想把 AI 编码工具接进现有 .NET 项目的同学。下面从控件配置讲到 Key 接入每一步都能直接复制。2. 前置准备TaoToken 统一 Key 与项目环境在动手写控件代码之前先把 AI 辅助开发这条链路搭好因为后面调试控件时你会频繁让 AI 帮你查 DevExpress 的 API。TaoToken 在这里扮演的角色是统一 Key 通道你不需要在 Cline、CC Switch、Cursor 里各配一套不同厂商的 Key而是通过一个入口拿到 Key再分发给各个工具。先访问官网了解通道能力https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后在控制台创建 API Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content Key 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。API 基础地址统一用 https://taotoken.net/api 注意这个地址后面不加任何 UTM 参数配置工具时直接填这个。项目环境这边你需要Visual Studio 2019 或 2022装了 DevExpress 的 WinForms 组件v20 以上都行我用的是 22.2一个 .NET Framework 4.7.2 或 .NET 6/8 的 WinForms 项目数据库里有一张带ID、PARENTID、NAME字段的树形表比如角色菜单表我试过在 .NET 8 的 WinForms 项目里用 DevExpress 23.1TreeListLookUpEdit的 API 和 Framework 版本基本一致唯一区别是DataRowView的引用需要System.Data命名空间.NET Core 之后要显式 using。3. 可复制配置TreeListLookUpEdit 勾选回传完整代码3.1 控件初始化与数据绑定先看数据绑定部分。核心是设置KeyFieldName、ParentFieldName、DataSource、ValueMember、DisplayMember这五个属性缺一个树都建不起来。private void txtRoleBind() { // 从业务层拿树形数据按 PARENTID、ID 排序保证父节点先出现 DZAMS.BLL.DZ_RoleInfo rf new BLL.DZ_RoleInfo(); string where 11 order by PARENTID, ID ASC; DataTable tblDatas rf.GetList(where).Tables[0]; // 关键五连主键、父键、数据源、值成员、显示成员 this.txtRole.Properties.TreeList.KeyFieldName ID; this.txtRole.Properties.TreeList.ParentFieldName PARENTID; this.txtRole.Properties.DataSource tblDatas; this.txtRole.Properties.ValueMember ID; this.txtRole.Properties.DisplayMember NAME; }这里有个容易忽略的点DataSource必须是排好序的DataTable如果父节点排在子节点后面TreeList 会把它当成根节点处理树结构就乱了。所以order by PARENTID, ID ASC不是可选项是必须的。3.2 开启 checkbox 与默认勾选在窗体的OnLoad里开启 checkbox并做默认勾选。注意DesignMode判断否则设计器里会报错。protected override void OnLoad(EventArgs e) { base.OnLoad(e); if (DesignMode) return; // 开启节点前的复选框 txtRole.Properties.TreeList.OptionsView.ShowCheckBoxes true; txtRoleBind(); DefaultChecked(3); // 默认勾选角色 3 对应的菜单 GetSelectedRoleIDandName(); // 初始化回显 // 勾选后实时刷新回显 txtRole.Properties.TreeList.AfterCheckNode (s, a) { a.Node.Selected true; GetSelectedRoleIDandName(); }; }AfterCheckNode事件里把a.Node.Selected true是为了让当前节点高亮视觉上更清楚。如果你希望勾选后下拉框自动关闭可以加txtRole.ForceClosePopup()但多选场景一般不建议关用户还要继续勾。3.3 递归收集勾选节点的 ID 与 Name这是整个控件最核心也最容易写错的地方。GetDataRecordByNode返回的是object必须转成DataRowView才能取字段值。private Listint lstCheckedKeyID new Listint(); private Liststring lstCheckedKeyName new Liststring(); private void GetSelectedRoleIDandName() { lstCheckedKeyID.Clear(); lstCheckedKeyName.Clear(); if (txtRole.Properties.TreeList.Nodes.Count 0) { foreach (TreeListNode root in txtRole.Properties.TreeList.Nodes) { GetCheckedKeyID(root); } } // 拼接回显文本 lblKeyID.Text string.Join(,, lstCheckedKeyID); lblKeyName.Text string.Join(,, lstCheckedKeyName); } private void GetCheckedKeyID(TreeListNode parentNode) { if (parentNode.Nodes.Count 0) return; // 递归终止 if (parentNode.CheckState ! CheckState.Unchecked) { DataRowView drv txtRole.Properties.TreeList .GetDataRecordByNode(parentNode) as DataRowView; if (drv ! null) { int keyId (int)drv[ID]; string name drv[NAME].ToString(); if (!lstCheckedKeyID.Contains(keyId)) lstCheckedKeyID.Add(keyId); if (!lstCheckedKeyName.Contains(name)) lstCheckedKeyName.Add(name); } } foreach (TreeListNode node in parentNode.Nodes) { if (node.CheckState ! CheckState.Unchecked) { DataRowView drv txtRole.Properties.TreeList .GetDataRecordByNode(node) as DataRowView; if (drv ! null) { int keyId (int)drv[ID]; string name drv[NAME].ToString(); if (!lstCheckedKeyID.Contains(keyId)) lstCheckedKeyID.Add(keyId); if (!lstCheckedKeyName.Contains(name)) lstCheckedKeyName.Add(name); } } GetCheckedKeyID(node); } }注意drv[NAME]的大小写要和数据库字段一致我见过有人写成drv[Name]结果取到 null排查半天。另外Contains去重是必要的因为父节点和子节点可能被重复遍历。3.4 回显到输入框CustomDisplayTextTreeListLookUpEdit默认显示的是DisplayMember对应的值多选场景下这个值没意义需要用CustomDisplayText事件覆盖。void txtRole_CustomDisplayText(object sender, CustomDisplayTextEventArgs e) { e.DisplayText lblKeyName.Text; // 用勾选名称拼接串回显 }记得在OnLoad里挂上这个事件txtRole.CustomDisplayText txtRole_CustomDisplayText;。如果你不挂输入框里显示的就是最后勾选的那个节点名不是拼接串。3.5 TaoToken 通道配置骨架控件调通了接下来把 AI 工具的 Key 配好。TaoToken 的 API 地址是https://taotoken.net/apiKey 从控制台拿。下面给两个常用工具的配置骨架。ClineVS Code 插件的settings.json片段{ cline.apiProvider: openai, cline.openAiBaseUrl: https://taotoken.net/api, cline.openAiApiKey: sk-你的TaoTokenKey, cline.openAiModelId: claude-sonnet-4-20250514 }CC Switch 的config.toml骨架[provider] name taotoken base_url https://taotoken.net/api api_key sk-你的TaoTokenKey [model] default claude-sonnet-4-20250514 max_tokens 8192如果你用的是 Claude Code 这类命令行工具接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面有各工具的详细参数说明。长期做编码和 Agent 任务的话可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 按用量规划更划算。4. 验证请求勾选回传与通道连通性4.1 验证 TreeListLookUpEdit 勾选回传写一个简单的验证按钮把lstCheckedKeyID和lstCheckedKeyName打印出来private void btnVerify_Click(object sender, EventArgs e) { GetSelectedRoleIDandName(); string msg $勾选ID: {string.Join(,, lstCheckedKeyID)}\n $勾选名称: {string.Join(,, lstCheckedKeyName)}; MessageBox.Show(msg, 勾选结果验证); }操作步骤运行程序下拉框展开勾选几个父节点和子节点点验证按钮。预期结果是 ID 和名称都按勾选顺序拼接且没有重复。如果发现某个子节点勾了但没出现在结果里检查GetCheckedKeyID的递归逻辑是不是漏了node.Nodes的遍历。4.2 验证 TaoToken 通道连通性用 curl 直接打一次接口确认 Key 和地址都对curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的TaoTokenKey \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: 回复OK两个字}], max_tokens: 16 }预期返回里能看到content: OK之类的响应。如果返回 401说明 Key 不对返回 404检查 base_url 是不是多写了/v1或者少了。TaoToken 的 API 地址就是https://taotoken.net/api路径拼接交给工具自己处理。想直接在网页上试模型对话的话可以打开 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 选好模型发一句话能回就说明通道没问题。5. 本篇常见错误排查5.1 GetDataRecordByNode 返回 null最常见的原因是DataSource不是DataTable或者KeyFieldName没设对。GetDataRecordByNode依赖KeyFieldName去数据源里找记录如果主键字段名写错返回就是 null。检查KeyFieldName ID和数据库字段是否完全一致。5.2 勾选父节点子节点不联动TreeListLookUpEdit默认的勾选行为是独立的父节点勾选不会自动勾子节点。如果你需要联动得在AfterCheckNode里手动递归设置子节点的CheckStatetxtRole.Properties.TreeList.AfterCheckNode (s, a) { SetChildCheckState(a.Node, a.Node.CheckState); a.Node.Selected true; GetSelectedRoleIDandName(); }; private void SetChildCheckState(TreeListNode node, CheckState state) { foreach (TreeListNode child in node.Nodes) { child.CheckState state; SetChildCheckState(child, state); } }5.3 输入框回显为空两个原因一是CustomDisplayText事件没挂二是lblKeyName.Text在GetSelectedRoleIDandName之前被清空了。检查GetSelectedRoleIDandName里是不是先Clear了列表再拼接顺序不能反。5.4 TaoToken 返回 429说明请求频率超了检查是不是在循环里疯狂调接口。Cline 这类工具默认会带重试如果一直 429去控制台看用量或者换 Coding Plan 提升配额。5.5 配置文件里 Key 泄露settings.json和config.toml千万别提交到 Git。把这两个文件加进.gitignore或者用环境变量注入 Key。TaoToken 控制台可以随时吊销旧 Key 重新生成发现泄露第一时间去 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 处理。6. 接入文档与工具分流控件代码和 Key 配置都跑通之后日常开发就是两件事并行写 DevExpress 业务代码时让 AI 帮你查 APIAI 工具通过 TaoToken 统一通道调模型。如果你在接入过程中遇到报错优先看接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面按工具分类列了常见问题和参数说明。需要管理多个 Key 或者给团队分发时去 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 创建子 Key。长期跑编码 Agent 的话Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 比按次调用更稳。模型对话验证用 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 最快。最后补一个我踩过的坑TreeListLookUpEdit的Closed事件里不要再去调GetSelectedRoleIDandName因为关闭时节点状态可能还没同步完容易拿到旧数据。把刷新逻辑放在AfterCheckNode里就够了。
返回列表