我目前正在尝试使用基于对话框的GUI,同时尝试自定义它。我已经绘制了一些位图,我想用它作为按钮和dlg中的徽标。
我写了两个成员函数,一个用于将位图设置为CButton
,另一个用于将位图设置为CStatic
s。实际上,当按下按钮时,它们都正在工作。但是,在对话框初始化期间,只有用于设置CButton
的成员函数才能正常工作。我的CStatic
被某种方式覆盖了。
这在OnInitDialog()
void CMyDlg::setBitmapAsButton(std::string file, CButton &Button, int xPos, int yPos)
{
CImage imgFromFile;
CBitmap bitmap;
std::wstring wfile(file.begin(), file.end());
HRESULT ret = imgFromFile.Load(wfile.c_str());
int width = imgFromFile.GetWidth();
int height = imgFromFile.GetHeight();
bitmap.Attach(imgFromFile.Detach());
Button.Create(_T("My button"), WS_CHILD | WS_VISIBLE | BS_BITMAP,
CRect(xPos, yPos, xPos + width, yPos + height), this, 1);
Button.SetBitmap(bitmap);
}
在OnInitDialog()
中调用时,这不起作用:
void CVisTrayDlg::setBitmapAsStatic(std::string file, CStatic &Static, int xPos, int yPos)
{
CImage imgFromFile;
CBitmap bitmap;
std::wstring wfile(file.begin(), file.end());
HRESULT ret = imgFromFile.Load(wfile.c_str());
int width = imgFromFile.GetWidth();
int height = imgFromFile.GetHeight();
bitmap.Attach(imgFromFile.Detach());
Static.Create(_T("My Static"), WS_CHILD | WS_VISIBLE | SS_BITMAP,
CRect(xPos, yPos, xPos + width, yPos + height), this);
Static.SetBitmap(bitmap);
}
CButton
和CStatic
控件被声明为CMyDlg
的成员。
关于如何避免这种行为的任何想法?或者是否有更好的方法将位图放在对话框中? (我尝试了CImage::BitBlt()
,但也以某种方式被覆盖了。)
答案 0 :(得分:4)
问题是所有权问题:在调用SetBitmap
后,单个位图实例有2个所有者,CBitmap
对象以及CButton
/ {{1} } 1)控制。当CStatic
对象超出范围时,它会销毁单个实例,使控件留下无效位图的句柄。
标准解决方案是通过在将CBitmap
关闭到按钮或静态控件之前调用CImage::Detach()来明确传递所有权:
HBITMAP
关于实施的一些注释:
void CVisTrayDlg::setBitmapAsStatic(std::string file, CStatic &Static, int xPos, int yPos)
{
CImage bitmap;
// Convert from ANSI codepage to UTF-16
CStringW wfile(file.c_str());
// Error handling needed. Use the SUCCEEDED() or FAILED() macros here:
HRESULT ret = bitmap.Load(wfile);
int width = bitmap.GetWidth();
int height = bitmap.GetHeight();
Static.Create(_T("My Static"), WS_CHILD | WS_VISIBLE | SS_BITMAP,
CRect(xPos, yPos, xPos + width, yPos + height), this);
// Pass ownership from CImage to CStatic:
HBITMAP hbmOld = Static.SetBitmap(bitmap.Detach());
// SetBitmap() passes ownership of the previous image (if set).
// We are responsible for cleanup:
if (hbmOld != nullptr ) {
::DeleteObject(hbmOld);
}
}
到string
的转换将失败。我更改了代码以处理ANSI代码页编码,但这可能不是源代码编码。您可能需要更改此内容。 2) wstring
转换为CImage
。 CBitmap
同时拥有CImage
成员和operator HBITMAP()
成员,这就是我们真正需要的所有成员。Detach()
的调用返回先前关联的位图的句柄。我们负责清理工作。<小时/> 1) 没有记录合同,按钮和静态控件的行为不同。如果它似乎适用于按钮,那么它只是巧合。这不是你应该依赖的任何东西。建议对按钮和静态控件使用相同的解决方案。
2) Windows始终使用UTF-16。通常最好使用UTF-16作为应用程序的内部字符编码。建议使用SetBitmap
。
答案 1 :(得分:2)
确保CBitmap
对象保留在内存中。因此,make是您的类的成员变量。对话框类的析构函数最终将调用位图对象的析构函数,因此无需担心资源/内存泄漏。
对于按钮,当您移动/恢复窗口时,它可能会显示其效果。我建议你在按钮中保留CBitmap
对象也在内存中。