MFC 小记

1.CtreeControl  树控件代码设置选择的节点高亮问题

     由于树控件节点被点击过之后,节点会保持高亮,同时不影响节点切换后的高亮,那么在设置选中节点前模拟鼠标点击一下:
         m_treeCameras->SendMessage(WM_LBUTTONDOWN,   0, MAKELONG(0, 200));
        m_treeCameras->SendMessage(WM_LBUTTONUP,  0, MAKELONG(0, 200));
        m_treeCameras->SelectItem(item);

2.CToolTipCtrl 提示控件与进度条绑定,有时无法正常显示提示,如焦点切换,内部子窗体相互切换,操作后需要通过移动窗体才能够正常显示绑定在进度条的提示。
    通过多次实验,可以通过在需要首次提示前,使用 UpdateWindow 更新一下最上层的对话框窗体来触发提示控件的信息更新提示的正常显示。

        eg:

          if (!this->IsTopParentActive() || this->m_hWnd != ::GetFocus())
            {
                 this->SetFocus();
                this->UpdateWindow(); 
            }

3.拖动窗体到屏幕外,窗体区域重绘按钮消失。

      由于窗体拖动到屏幕边缘后会触发主窗体的OnPaint事件,在主窗体的OnPain未处理子对话框和按钮的绘制事件导致子对话框和按钮消失。因UpdateWindow 会触发子窗体及其子按钮的重绘事件,所有在主窗体的OnPaint事件里添加UpdateWindow即可。

    eg: 
void CplayPanel::OnPaint()
{
    CPaintDC dc(this); // device context for painting
                       // TODO: 在此处添加消息处理程序代码
                       // 不为绘图消息调用 __super::OnPaint() 

       UpdateWindow();
}

4.picture绘制视频图片画面卡顿。

   由于准备的图片数量与绘制的数量的时间差异以及绘制时间的不可确定性,导致卡顿。为此可以开两个线程,一个准备图片,一个绘制图片。为避免准备不充分引起卡顿,准备图片要比绘制的图片多一张。虽然普遍的帧率是1s/24个画面,但考虑到绘制性能可以使用1s/16个画面。

   eg:

    if (m_sleepTime == 0) //每个线程的sleep会有差异需要重新计算一下
    { 
        m_sleepTime = getSecTick(17);  //绘制改为 16
    }
    if (throwTime(m_sleepTime, 17))  //绘制改为 16
        return;

   //处理其他操作

   计时器类:


class Times
{
public: 
	int getSecTick(int count);  //每个线程的sleep会有差异需要重新计算一下 
	bool throwTime(int secTick = 40, int count = 24);
private:
	DWORD m_tickLastT = 0;
	DWORD m_tickEndT = 0;
	DWORD m_tickStartT = 0;
	int i = 0;
	int ticks = 100;
};


int Times::getSecTick(int count)
{ 
	if (count < 1)
		return 0; 
	int tick = 1000 / count;
	auto tmpTick = GetTickCount(); 
	Sleep(tick);
	auto tmpTick2 = GetTickCount(); 
	return tmpTick2 - tmpTick;
}

bool Times::throwTime(int tick, int count) 
{
	auto tmpTick = GetTickCount();
	if (tmpTick > m_tickEndT)
	{ 
		m_tickStartT = tmpTick;
		m_tickEndT = tmpTick + 1000;
		i = 0;
	}
	else {
		if (tmpTick < m_tickLastT && m_tickStartT + i * tick >= m_tickLastT)
			return true;
	}
	m_tickLastT = tmpTick + tick;
	i++;
	if (count >0 && i > count)
	{
		CString fmt;
		fmt.Format("time to show:%d--%d-%d-%d\n", i, m_tickStartT, tmpTick, m_tickEndT);
		OutputDebugString(fmt);
		return true;
	}
	return false;
}

5.创建对话框时如何隐藏

   BOOL CxxxxApp::InitInstance() 里弹出的对话框无法做到开启即隐藏,可以将Dlg主类使用非模态的方式进行创建,同时创建一个新的对话框(X),作用时使程序不会直接退出,注意m_pMainWnd要用(X)赋值,否则Dlg主类即使创建成功也无法操作。

   eg:

     CDemoDlg* dlg =  new CDemoDlg; 
       dlg->Create(IDD_DLG);
      dlg->ShowWindow(SW_HIDE);
    CLoginDlg login;
    m_pMainWnd = &login;
    INT_PTR nResponse = login.DoModal();

6.两个进程窗体设置父子关系

   要使得A窗体在B窗体的上方,可以应用MFC的窗体父子关系的设置应用。

   eg:

