有时候,我们需要在程序中调整界面,比如对话框上的控件位置和大小,以实现用户定义对话框。下面我实现一个例子,程序运行时,可以用鼠标调整对话框上的控件。
建立一个对话框程序,添加两个成员:
protected:
CRectTracker m_rtTracker;
CWnd* m_pSelectedCtrl; //初始化为NULL
再添加三个消息函数:
public:
afx_msg int OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
BOOL CEditableDlg::OnInitDialog()
{
CDialog::OnInitDialog();
......
// TODO: 在此添加额外的初始化代码
m_rtTracker.m_nStyle = m_rtTracker.dottedLine | m_rtTracker.resizeOutside;
m_rtTracker.m_nHandleSize = 6;
m_rtTracker.m_sizeMin.SetSize(10, 10);
return TRUE; // 除非设置了控件的焦点,否则返回 TRUE
}
在对话框的绘制函数中:
void CEditableDlg::OnPaint()
{
CPaintDC dc(this); // 用于绘制的设备上下文
if (IsIconic())
{
......
}
else
{
CDialog::OnPaint();
if (m_pSelectedCtrl)
{
m_rtTracker.Draw(&dc);
}
}
}
在相应鼠标消息中添加代码:
int CEditableDlg::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CPoint point;
::GetCursorPos(&point);
ScreenToClient(&point);
CWnd *pWnd = ChildWindowFromPoint(point);
if (pWnd && IsChild(pWnd))
{
while (pWnd && pWnd->GetParent() != this) pWnd = pWnd->GetParent();
CRect rcWnd;
m_rtTracker.GetTrueRect(rcWnd);
InvalidateRect(rcWnd);
pWnd->GetWindowRect(rcWnd);
ScreenToClient(rcWnd);
m_rtTracker.m_rect.CopyRect(rcWnd);
m_rtTracker.GetTrueRect(rcWnd);
InvalidateRect(rcWnd);
m_pSelectedCtrl = CWnd::FromHandle(pWnd->GetSafeHwnd());
return MA_ACTIVATEANDEAT;
} return CDialog::OnMouseActivate(pDesktopWnd, nHitTest, message);
}
void CEditableDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CWnd *pWnd = ChildWindowFromPoint(point);
if (pWnd && IsChild(pWnd) && pWnd->GetParent() == this)
{
CRect rcWnd;
m_rtTracker.GetTrueRect(rcWnd);
InvalidateRect(rcWnd);
pWnd->GetWindowRect(rcWnd);
ScreenToClient(rcWnd);
m_rtTracker.m_rect.CopyRect(rcWnd);
m_rtTracker.GetTrueRect(rcWnd);
InvalidateRect(rcWnd);
m_pSelectedCtrl = CWnd::FromHandle(pWnd->GetSafeHwnd());
return;
}
CRect rectSave;
m_rtTracker.GetTrueRect(rectSave);
if (m_rtTracker.HitTest(point) >= 0 && m_rtTracker.Track(this, point, false))
{
InvalidateRect(rectSave);
if (m_pSelectedCtrl)
m_pSelectedCtrl->MoveWindow(m_rtTracker.m_rect);
m_rtTracker.GetTrueRect(rectSave);
InvalidateRect(rectSave);
}
else if (m_pSelectedCtrl)
{
m_pSelectedCtrl = NULL;
InvalidateRect(rectSave);
}
CDialog::OnLButtonDown(nFlags, point);
}
下面是设置光标:
BOOL CEditableDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if ( m_pSelectedCtrl && m_rtTracker.SetCursor( this, nHitTest ))
{
return TRUE;
}
else
{
SetCursor(::LoadCursor(NULL, IDC_ARROW));
return true;
}
return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
下面再说说保存的问题。修改了对话框,希望下次可以调用,可以采用保存对话框模板的方法。不过我们这里只需要保存各个控件的大小和位置,因此可以简化处理,下面是保存代码:
void CEditableDlg::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CFile dlgTempFile(_T("dlgtemp.dtf"), CFile::modeReadWrite | CFile::modeCreate | CFile::typeBinary);
if (dlgTempFile)
{
CWnd *pWnd = FindWindowEx(GetSafeHwnd(), NULL, NULL, NULL);
while (pWnd)
{
int nID = pWnd->GetDlgCtrlID();
dlgTempFile.Write(&nID, sizeof(int));
RECT rcWnd;
pWnd->GetWindowRect(&rcWnd);
ScreenToClient(&rcWnd);
dlgTempFile.Write(&rcWnd, sizeof(RECT));
pWnd = FindWindowEx(GetSafeHwnd(), pWnd->GetSafeHwnd(), NULL, NULL);
}
dlgTempFile.Close();
}
CDialog::OnClose();
}
下面是调用代码:
BOOL CEditableDlg::OnInitDialog()
{
.....
CFile dlgTempFile(_T("dlgtemp.dtf"), CFile::modeRead | CFile::typeBinary);
if (dlgTempFile)
{
int nID;
RECT rcWnd;
while (dlgTempFile.Read(&nID, sizeof(nID)))
{
if (dlgTempFile.Read(&rcWnd, sizeof(RECT)))
{
CWnd *pWnd = GetDlgItem(nID);
if (pWnd)
{
//ScreenToClient(&rcWnd);
pWnd->MoveWindow(&rcWnd);
}
}
}
dlgTempFile.Close();
}
return TRUE; // 除非设置了控件的焦点,否则返回 TRUE
}
在OnClose中保存,在OnInitDialog中调用,简化了文件处理。如果需要,可以增加选择保存文件和调用文件的代码提供更多的选择。为了保证调用正确的对话框设置,还需要在文件头部写入一个标识号,可以用GUID之类生成唯一标识,就更加保险了。
最后一个问题,对话框要用,可以用一个布尔型变量来切换可编辑状态,只要上面鼠标消息中德语句放到一个条件判断中就可以了。