改BUG心得
-
指针使用,平时指针直接传参是因为我们修改的是指针指向的区域;如果要修改指针本身,则要加引用
- 指针是null,在函数中new:加&
- 类中指针,通过函数get出来,想修改类内指针:不能直接对get出来的指针赋值,需要Set或者将类内变量改为public,直接对类内属性赋值
-
对话框相关
- 遇到不适当参数:MessageMap中的资源出问题了
- 崩了:DodataExchange中的资源出问题了
-
如果一个元素需要再该方法之外的地方进行使用,那么一定要使用指针
我在A中声明一个类变量,将其赋值给一个公共成员,在B中进行使用,但是会发现,得到的内存地址都是正确的,只是无法使用,原因便是A中的函数定义完变量之后,在函数结束后,会将该成员变量释放,导致之后使用会无效,所以需要使用指针,这样就会一直存在于内存,可以随时读取,不过要记得在最后将其释放掉
void CDlgLayerTree::InitTreeStruct(CString rootName) { //AfxSetResourceHandle(((CDrawingFrame*)AfxGetMainWnd())->dllRc); CImageList* m_FileViewImages = new CImageList(); bool asd = m_FileViewImages->Create(IDB_FILE_VIEW, 16, 16, RGB(255, 0, 0)); int n = m_FileViewImages->GetImageCount(); m_layerTree.SetImageList(m_FileViewImages, TVSIL_NORMAL); //AfxSetResourceHandle(((CDrawingFrame*)AfxGetMainWnd())->currnetProRc); }void CDlgLayerTree::InitTreeStruct(CString rootName) { //AfxSetResourceHandle(((CDrawingFrame*)AfxGetMainWnd())->dllRc); CImageList m_FileViewImages = new CImageList(); bool asd = m_FileViewImages.Create(IDB_FILE_VIEW, 16, 16, RGB(255, 0, 0)); int n = m_FileViewImages.GetImageCount(); m_layerTree.SetImageList(m_FileViewImages, TVSIL_NORMAL); //AfxSetResourceHandle(((CDrawingFrame*)AfxGetMainWnd())->currnetProRc); }以上两种写法看似没区别,但是实际差距很大
void CRHTree::DrawItemImage(CDC* pDC, CRect rCell, HTREEITEM hItem, BOOL bSelected) // Draws an item's image from the tree's list { CImageList* pIL = this->GetImageList(TVSIL_NORMAL); int n = pIL->GetImageCount(); }按照第一种写法,则
Get出来的imagelist就是充实的,n是有值的;如果按照第二种写法,看似没影响,
Get出来的地址也是相同的,根本找不到问题,但是n却是0,表明该list根本不存在,已经被释放掉了所以这个bug十分隐晦,如果经验不足很容易掉进坑里,尤其是这种错误有时候不会报错,也不会影响程序运行,根本无法定位
- 2023/11/16 第二次踩坑,原因是读取图片数据用的是临时变量,但是传递的是指针;如果使用指针经常会忘掉,那不如直接对象赋值,这样会拷贝一份,不会释放数据
-
资源文件要对应,多个项目尤其是设计资源文件转化的时候,如果资源调用与预期设计不符,那一定是那个地方资源文件切换有问题,一点点定位就好**(着重找资源文件使用的函数)**
FindResource(); LockResource();- 找相应的接收变量,看变量的赋值过程
- 找赋值最初是如何调用的资源ID
- 根据ID确定该使用哪个rc句柄,进行切换
HINSTANCE save_hInstance = AfxGetResourceHandle(); HINSTANCE hDll = LoadLibrary(_T("rc.dll")); AfxSetResourceHandle(dllRc); HINSTANCE currnetProRc = save_hInstance; HINSTANCE dllRc = hDll; AfxSetResourceHandle(currnetProRc); -
控件无法显示完全:onInitDialog没有调用父类方法
BOOL CDrawing::OnInitDialog() { CDialog::OnInitDialog(); } -
OnInitDialog 不支持的参数
- rc文件中的资源和DoDataExchange中的资源出现了链接问题
- rc Dll 没有编译或者编译的位置不对【x64环境的dll编译到了32】
-
无法解析的外部符号
- class _DACSDATA_EXT_CLASS ClassObj: public CDacsObj
-
区分
for (auto polygon : polygonList) { polygon.x = 1; } for (int polI = 0; polI < polygonList.size(); polI++) { polygonList[i].x = 1; }两种有什么区别:第一种会保护原数据,第二种会污染原数据,等价起来应写为
for (int polI = 0; polI < polygonList.size(); polI++) { auto polygon = polygonList[i]; polygon.x = 1; }这是个很容易忽视的小地方,容易想当然,浪费了好几个小时
-
找坑
int hidI = 0; for(auto hidePol : hidePolygonList) { hidI++; for (int ptI = 0; ptI < hidePol.points.size(); ptI++) { Hge::Vec2f tempVec; tempVec.y() = (hidePol.points[ptI].y() + 1) * pViewReport->height() / 2; tempVec.x() = (hidePol.points[ptI].x() + 1) * pViewReport->width() / 2; tempVec.y() = pViewReport->height() - tempVec.y(); hidePol.points[ptI] = tempVec; } if (polygonOverAllIdx[polI] < hidepolygonOverAllIdx[hidI] || insideH == true) break; if (polygonOverAllIdx[polI] < hidepolygonValuable[hidI] || hidepolygonValuable[hidI] == -1) { insideH = pointInPolygon(pos, hidePol.points, polygonBoxMax[polI], polygonBoxMin[polI], hidePol.wvp, pViewReport->width(), pViewReport->height() ); if (insideH == true) { //break; } }找到了吗,坑在于hidI在后面使用了,但是在进入循环的时候直接被更新掉,所以对不起来,就会有bug,浪费一个小时
-
是否保存修改
AfxFormatString1(prompt, AFX_IDP_ASK_TO_SAVE, name);
GDI Drawing
首先要明白,GDI和GDI+不是一个东西,GDI是C++最基础的画图工具,实现比较简单,而GDI+是相对复杂的画图体系,与OpenGL以及DirectX类似
分别讲一下GDI和GDI+画图的步骤
GDI
-
代码
void CMyView::DrawWithGDI(CDC* pDC, CPoint point) { HDC pHDC = pDC->GetSafeHdc(); HPEN pen; pen = CreatePen(PS_DOT, penWidth, penColor); SelectObject(pHDC, pen); MoveToEx(pHDC, point.x, point.y, NULL); LineTo(pHDC, lastMousePos.x, lastMousePos.y); } -
画图的时候会需要一个绘图上下文
HDC:标榜的是设备环境,可以理解为往哪里画CDC是MFC特有的上下文信息,可以通过GetDC获取,或者通过消息函数自带的参数,而通过GetSafeHdc可以获取到HDC
-
GDI画图需要的画笔为HPEN,当然还有其他类型比如Brush等等,使用之前需要先创建参数
pen画出线的类型- 粗细
- 颜色(
COLORREF)
-
SelectObject:为特定的HDC选取所使用的obj,这里选取的是pen,表示之后画图等操作使用的是pen- 如果之后需要切换操作,比如换成笔刷,需要重新选择
obj - 后续的操作不会以
pen作为参数,而是以HDC作为参数,需要注意,所以使用之前需要先选择obj
- 如果之后需要切换操作,比如换成笔刷,需要重新选择
-
MoveToEX:将操作移动到特定位置 -
LineTo:画一条线
GDI画线的时候用的是先移动画笔,然后落笔,而GDI+中可以直接定位两点连线
GDI Plus
-
demo代码#include <gdiplus.h> { Gdiplus::GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; // Initialize GDI+. GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); HDC hdc; Gdiplus::Graphics* graphics; Gdiplus::Pen* pen; hdc = pDC->GetSafeHdc(); graphics = new Gdiplus::Graphics(hdc); pen = new Gdiplus::Pen(Gdiplus::Color(GetRValue(penColor), GetGValue(penColor), GetBValue(penColor)), penWidth); pen->SetStartCap(Gdiplus::LineCapRound); pen->SetEndCap(Gdiplus::LineCapRound); graphics->DrawLine(pen, lastMousePos.x, lastMousePos.y, point.x, point.y); delete graphics; delete pen; } -
使用
GDI+之前,首先需要初始化GDI+环境-
Gdiplus::GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; // Initialize GDI+. GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); -
具体干啥不太明白,但是加上就行了
-
-
与
GDI类似,也需要一个Pen,区别是GDI+用的是Graphics进行操作 -
Graphics:创建的时候将hdc作为参数,保证绘图的窗口能显示 -
Pen:正常创建 -
pen->SetStartCap(Gdiplus::LineCapRound);:设置开始和结束的类型- 在
GDI中,默认划线图元是一个小圆点,这样画出来的线就是丰满的 - 但是
GDI+中,默认是一条垂直的线,这样画的时候,就会次次毛毛的,看着不好看 - 所以需要换成点,其实参数中还有其他数值,比如方块,三角形等等,效果都不如圆点好,因此这里换成圆点
- 在
-
正常的
DrawLine,其他的绘图方法在MS的官方文档中有,写的还是很详细的,直接套用就可以
GDI+相对于GDI中很牛的一个改变,就是GDI+可以存储路径,并且进行读取其实路径存取有很多方法,最笨的就是将所有画出来的点都存到
vec中,但是这样会很吃内存,而且效率会比较低目前没想到什么好方法,而
GDI+自带的方法目前也只是存取显示可以正常,关闭再打开显示的时候总会有一点小毛病,看看之后如果不行就得换到OpenGL
文件存取
-
Metafile metafile(L"SampleMetafile.emf", hdc); { Graphics graphics(&metafile); Pen greenPen(Color(255, 0, 255, 0)); SolidBrush solidBrush(Color(255, 0, 0, 255)); // Add a rectangle and an ellipse to the metafile. graphics.DrawRectangle(&greenPen, Rect(50, 10, 25, 75)); graphics.DrawEllipse(&greenPen, Rect(100, 10, 25, 75)); // Add an ellipse (drawn with antialiasing) to the metafile. graphics.SetSmoothingMode(SmoothingModeHighQuality); graphics.DrawEllipse(&greenPen, Rect(150, 10, 25, 75)); // Add some text (drawn with antialiasing) to the metafile. FontFamily fontFamily(L"Arial"); Font font(&fontFamily, 24, FontStyleRegular, UnitPixel); graphics.SetTextRenderingHint(TextRenderingHintAntiAlias); graphics.RotateTransform(30.0f); graphics.DrawString(L"Smooth Text", 11, &font, PointF(50.0f, 50.0f), &solidBrush); } // End of recording metafile. // Play back the metafile. Graphics playbackGraphics(hdc); playbackGraphics.DrawImage(&metafile, 200, 100); -
代码是在官方文档中抄的,没什么注意点挺好理解的
- 想要实现存储的目的,一定要给文件一个结束信号,比如官方文档中,将
graphics定义到大括号内部,在大括号结束的时候,graphics的生命周期默认结束,此时会识别到Recording结束,这样才能正常存储 - 如果不加大括号,那可以用指针,在画完的时候手动
delete或者release,总之要明确告诉文件,Recording的行为结束了 - 否则存的就是空的
- 想要实现存储的目的,一定要给文件一个结束信号,比如官方文档中,将
-
有几个问题没解决,很烦
-
文件存储完毕,关掉程序再打开,读取会显示不出来
可能与
DrawImage的位置有关系? -
有时候能读取出来,但是读取出来的只是一道小杠杠,画了,但是画的不对
-
边画边存,比如这个
demo,画的图形是正常的,但是用Line,定位之前和现在的pos,画出来的就很奇怪,会缩在一团,而且有时候会突然蹦出好长一条线,尽可能填满之后,发现缩在了view中间的一块方形区域,很纳闷
-
字符转换
在MFC(Microsoft Foundation Classes)中,CString 类提供了两种不同的字符集编码版本:CStringA(ANSI 字符集)和 CStringW(Unicode 字符集)。函数 CT2A 是用于将 CString 转换为 ANSI 字符集的 C 字符串(const char*)的宏。
如果你使用 CT2A(cstring.GetBuffer(0)) 进行转换,它的工作原理是将 CString 转换为 const char*,然后再构造一个 std::string。这个方法使用了 CT2A 宏来执行 ANSI 字符集的转换。
但是,cstring.GetBuffer(0) 返回的是 TCHAR* 指针,它的类型可能是 char* 或 wchar_t*,具体取决于你的项目的字符集设置。如果项目使用 Unicode 字符集,GetBuffer(0) 返回的是 wchar_t*,而不是 char*。因此,直接将其赋给 std::string 可能导致编译器错误,因为 std::string 需要 char* 类型。
序列化反序列化
-
cstring不可直接序列化反序列化 -
string不可直接序列化反序列化 -
序列化和反序列化的数据是可以直接转为const char*的数据,而cstring和string都不符合,需要转换一下
cstring.getbufferstring.c_str()&string[0]
-
序列化总是失败的原因:在对
class序列化的时候,每次都直接关掉了;如果是一系列的序列化过程,需要保持文件的打开状态,不能直接close -
尝试一下保存
class创建时需要的信息,尝试重建 -
结构体中有
cstring时,不能整体序列化,会将cstring置为空
控件
RibbonBar
-
快速启动工具栏
GetQuickAccessCommands
右键->添加到快速启动工具栏
m_wndRibbonBar.SetQuickAccessCommands(MainlstCommands);
{
MainlstCommands.AddTail(ID_FILE_OPEN); MainlstCommands.AddTail(ID_OPTION); MainlstCommands.AddTail(ID_APP_ABOUT);}
m_wndRibbonBar.SetQuickAccessCommands(MainlstCommands);
控件位置获取
// 获取鼠标的屏幕坐标
CPoint ptClient;
::GetCursorPos(&ptClient);
m_ProjectList.ScreenToClient(&ptClient); // 转为控件的局部坐标
// 获取列表控件的客户区矩形大小
CRect rectList;
m_ProjectList.GetClientRect(&rectList);
// 检查点击位置是否在列表控件的客户区域内
if (rectList.PtInRect(ptClient))
{}
这样写是没问题的
坐标函数前面加上控价是可以对于控件进行操作的;
但是之前在子对话框的时候使用这种思路出了问题,猜测可能是和框架以及父对话框设置有关系,还需继续考察
对话框
-
对话框居中显示
onInitDialog()CRect rect; GetWindowRect(&rect); int nX = ::GetSystemMetrics(SM_CXFULLSCREEN); int nY = ::GetSystemMetrics(SM_CYFULLSCREEN); int nW = rect.Width(); int nH = rect.Height(); MoveWindow((nX - nW) / 2, (nY - nH) / 2, nW, nH, TRUE); //显示居中 -
对话框相对位置定位
CRect rect; GetDlgItem(IDC_BUTTON_PANEL_CON)->GetWindowRect(&rect); ScreenToClient(&rect); switch (CurSel) { case CONNECT_COM: { m_pnow->connectType = CONNECT_COM; m_dlgPort = new CDlgCommunicateSetting(); m_dlgPort->Create(IDD_DLG_COMMUNICATE_SETTING_COM, this); m_dlgPort->InitComboCtrl(m_pnow); m_dlgPort->SetWindowPos(NULL, rect.left, rect.top, 0, 0, SWP_NOSIZE); m_dlgPort->ShowWindow(SW_SHOW); break; } case CONNECT_BLE: { m_pnow->connectType = CONNECT_BLE; m_dlgBle = new CDlgCommunicateSettingBle(); m_dlgBle->Create(IDD_DLG_COMMUNICATE_SETTING_BLE, this); m_dlgBle->SetWindowPos(NULL, rect.left, rect.top, 0, 0, SWP_NOSIZE); m_dlgBle->ShowWindow(SW_SHOW); break; } }// switch
响应函数
BEGIN_MESSAGE_MAP(CDlg, CDialogEx)
END_MESSAGE_MAP()
-
任意右键响应、通知
ON_NOTIFY(NM_RCLICK, IDC_LIST_PRPJECTLIST, &CPointCloudManagerMFCDlg::OnProjectListRbtnUp)
-
按钮、菜单响应
ON_COMMAND(IDC_LIST_PRPJECTLIST, &CPointCloudManagerMFCDlg::OnAddProject)
-
独立响应
ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_SIZE()
ListCtrl
-
初始化
CWnd* pWnd = GetDlgItem(IDC_LIST_PRPJECTLIST); CRect rect; m_ProjectList.GetClientRect(&rect); int width = rect.Width(); CFont font; font.CreatePointFont(120, _T("黑体")); // 120 corresponds to 12 points long dwStyle = m_ProjectList.GetExtendedStyle(); dwStyle |= LVS_EX_FULLROWSELECT; //如果您想要在 List Control 中启用完整行选择,您可以使用 LVS_EX_FULLROWSELECT 扩展样式。这个样式允许用户单击任何一行的任意位置来选择整行。 //dwStyle |= LVS_EX_GRIDLINES; m_ProjectList.SetExtendedStyle(dwStyle); m_ProjectList.SetFont(&font); -
添加元素
m_ProjectList.InsertColumn(0, _T("项目列表"), 0, width); m_ProjectList.InsertItem(0, L"项目1");// 假设你有一个 CListCtrl 控件的成员变量 m_listCtrl // 控件的风格需要设置为 LVS_REPORT,两列的标识符分别为 0 和 1 // 在 OnInitDialog 或其他适当的位置初始化列表控件和列 m_listCtrl.InsertColumn(0, _T("Column 1"), LVCFMT_LEFT, 100); m_listCtrl.InsertColumn(1, _T("Column 2"), LVCFMT_LEFT, 100); // 插入一行数据 int nIndex = m_listCtrl.InsertItem(0, _T("Item 1")); // 设置第一列的文本 m_listCtrl.SetItemText(nIndex, 0, _T("Data for Column 1")); // 实际会和上一句重复,这里是为了整体 // 设置第二列的文本 m_listCtrl.SetItemText(nIndex, 1, _T("Data for Column 2")); -
添加完元素可能会出现奇怪的布局,那是listctrl设置不对,在vs中,listCtrl的视图属性,进行调整即可
-
如何在对话框中,给单独控件加鼠标响应
ON_NOTIFY(NM_RCLICK, IDC_LIST_PRPJECTLIST, &CPointCloudManagerMFCDlg::OnProjectListRbtnUp)鼠标响应写法,可以看一下ON_NOTIFY需要的函数类型,如果对不上则会报错
-
List,主动检测元素 以上一条右键响应为例子,检测是否存在条目
void CPointCloudManagerMFCDlg::OnProjectListRbtnUp(NMHDR* pNMHDR, LRESULT* pResult) { CPoint pt; GetCursorPos(&pt); m_ProjectList.ScreenToClient(&pt); int item = m_ProjectList.HitTest(pt); if (item == -1) { AfxMessageBox(L"空白区域"); } else { AfxMessageBox(L"第 item 条"); // 0开始 } } -
分组
如何添加分组
// 按顺序添加分组 LVGROUP group1, group2; ZeroMemory(&group2, sizeof(LVGROUP)); group2.cbSize = sizeof(LVGROUP); group2.mask = LVGF_HEADER | LVGF_GROUPID; group2.pszHeader = L"分割点云"; group2.iGroupId = 1; m_listCtrl.InsertGroup(0, &group2); ZeroMemory(&group1, sizeof(LVGROUP)); group1.cbSize = sizeof(LVGROUP); group1.mask = LVGF_HEADER | LVGF_GROUPID; group1.pszHeader = L"原始点云"; group1.iGroupId = 0; m_listCtrl.InsertGroup(0, &group1);如何对分组添加元素
int nItem = m_listCtrl.GetItemCount(); LVITEM lvItem; ZeroMemory(&lvItem, sizeof(LVITEM)); lvItem.mask = LVIF_TEXT | LVIF_GROUPID | LVIF_IMAGE; lvItem.iImage = 0; lvItem.iItem = nItem; lvItem.iGroupId = 0; lvItem.pszText = const_cast<LPTSTR>(static_cast<LPCTSTR>(strLastPart)); m_listCtrl.InsertItem(&lvItem); CString* pStrPath = new CString(subdirectory); m_listCtrl.SetItemData(nItem, (DWORD_PTR)pStrPath); -
获取选中行
CListCtrl* list = (CListCtrl*)_Ctrl; CString str; POSITION pos = list->GetFirstSelectedItemPosition(); //pos选中的首行位置 if (pos == NULL) { return; } while (pos) //如果你选择多行 { int nIdx = -1; nIdx = list->GetNextSelectedItem(pos); if (nIdx >= 0) { str = list->GetItemText(nIdx - preN, 0); AfxMessageBox(str); } }这里注意,如果是多选删除的话,删除条目时候的下标是选之前的下标,但是每次删除之后,其实列表是直接变化了的
std::vector<int> indList; while (pos) //如果你选择多行 { int nIdx = -1; nIdx = list->GetNextSelectedItem(pos); if (nIdx >= 0 && nIdx < list->GetItemCount()) { indList.push_back(nIdx); } } int preN = 0; for (int ind : indList) { list->DeleteItem(ind - preN ++); // 消除影响 // 也不能放到pos里后面,因为删除项也会影响pos,所以需要将下标全都获取完,再进行删除 } -
获取选中组
int GetGroupIDForItem(CListCtrl& listCtrl, int nIndex) { // 确保索引在有效范围内 if (nIndex >= 0 && nIndex < listCtrl.GetItemCount()) { LVITEM lvItem; ZeroMemory(&lvItem, sizeof(LVITEM)); lvItem.iItem = nIndex; lvItem.mask = LVIF_GROUPID; if (listCtrl.GetItem(&lvItem)) { // 返回项的组 ID return lvItem.iGroupId; } } // 如果未找到项或者无法获取组信息,则返回 -1 表示失败 return -1; }listCtrl.GetItem(&lvItem)有点类似索引搜索,而lvItem.mask = LVIF_GROUPID表示只对组这个属性感兴趣
-
复选框
直接看代码吧
// 启用 List Control 的复选框功能 m_listCtrl.SetExtendedStyle(m_listCtrl.GetExtendedStyle() | LVS_EX_CHECKBOXES); // 在 List Control 中添加两列 m_listCtrl.InsertColumn(0, _T("Name"), LVCFMT_LEFT, 200); // 添加第一列 m_listCtrl.InsertColumn(1, _T("Checkbox"), LVCFMT_LEFT, 100); // 添加复选框列 // 向 List Control 中添加几行数据,并设置复选框初始状态为 true for (int i = 0; i < 5; i++) { CString strName; strName.Format(_T("Item %d"), i + 1); int nIndex = m_listCtrl.InsertItem(i, strName); m_listCtrl.SetCheck(nIndex, TRUE); // 将复选框默认设置为选中状态 // 将复选框显示在第二列 m_listCtrl.SetItemText(nIndex, 1, _T("")); // 设置第二列的文本为空 } -
重绘表头颜色 Gitee仓库 - MFC相关资料
绘制消息
NM_CUSTOMDRAW是 Windows 消息中的一种通知消息,用于自定义绘制(Custom Draw)控件的外观。在 MFC 中,它通常与CListCtrl、CTreeCtrl、CListCtrl等控件结合使用,用于自定义绘制列表项、树节点等元素。当你在
CListCtrl或者其他控件中启用了自定义绘制功能后,当控件需要进行绘制时,会发送NM_CUSTOMDRAW消息给父窗口(即控件的所有者窗口),然后父窗口可以根据需要进行绘制操作。
注意:listCtrlEx中重绘的只能是列表项,如果想重绘表头,需要自定义HeaderCtrl,并对list的Header进行重赋值
要将自定义的表头控件赋值给当前列表控件,你需要做两件事:
- 创建自定义的表头控件类(例如
CMyHeaderCtrl),并在其中实现你自定义的绘制逻辑。- 在你的列表控件类中,使用自定义的表头控件类,并在
PreSubclassWindow函数中将其与列表控件的表头控件关联起来。
菜单
-
菜单弹出来只有一条缝
CreatePopupMenu用于创建弹出菜单,而CreateMenu用于创建常规菜单。这两个函数的使用取决于你的应用程序的具体需求和用户交互设计。所以右键的时候应该是有
Popupmenu,否则出来的就只是一条缝CMenu menu1; menu1.CreatePopupMenu(); //动态创建弹出式菜单对象 menu1.AppendMenu(MF_STRING, ID_ADD_PROJECT, L"菜单项1"); menu1.AppendMenu(MF_STRING, ID_DELETE_PROJECT, L"菜单项2"); menu1.InsertMenu(2, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT)menuMain.m_hMenu, "子菜单"); //添加子菜单 CPoint pt; GetCursorPos(&pt); menu1.TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this); menu1.DestroyMenu();
Text
-
获取内容
m_EProjName.GetWindowText(pName);直接获取即可,输入内容这部分是自动执行的
Combobox
-
添加内容
m_combo.AddString(_T("小明")); m_combo.AddString(_T("小红")); m_combo.AddString(_T("小兰")); // 设置默认选择项 m_combo.SetCurSel(0);注意,AddString会自动排序,如果不希望排序
m_CobOpenMode.InsertString(0, L"Modeqweqweqwe"); m_CobOpenMode.InsertString(1, L"Mode2"); m_CobOpenMode.InsertString(2, L"Mode3"); m_CobOpenMode.SetCurSel(0); -
获取内容
// 在需要获取 ComboBox 内容的地方使用如下代码: int nIndex = m_combo.GetCurSel(); // 获取当前选中项的索引 if (nIndex != CB_ERR) { CString strText; m_combo.GetLBText(nIndex, strText); // 获取当前选中项的文本内容 // 在这里使用 strText,它包含了当前选中项的文本内容 } -
点击不展开
打开exe
-
简单
demovoid CMainWindow::OnOpenExternalApp() { // 要打开的外部应用程序的路径 CString strAppPath = _T("C:\\Path\\To\\YourApp.exe"); // 传入的参数 CString strArguments = _T("/your_argument_here"); // 使用ShellExecute启动外部应用程序 ShellExecute(NULL, _T("open"), strAppPath, strArguments, NULL, SW_SHOWNORMAL); }
选择文件、文件夹
-
选择单个文件
void CDlgImportPointCloud::OnSelectPath() { CFileDialog dlg(TRUE, _T("*.*"), _T("points"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _g(_T("xyz File(*.xyz)|*.xyz|" "pts File(*.pts)|*.pts|" "ptx File(*.ptx)|*.ptx|" "las File(*.las)|*.las|" "laz File(*.laz)|*.laz|" "stl File(*.stl)|*.stl|" "asc File(*.asc)|*.asc|" "txt File(*.txt)|*.txt|")));//FALSE表示为“另存为”对话框,否则为“打开”对话框 if (dlg.DoModal() == IDOK) { CString strFile = dlg.GetPathName(); CString strTitle = dlg.GetFileTitle(); m_FilePath.SetWindowText(strFile); m_FileName.SetWindowText(strTitle); } } -
选择目录
WCHAR szPath[MAX_PATH]; //存放选择的目录路径 CString str; ZeroMemory(szPath, sizeof(szPath)); BROWSEINFO bi; bi.hwndOwner = m_hWnd; bi.pidlRoot = NULL; bi.pszDisplayName = szPath; bi.lpszTitle = L"请选择处理结果存储路径"; bi.ulFlags = 0; bi.lpfn = NULL; bi.lParam = 0; bi.iImage = 0; //弹出选择目录对话框 LPITEMIDLIST lp = SHBrowseForFolder(&bi); if (lp && SHGetPathFromIDList(lp, szPath)) { str.Format(L"%s", szPath); SetDlgItemText(IDC_EDIT2, str); AfxMessageBox(str); } else { AfxMessageBox(L"wrong"); }
遍历文件、文件夹
-
遍历当前目录下的文件夹,并判断文件夹中是否有
json文件#include <iostream> #include <filesystem> #include <vector> namespace fs = std::tr2::sys; bool hasJsonFile(const fs::path& folderPath) { for (const auto& entry : fs::directory_iterator(folderPath)) { if (fs::is_regular_file(entry.status()) && entry.path().extension() == ".json") { return true; // 发现JSON文件 } } return false; // 没有找到JSON文件 } int main() { std::string currentPath = "."; try { for (const auto& entry : fs::directory_iterator(currentPath)) { if (fs::is_directory(entry.status())) { std::cout << "Folder: " << entry.path().string() << std::endl; if (hasJsonFile(entry.path())) { std::cout << " - Contains JSON file(s)" << std::endl; } else { std::cout << " - No JSON file found" << std::endl; } } } } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; } return 0; }
获取当前路径
// 获取可执行文件路径
TCHAR buffer[MAX_PATH];
::GetModuleFileName(NULL, buffer, MAX_PATH);
// 从路径中提取出所在文件夹
PathRemoveFileSpec(buffer);
// 将路径存储在CString对象中
CString currentPath(buffer);
std::string dpPath = CW2A(currentPath) + "\\File.dp";
std::ifstream ifs(dpPath);
重绘以及原理
-
重绘的具体操作,以为dlg为例,就是重写onPaint函数,可以简单的实现背景的替换或者添加花纹等等,像圆角和透明度等操作需要在外部实现
一个简单的小demo
void CDlgLayerTree::OnPaint() { CPaintDC dc(this); // 用于绘制的设备上下文 // 获取对话框客户区的矩形区域 CRect rect; GetClientRect(&rect); // 创建一个内存设备上下文 CDC memDC; memDC.CreateCompatibleDC(&dc); // 创建一个位图与内存设备上下文相关联 CBitmap bitmap; bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height()); memDC.SelectObject(&bitmap); // 在内存设备上下文中绘制背景(示例中使用红色背景,你可以根据需要自定义) memDC.FillSolidRect(rect, RGB(200, 255, 255)); // 在内存设备上下文中绘制聊天内容(示例中绘制一行文本,你可以根据需要自定义) CString strMessage = _T("Hello, World!"); // 这里可以是你的聊天内容 memDC.SetTextColor(RGB(255, 255, 255)); // 设置文本颜色为白色 memDC.SetBkMode(TRANSPARENT); // 设置背景透明 memDC.DrawText(strMessage, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); // 绘制文本 // 将内存设备上下文的内容绘制到窗口设备上下文中 dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); CDialog::OnPaint(); }


2606

被折叠的 条评论
为什么被折叠?