//引用参考
//--https://blog.csdn.net/safedebug/article/details/19122561  --获取进程句柄
//--https://blog.csdn.net/dieruowan6112/article/details/101202646   --设置父子关系
//--http://www.cppblog.com/gohan/archive/2007/06/03/25361.html
HWND GetWindowHandleByPID(DWORD dwProcessID)
{
	HWND h = GetTopWindow(0);
	while (h)
	{
		DWORD pid = 0;
		DWORD dwTheardId = GetWindowThreadProcessId(h, &pid);
		if (dwTheardId != 0)
		{
			if (pid == dwProcessID/*your process id*/)
			{
				// here h is the handle to the window

				if (GetTopWindow(h))
				{
					return h;
				}
				// return h;	
			}
		}
		h = ::GetNextWindow(h, GW_HWNDNEXT);
	}
	return NULL;
}

--获取进程pid  auto id = _getpid();  
auto parent = GetWindowHandleByPID(59912);
if(parent)
	SetWindowLong(m_hWnd, GWL_HWNDPARENT, (long)parent);
x64--使用
if(parent)
	SetWindowLongPtr(_this, GWLP_HWNDPARENT, (long)parent);
    
   

   GetWindowHandleByPID(59912); 获取的handle 有时不一定是主窗体,可以将 外部的m_hWnd强制转换为long类型,通过socket等方式来进行信息交互。

    S: auto parent = (long)this->m_hWnd;   

   C: SetWindowLongPtr(this->m_hWnd, GWLP_HWNDPARENT, (long)parent);

7.将窗体置顶

        ShowWindow(SW_SHOW);   //--显示窗体
        CRect rect;
        GetWindowRect(rect);  //--获取窗体原有位置
        ::SetWindowPos(this->m_hWnd, HWND_TOPMOST, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOREDRAW | SWP_NOSIZE );  //  置顶
        UpdateWindow();   //-- 刷新窗体
        ::SetWindowPos(this->m_hWnd, HWND_NOTOPMOST, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOREDRAW | SWP_NOSIZE); //取消置顶

8.获取命令参数

    命令行启动mfc, 程序可以使用宏定义来得到传入的参数

   eg:


char *getParam(int argc, char *argv[], const char*Tag)
{
    for (auto i = 1; i < argc; i++) {
        if (strcmp(argv[i], Tag) == 0) {
            if (i + 1 < argc) {
                return argv[i + 1];
            }
            return NULL;
        }
    }
    return NULL;
} 

BOOL CxxApp::InitInstance()

{ 
    const char* ip = getParam(__argc, __argv, "-ip");
    OutputDebugString(ip);
}


  9.整个子窗体及按钮未显示

     窗体按钮有层叠关系,如若子窗体是非模态创建,那么有可能被上层的控件遮挡,如使用静态框作为分组边框,遮挡了后期创建的子对话框。

    可以通过设置相应的控件背景为透明背景,实现子对话框的正常显示。
   eg:

    

//--https://blog.csdn.net/p312011150/article/details/82909711



HBRUSH CSwtjAppmgrDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

	// TODO:  在此更改 DC 的任何特性

	// TODO:  如果默认的不是所需画笔,则返回另一个画笔
	if (nCtlColor == CTLCOLOR_STATIC)
	{
		pDC->SetBkMode(TRANSPARENT);
		return   (HBRUSH)::GetStockObject(NULL_BRUSH);
	}
	return hbr;
}

10.如何区分用户事件还是代码事件

    用户事件即外部事件,可以通过判断鼠标键盘的状态来对代码进行优化。

    eg:   if(GetAsyncKeyState(VK_LBUTTON))  return 1;

11.MFC 加载图片列表

     通过使用CImage + 列表控件实现图片列表的功能。细节上SetStretchBltMode处理失真问题。

     eg:


//--https://blog.csdn.net/tajon1226/article/details/106735640
CListCtrl m_iconList;
CImageList  m_ImageList;

