// IOMonitoringDlg.cpp: 实现文件 // #include "stdafx.h" #include "BondEq.h" #include "afxdialogex.h" #include "IOMonitoringDlg.h" #define TIMER_INIT 1 #define TIMER_READ_PLC_DATA 2 // CIOMonitoringDlg 对话框 IMPLEMENT_DYNAMIC(CIOMonitoringDlg, CDialogEx) CIOMonitoringDlg::CIOMonitoringDlg(CWnd* pParent /*=nullptr*/) : CDialogEx(IDD_DIALOG_IO_MONITORING, pParent) { m_nCurrentPage = 1; m_nTotalPages = 1; m_nRowsPerPage = 10; m_nCols = 6; } CIOMonitoringDlg::~CIOMonitoringDlg() { for (auto& pair : m_mapFonts) { if (pair.second) { pair.second->DeleteObject(); delete pair.second; } } m_mapFonts.clear(); ClearDynamicControls(); } void CIOMonitoringDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_STATIC_PAGE_NUMBER, m_staticPageNum); } void CIOMonitoringDlg::SetIOManager(const std::string& machineName) { IOManager manager; manager.loadFromFile(machineName); m_machineName = machineName; // 加载数据 m_displayData = manager.GetMachineData(machineName); // 计算页数 m_nCurrentPage = 1; m_nTotalPages = (m_displayData.size() + m_nRowsPerPage - 1) / m_nRowsPerPage; } CFont* CIOMonitoringDlg::GetOrCreateFont(int nFontSize) { auto it = m_mapFonts.find(nFontSize); if (it != m_mapFonts.end()) { return it->second; } CFont* font = new CFont(); LOGFONT logFont = { 0 }; _tcscpy_s(logFont.lfFaceName, _T("Segoe UI")); logFont.lfHeight = -nFontSize; logFont.lfQuality = CLEARTYPE_QUALITY; font->CreateFontIndirect(&logFont); m_mapFonts[nFontSize] = font; return font; } void CIOMonitoringDlg::SetDefaultFont() { CFont* defaultFont = GetOrCreateFont(12); // 遍历所有控件,应用默认字体 CWnd* pWnd = GetWindow(GW_CHILD); while (pWnd) { pWnd->SetFont(defaultFont, TRUE); pWnd = pWnd->GetNextWindow(); } } void CIOMonitoringDlg::AdjustControls(float dScaleX, float dScaleY) { CWnd* pWnd = GetWindow(GW_CHILD); while (pWnd) { int nCtrlID = pWnd->GetDlgCtrlID(); if (nCtrlID != -1 && m_mapCtrlLayouts.find(nCtrlID) != m_mapCtrlLayouts.end()) { CRect originalRect = m_mapCtrlLayouts[nCtrlID]; CRect newRect( static_cast(originalRect.left * dScaleX), static_cast(originalRect.top * dScaleY), static_cast(originalRect.right * dScaleX), static_cast(originalRect.bottom * dScaleY)); pWnd->MoveWindow(&newRect); AdjustControlFont(pWnd, newRect.Width(), newRect.Height()); } pWnd = pWnd->GetNextWindow(); } } void CIOMonitoringDlg::AdjustControlFont(CWnd* pWnd, int nWidth, int nHeight) { TCHAR szClassName[256]; GetClassName(pWnd->m_hWnd, szClassName, sizeof(szClassName)); // 根据控件高度动态调整字体大小 int fontSize = nHeight / 2; if (fontSize < 8) fontSize = 8; if (fontSize < 24) fontSize = 24; // 获取或创建字体 CFont* pFont = GetOrCreateFont(fontSize); pWnd->SetFont(pFont); pWnd->Invalidate(); // 刷新控件显示 } void CIOMonitoringDlg::UpdatePageInfo() { // 格式化页码信息为 "当前页/总页数" CString pageInfo; pageInfo.Format(_T("%d/%d 页"), m_nCurrentPage, m_nTotalPages); m_staticPageNum.SetWindowText(pageInfo); } void CIOMonitoringDlg::CreateDynamicControls() { CRect rect; GetClientRect(&rect); // 获取按钮的大小 CWnd* pPrevButton = GetDlgItem(IDC_BUTTON_PREV_PAGE); CWnd* pNextButton = GetDlgItem(IDC_BUTTON_NEXT_PAGE); CRect prevButtonRect, nextButtonRect; pPrevButton->GetWindowRect(&prevButtonRect); pNextButton->GetWindowRect(&nextButtonRect); // 转换按钮坐标到对话框的坐标系统 ScreenToClient(&prevButtonRect); ScreenToClient(&nextButtonRect); int buttonHeight = prevButtonRect.Height(); // 按钮的高度 int topMargin = rect.Height() * 0.05; // 顶部保留 5% 的高度 int bottomMargin = buttonHeight + topMargin; // 底部保留按钮高度加间距 int sideMargin = topMargin; // 左右间距与顶部间距相同 int groupSpacing = 20; // 两组之间的间距 int verticalSpacing = 10; // 垂直间距 // 每行高度和列宽度 int availableHeight = rect.Height() - topMargin - bottomMargin; int rowHeight = (availableHeight / m_nRowsPerPage) - verticalSpacing; // 控件高度包含间距 int availableWidth = rect.Width() - 2 * sideMargin; // 可用宽度(减去左右间距) int colWidthSmall = availableWidth / 14; // 小宽度列更小 int colWidthLarge = availableWidth * 4 / 14; // 大宽度列比例 int groupWidth = colWidthSmall * 2 + colWidthLarge; // 每组总宽度 for (int i = 0; i < m_nRowsPerPage; ++i) { // 每一行的起始 Y 坐标 int y = topMargin + i * (rowHeight + verticalSpacing); // 创建第 1 组 (0, 1, 2) int x = sideMargin; // 从左边距开始 CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter); x += colWidthSmall; CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("X1000"), false, AlignCenter); x += colWidthSmall; CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false); // 第 2 组起始位置,加上组间距 x += colWidthLarge + groupSpacing; // 创建第 2 组 (3, 4, 5) CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("OFF"), true, AlignCenter, [this, i]() { // 自定义点击事件的逻辑 auto* pControl = static_cast(m_staticControls[i * m_nCols + 3]); CString currentText; pControl->GetWindowText(currentText); if (currentText == _T("OFF")) { pControl->SetBkColor(RGB(0, 255, 0)); // 绿色背景 pControl->SetText(_T("ON")); // 更新文本为 ON } else { pControl->SetBkColor(RGB(255, 0, 0)); // 红色背景 pControl->SetText(_T("OFF")); // 更新文本为 OFF } }); x += colWidthSmall; CreateStaticControl(x, y, colWidthSmall, rowHeight, _T("Y1010"), false, AlignCenter); x += colWidthSmall; CreateStaticControl(x, y, colWidthLarge, rowHeight, _T("描述文本"), false); } } void CIOMonitoringDlg::CreateStaticControl(int x, int y, int width, int height, const CString& text, bool hasBorder, TextAlign alignment, std::function clickCallback) { // 创建动态控件 CBLLabel* pStatic = new CBLLabel(); DWORD style = WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE; // 确保垂直居中 if (hasBorder) { style |= WS_BORDER; // 添加边框 } pStatic->Create(text, style, CRect(x, y, x + width, y + height), this); // 设置文本对齐方式 pStatic->SetAlignment(alignment); // 设置动态字体调整,并设置字体大小(动态字体会根据控件大小调整) int nSize = height / 3; CFont* pFont = GetOrCreateFont(nSize); pStatic->SetFont(pFont); pStatic->SetFontSize(nSize); pStatic->SetDynamicFont(TRUE); // 设置回调 if (clickCallback) { pStatic->SetClickCallback(clickCallback); } // 存储控件指针 m_staticControls.push_back(pStatic); } void CIOMonitoringDlg::DisplayCurrentPage() { int startIndex = (m_nCurrentPage - 1) * m_nRowsPerPage; int endIndex = min(startIndex + m_nRowsPerPage, static_cast(m_displayData.size())); m_inputPLCAddresses.clear(); m_outputPLCAddresses.clear(); for (int i = 0; i < m_nRowsPerPage; ++i) { int row = i; if (startIndex + i < endIndex) { const auto& data = m_displayData[startIndex + i]; // 添加 PLC 地址到容器中 m_inputPLCAddresses.push_back(CString(data.inputAddress.c_str())); // 1 列 m_outputPLCAddresses.push_back(CString(data.outputAddress.c_str())); // 4 列 // 显示控件并设置内容 m_staticControls[row * m_nCols + 0]->SetWindowText(_T("OFF")); m_staticControls[row * m_nCols + 0]->ShowWindow(SW_SHOW); m_staticControls[row * m_nCols + 0]->SetBkColor(RGB(255, 0, 0)); m_staticControls[row * m_nCols + 1]->SetWindowText(CString(data.inputAddress.c_str())); m_staticControls[row * m_nCols + 1]->ShowWindow(SW_SHOW); m_staticControls[row * m_nCols + 2]->SetWindowText(CString(data.inputDescription.c_str())); m_staticControls[row * m_nCols + 2]->ShowWindow(SW_SHOW); m_staticControls[row * m_nCols + 3]->SetWindowText(_T("OFF")); m_staticControls[row * m_nCols + 3]->ShowWindow(SW_SHOW); m_staticControls[row * m_nCols + 3]->SetBkColor(RGB(255, 0, 0)); m_staticControls[row * m_nCols + 4]->SetWindowText(CString(data.outputAddress.c_str())); m_staticControls[row * m_nCols + 4]->ShowWindow(SW_SHOW); m_staticControls[row * m_nCols + 5]->SetWindowText(CString(data.outputDescription.c_str())); m_staticControls[row * m_nCols + 5]->ShowWindow(SW_SHOW); } else { // 隐藏这一行的所有控件 for (int col = 0; col < m_nCols; ++col) { m_staticControls[row * m_nCols + col]->ShowWindow(SW_HIDE); } } } UpdatePageInfo(); } void CIOMonitoringDlg::ClearDynamicControls() { for (auto* pStatic : m_staticControls) { if (pStatic) { pStatic->DestroyWindow(); delete pStatic; } } m_staticControls.clear(); } void CIOMonitoringDlg::UpdatePLCStates() { // 随机值模拟 for (size_t i = 0; i < m_inputPLCAddresses.size(); ++i) { // 模拟获取输入状态 bool inputState = (rand() % 2 == 0); // 偶尔为 true/false auto* inputControl = static_cast(m_staticControls[i * m_nCols + 0]); inputControl->SetBkColor(inputState ? RGB(0, 255, 0) : RGB(255, 0, 0)); inputControl->SetText(inputState ? _T("ON") : _T("OFF")); } for (size_t i = 0; i < m_outputPLCAddresses.size(); ++i) { // 模拟获取输出状态 bool outputState = (rand() % 2 == 0); // 偶尔为 true/false auto* outputControl = static_cast(m_staticControls[i * m_nCols + 3]); outputControl->SetBkColor(outputState ? RGB(0, 255, 0) : RGB(255, 0, 0)); outputControl->SetText(outputState ? _T("ON") : _T("OFF")); } } BEGIN_MESSAGE_MAP(CIOMonitoringDlg, CDialogEx) ON_BN_CLICKED(IDC_BUTTON_PREV_PAGE, &CIOMonitoringDlg::OnBnClickedButtonPrevPage) ON_BN_CLICKED(IDC_BUTTON_NEXT_PAGE, &CIOMonitoringDlg::OnBnClickedButtonNextPage) ON_WM_SIZE() ON_WM_TIMER() ON_WM_CLOSE() END_MESSAGE_MAP() // CIOMonitoringDlg 消息处理程序 BOOL CIOMonitoringDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // TODO: 在此添加额外的初始化 CRect screenRect, dlgRect, clientRect; SystemParametersInfo(SPI_GETWORKAREA, 0, &screenRect, 0); GetClientRect(&clientRect); m_nInitialWidth = clientRect.Width(); m_nInitialHeight = clientRect.Height(); // 初始化默认字体 CFont* pDefaultFont = GetOrCreateFont(12); // 遍历所有子控件,记录初始位置并设置默认字体 CWnd* pWnd = GetWindow(GW_CHILD); while (pWnd) { int nCtrlID = pWnd->GetDlgCtrlID(); if (nCtrlID != -1) { // 记录控件初始布局 CRect ctrlRect; pWnd->GetWindowRect(&ctrlRect); ScreenToClient(&ctrlRect); m_mapCtrlLayouts[nCtrlID] = ctrlRect; // 跳过特殊控件(如 MFCGridCtrl) TCHAR szClassName[256]; GetClassName(pWnd->m_hWnd, szClassName, sizeof(szClassName)); if (_tcsicmp(szClassName, _T("MFCGridCtrl")) == 0) { pWnd = pWnd->GetNextWindow(); continue; } // 设置默认字体 pWnd->SetFont(pDefaultFont); } pWnd = pWnd->GetNextWindow(); } GetWindowRect(&dlgRect); int dlgWidth = dlgRect.Width() * 2; int dlgHeight = dlgRect.Height() * 2; if (dlgWidth > screenRect.Width()) { dlgWidth = screenRect.Width(); } if (dlgHeight > screenRect.Height()) { dlgHeight = screenRect.Height(); } int centerX = screenRect.left + (screenRect.Width() - dlgWidth) / 2; int centerY = screenRect.top + (screenRect.Height() - dlgHeight) / 2; MoveWindow(centerX, centerY, dlgWidth, dlgHeight); CreateDynamicControls(); DisplayCurrentPage(); SetTimer(TIMER_READ_PLC_DATA, 500, nullptr); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CIOMonitoringDlg::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); // TODO: 在此处添加消息处理程序代码 if (nType == SIZE_MINIMIZED || m_mapCtrlLayouts.empty()) { return; } float dScaleX = static_cast(cx) / m_nInitialWidth; float dScaleY = static_cast(cy) / m_nInitialHeight; // 遍历对话框中的所有控件 AdjustControls(dScaleX, dScaleY); } void CIOMonitoringDlg::OnBnClickedButtonPrevPage() { // TODO: 在此添加控件通知处理程序代码 if (m_nCurrentPage > 1) { --m_nCurrentPage; DisplayCurrentPage(); } else { AfxMessageBox(_T("已经是第一页!")); } } void CIOMonitoringDlg::OnBnClickedButtonNextPage() { // TODO: 在此添加控件通知处理程序代码 if (m_nCurrentPage < m_nTotalPages) { ++m_nCurrentPage; DisplayCurrentPage(); } else { AfxMessageBox(_T("已经是最后一页!")); } } void CIOMonitoringDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if (TIMER_READ_PLC_DATA == nIDEvent) { //ASSERT(m_pPLC); UpdatePLCStates(); Sleep(100); } CDialogEx::OnTimer(nIDEvent); } void CIOMonitoringDlg::OnClose() { // TODO: 在此添加消息处理程序代码和/或调用默认值 KillTimer(TIMER_READ_PLC_DATA); CDialogEx::OnClose(); }