// 新建项目后要将对话框的Border属性改成Resizing
// WndResize.h
#pragma once
#include <Afxtempl.h>
class CWndResize
{
protected:
struct SSiszeData
{
HWND hWndCtrl; //窗口句柄
double fLeft, fTop, fRight, fBottom; //位置相对比例
SSiszeData();
void Init(HWND hParent, RECT rcParent, HWND hCtrl); //初始化 (记录窗口原始坐标位置)
RECT GetRect(RECT rcParent); //获取窗口坐标
};
public:
INT_PTR Bind(HWND hParent); //初始化
void ResizeWindow(); //缩放窗口控件
protected:
HWND m_hParentWnd;
CArray <SSiszeData, const SSiszeData &>m_SizeArray;
CRect m_rcOrgRect;
};
// WndResize.cpp
#include "pch.h"
#include "WndResize.h"
CWndResize::SSiszeData::SSiszeData()
{
memset(this, 0, sizeof(*this));
}
//初始化 (记录窗口原始坐标位置)
void CWndResize::SSiszeData::Init(HWND hParent, RECT rcParent, HWND hCtrl)
{
ASSERT(hParent && hCtrl);
hWndCtrl = hCtrl;
//获取控件的坐标
RECT rcCtrl;
GetWindowRect(hCtrl, &rcCtrl);
POINT ptLT = { rcCtrl.left, rcCtrl.top };
POINT ptRB = { rcCtrl.right, rcCtrl.bottom };
ScreenToClient(hParent, &ptLT);
ScreenToClient(hParent, &ptRB);
//计算相对位置
double cx = rcParent.right - rcParent.top;
double cy = rcParent.bottom - rcParent.top;
fLeft = ptLT.x / cx;
fTop = ptLT.y / cy;
fRight = ptRB.x / cx;
fBottom = ptRB.y / cy;
}
//获取窗口坐标
RECT CWndResize::SSiszeData::GetRect(RECT rcParent)
{
RECT rcCtrl = { 0,0,0,0 };
if (hWndCtrl)
{
int cx = rcParent.right - rcParent.left;
int cy = rcParent.bottom - rcParent.top;
rcCtrl.left = (LONG)(cx * fLeft);
rcCtrl.top = (LONG)(cy * fTop);
rcCtrl.right = (LONG)(cx * fRight);
rcCtrl.bottom = (LONG)(cy * fBottom);
}
return rcCtrl;
}
//初始化
INT_PTR CWndResize::Bind(HWND hParent)
{
ASSERT(hParent && IsWindow(hParent));
m_hParentWnd = hParent;
GetClientRect(hParent, &m_rcOrgRect);
m_SizeArray.RemoveAll();
HWND hWndCtrl = NULL;
while (true)
{
hWndCtrl = FindWindowEx(hParent, hWndCtrl, NULL, NULL);
if (!hWndCtrl) break;
SSiszeData tmp;
tmp.Init(hParent, m_rcOrgRect, hWndCtrl);
m_SizeArray.Add(tmp);
}
return m_SizeArray.GetSize();
}
//缩放窗口控件
//这个函数最好写成回调函数, 让用户自己来设置第三方控件大小的适应
void CWndResize::ResizeWindow()
{
CRect rcClient;
GetClientRect(m_hParentWnd, &rcClient);
if (m_hParentWnd && m_SizeArray.GetSize() > 0 && rcClient != m_rcOrgRect)
{
for (INT_PTR i = 0; i < m_SizeArray.GetSize(); i++)
{
SSiszeData *pData = &m_SizeArray[i];
if (!pData) continue;
CRect rcCtrl = pData->GetRect(rcClient);
// 如果控件有第三方控件, 比如OpenCV的视图窗口嵌入到MFC窗口
// 此时最好拖一个图片框,让OpenCV的视图窗恰好覆盖该图片框
// 保证图片框始终与OpenCV的视图窗始终一样大
MoveWindow(pData->hWndCtrl, rcCtrl.left, rcCtrl.top, rcCtrl.Width(), rcCtrl.Height(), TRUE);
// 假设图片框在m_SizeArray中的索引是0也许有多个视图, 不妨扩展结构体SSiszeData, 让它保存控件的ID
// 这样就不用费力的去猜索引与控件的对应关系了, 我这里只是一个例子所以...
if (i == 0)
{
// 调整OpenCV视图的大小
}
}
m_rcOrgRect = rcClient;
}
}
// ResizeWindowDlg.h
#pragma once
class CResizeWindowDlg : public CDialogEx
{
... ...
private:
afx_msg LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
CWndResize m_wndResize;
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
... ...
afx_msg void OnSize(UINT nType, int cx, int cy);
DECLARE_MESSAGE_MAP()
};
// WndResize.cpp
#include "pch.h"
#include "framework.h"
#include "ResizeWindow.h"
#include "ResizeWindowDlg.h"
#include "afxdialogex.h"
BOOL CResizeWindowDlg::OnInitDialog()
{
... ...
m_wndResize.Bind(m_hWnd);
return TRUE;
}
void CResizeWindowDlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
m_wndResize.ResizeWindow();
}
LRESULT CResizeWindowDlg::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
enum DragMask
{
_DragNull = 0x00,
_DragLeft = 0x01,
_DragTop = 0x02,
_DragRight = 0x04,
_DragBottom = 0x08,
};
static UINT nEnterDrag = _DragNull; //拖拽标记
switch (uMsg)
{
case WM_NCHITTEST:
{
CPoint point((SHORT)(LOWORD(lParam)), (SHORT)(HIWORD(lParam))); //鼠标位置
CRect rcRect; //窗口位置
GetWindowRect(&rcRect);
const int cx = GetSystemMetrics(SM_CXDRAG);
const int cy = GetSystemMetrics(SM_CYDRAG);
CRect rcLeftTop(rcRect.left, rcRect.top, rcRect.left + cx, rcRect.top + cy); // 左上角
if (rcLeftTop.PtInRect(point)) return HTTOPLEFT;
CRect rcRightBottom(rcRect.right - cx, rcRect.bottom - cy, rcRect.right, rcRect.bottom); //右下角
if (rcRightBottom.PtInRect(point)) return HTBOTTOMRIGHT;
CRect rcLeft(rcRect.left, rcRect.top, rcRect.left + cx, rcRect.bottom); //左边框
if (rcLeft.PtInRect(point)) return HTLEFT;
CRect rcTop(rcRect.left, rcRect.top, rcRect.right, rcRect.top + cx); //上边框
if (rcTop.PtInRect(point)) return HTTOP;
CRect rcBottom(rcRect.left, rcRect.bottom - cy, rcRect.right, rcRect.bottom); //下边框
if (rcBottom.PtInRect(point)) return HTBOTTOM;
CRect rcRihgt(rcRect.right - cx, rcRect.top, rcRect.right, rcRect.bottom); //右边框
if (rcRihgt.PtInRect(point)) return HTRIGHT;
break;
}
case WM_NCLBUTTONDOWN:
{
UINT nHittest = (UINT)wParam;
//在边框时捕捉焦点
nEnterDrag = _DragNull;
if (nHittest == HTTOPLEFT) nEnterDrag |= _DragTop | _DragLeft;
if (nHittest == HTTOP) nEnterDrag |= _DragTop;
if (nHittest == HTLEFT) nEnterDrag |= _DragLeft;
if (nHittest == HTBOTTOMRIGHT) nEnterDrag |= _DragRight | _DragBottom;
if (nHittest == HTBOTTOM) nEnterDrag |= _DragBottom;
if (nHittest == HTRIGHT) nEnterDrag |= _DragRight;
if (nEnterDrag != _DragNull) SetCapture();
break;
}
case WM_LBUTTONUP: case WM_NCLBUTTONUP:
{
//鼠标抬起时释放焦点
if (nEnterDrag)
{
ReleaseCapture();
nEnterDrag = _DragNull;
}
break;
}
case WM_MOUSEMOVE:
{
//拖拽操作
if (nEnterDrag != _DragNull)
{
CPoint point((SHORT)(LOWORD(lParam)), (SHORT)(HIWORD(lParam)));
ClientToScreen(&point);
CRect rcRect;
GetWindowRect(&rcRect);
if (nEnterDrag & _DragTop) rcRect.top = point.y; //top
if (nEnterDrag & _DragLeft) rcRect.left = point.x; //left
if (nEnterDrag & _DragRight) rcRect.right = point.x; //right
if (nEnterDrag & _DragBottom) rcRect.bottom = point.y; //bottom
MoveWindow(&rcRect);
}
break;
}
}
return CDialog::WindowProc(uMsg, wParam, lParam);
}