void addImg(CString imgPath)
{
	CImage img,dest;
	CBitmap* bitmap;
	img.Load(imgPath);
	if (FALSE == img.IsNull())
	{
		dest.Create(128, 128, 32);

		HDC destDC = dest.GetDC();   //失真处理
		::SetStretchBltMode(destDC, HALFTONE);
		img.StretchBlt(destDC, 0, 0, 128, 128);
		dest.ReleaseDC();
		bitmap = CBitmap::FromHandle((dest.Detach()));
		m_ImageList.Add(bitmap, RGB(255, 255, 255)); 
	}
}
void imgList()
{ 
	m_ImageList.Create(128, 128, ILC_COLOR24 | ILC_MASK, 1, 0);  
	addImg("e:\\tmp\\ts.jpg"); 
    //融合图片
	CImage bk, fr;
	bk.Load("e:/tmp/ts.jpg");
	fr.Load("e:/tmp/ts.ico"); 
	auto L = (bk.GetWidth() - fr.GetWidth()) / 2;
	auto T = (bk.GetHeight() - fr.GetHeight()) / 2;
	RECT rcDest = { L,T, L +fr.GetWidth(), T+fr.GetWidth() };
	fr.Draw(bk.GetDC(), rcDest); 
	bk.Save("e:/tmp/tmp.jpg");
	bk.ReleaseDC();
	bk.Destroy();
	addImg("e:/tmp/tmp.jpg");
}
// CLFCdemoDlg 消息处理程序
BOOL CLFCdemoDlg::OnInitDialog()
{ 
	//MoveWindow(0, 0, 0, 0);
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。
	imgList();
	m_iconList.Create(WS_CHILD | WS_VISIBLE | LVS_ICON, CRect(), this, 10201);
	m_iconList.SetExtendedStyle(m_iconList.GetExtendedStyle() | LVS_EX_BORDERSELECT);
	m_iconList.SetImageList(&m_ImageList, LVSIL_NORMAL);
	m_iconList.SetIconSpacing(128, 128); //item间隔
	for (int i = 0; i < 2; i++)
	{
		m_iconList.InsertItem(i, "", i);
	}
	m_iconList.MoveWindow(CRect{ 0,0, 306, 400 });
  .....

12. 程序信息和图标的获取

  通过 GetFileAttributesEx 和 GetFileAttributes 可以获取程序的部分信息,若是图标信息需要注意

   hIcon是一个指针,对hIcon的使用会占用文件,使用时可以通过DestroyIcon来销毁。

  由于对一个shell函数可能会重复多次调用,因此在程序初始化时需要添加CoInitializeEx(NULL, COINIT_MULTITHREADED); 否则可能会调用失败SH失败

  另外,路径 需要使用 \xx\xx.xx   即 ‘\’ ,否则会失败。 可以使用 CString path.Replace("/", "\\"); 转换地址符号。

eg:


//--https://blog.csdn.net/huasonl88/article/details/819341
SHFILEINFO sfi = { 0 };
SHGetFileInfo(path, 0, &sfi, sizeof(sfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON);
DestroyIcon(sfi.hIcon); //--销毁

bool getFileAttributes(CString path, std::vector<CString>& vRst)
{ 
	WIN32_FILE_ATTRIBUTE_DATA fileInfo;
	if (!GetFileAttributesEx(path, GetFileExInfoStandard, &fileInfo))
		return false;
	int nPos = path.ReverseFind(_T('\\')) + 1;
	auto name = path.GetString()+nPos; 
	vRst.push_back(name);
	//--size
	ULONGLONG liFileSize;
	liFileSize = fileInfo.nFileSizeHigh;
	//高位移动32位
	liFileSize <<= sizeof(DWORD) * 8;
	liFileSize += fileInfo.nFileSizeLow;
	int G=0, M=0, K=0, T=0;
	int G_ = 1024 * 1024 * 1024;
	int M_ = 1024 * 1024;
	int K_ = 1024;
	T = liFileSize;
	CString fmt;
	CString rst;
	if (T > G_)
	{
		G = T / G_;
		T = T % G_;
		fmt.Format("%dG", G);
		rst += fmt;
	}
	if (T > M_)
	{
		M = T / M_;
		T = T % M_;
		fmt.Format("%dM", M);
		rst += fmt;
	}
	if (T > K_)
	{
		K = T / K_;
		T = T % K_;
		fmt.Format("%dK", K);
		rst += fmt;
	}
	else {
		fmt.Format("%db", K);
		rst += fmt;
	}
	vRst.push_back(rst);
	rst = ""; 
	//文件时间结构
	FILETIME ftLocal;
	//系统时间结构
	SYSTEMTIME st;
	//调整为系统所在时区的时间
	FileTimeToLocalFileTime(
		&fileInfo.ftLastWriteTime,
		&ftLocal
	);
	//将文件时间转换为SYSTEMTIME格式,便于显示。
	FileTimeToSystemTime(
		&ftLocal,
		&st
	);
	//显示时间信息字符串
	fmt.Format("%4d/%.2d/%#02d",
		st.wYear, st.wMonth, st.wDay);
	vRst.push_back(fmt);
	fmt.Format("%.2d:%.2d:%.2d",
		st.wHour, st.wMinute, st.wSecond);
	vRst.push_back(fmt);
	return true;
}

13.CFolderPickerDialog 文件夹对话框异常处理

    在线程调用CFolderPickerDialog 弹窗的对话框出错,变成了打开文件对话框。尝试发现在主类里的oninitdialg里面调用CFolderPickerDialog dlgFile;即可解决。

14.CFile  共享打开文件

//--MFC CFile类读写文件详解-软件开发-鸡啄米 (jizhuomi.com)

CFile file;
	file.Open(m_Path, 
		CFile::modeRead|
		CFile::shareDenyNone  |   //共享打开
		CFile::typeBinary); 

15.tree 遍历选中节点

void ckNode(CString txt)
{
	auto root = m_detail.GetRootItem(); 
	auto hitem = m_detail.GetNextItem(root, TVGN_CHILD);
	while (hitem)
	{ 
		auto dt = m_detail.GetItemText(hitem);
		if (dt.Find(txt) > -1)
		{
			m_detail.SelectItem(hitem);
			m_detail.Select(hitem, TVGN_DROPHILITE);
			break;
		}
		hitem = m_detail.GetNextItem(hitem, TVGN_NEXT);

	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值