首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

《電子尺》V1.02:VC++ 透明全螢幕視窗實現螢幕測量工具,支援像素/公制/英制單位切換

     編輯:關於VC++

製作網頁/多媒體素材時常需量取螢幕區域寬高。《電子尺》利用 透明全螢幕視窗 (WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED) 鎖定螢幕,繪製十字線輔助定位,雙擊兩點自動計算距離並換算單位。本文完整解析核心類別 CTarget 設計與繪圖邏輯。

核心類別:CTarget (繼承 CWnd)

建立透明全螢幕視窗


void CTarget::CreateTarget(LPCTSTR title, CWnd* pParent) {
  m_cxScreen = GetSystemMetrics(SM_CXSCREEN);
  m_cyScreen = GetSystemMetrics(SM_CYSCREEN);

  // WS_EX_TRANSPARENT 讓滑鼠事件穿透 (但本工具需捕捉滑鼠,故不加此樣式)
  // 改用 WS_EX_TOPMOST 置頂 + WS_EX_LAYERED 透明度控制
  CreateEx(WS_EX_TOPMOST,
           AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
           title, WS_POPUP, 0, 0, m_cxScreen, m_cyScreen,
           NULL, NULL, NULL);

  m_pDC = GetDC(); // 整個螢幕 DC

  // 建立記憶體 DC 備份原始螢幕內容,供重繪時還原
  m_memDC.CreateCompatibleDC(m_pDC);
  CBitmap bmp; bmp.CreateCompatibleBitmap(m_pDC, m_cxScreen, m_cyScreen);
  m_memDC.SelectObject(&bmp);
  m_memDC.BitBlt(0,0,m_cxScreen,m_cyScreen, m_pDC,0,0, SRCCOPY);
  DeleteObject(bmp.Detach());

  m_bSecondClick = FALSE;
  m_pParent = pParent;
}

滑鼠移動:動態十字線


void CTarget::OnMouseMove(UINT nFlags, CPoint point) {
  // 先還原背景 (清除舊十字線)
  m_pDC->BitBlt(0,0,m_cxScreen,m_cyScreen, &m_memDC,0,0, SRCCOPY);

  // 繪製全螢幕十字線
  m_pDC->MoveTo(0, point.y); m_pDC->LineTo(m_cxScreen, point.y);
  m_pDC->MoveTo(point.x, 0); m_pDC->LineTo(point.x, m_cyScreen);

  // 若已點選第一點,繪製連接線
  if (m_bSecondClick) {
    m_pDC->MoveTo(m_ptStart);
    m_pDC->LineTo(point);
  }
  CWnd::OnMouseMove(nFlags, point);
}

左鍵點擊:兩點定距


void CTarget::OnLButtonDown(UINT nFlags, CPoint point) {
  if (!m_bSecondClick) {
    m_ptStart = point;
    m_bSecondClick = TRUE;

    // 還原背景
    m_pDC->BitBlt(0,0,m_cxScreen,m_cyScreen, &m_memDC,0,0, SRCCOPY);
    // 紅色標記第一點
    CPen pen(PS_SOLID, 2, RGB(255,0,0));
    CPen* pOld = m_pDC->SelectObject(&pen);
    m_pDC->MoveTo(point.x-10, point.y); m_pDC->LineTo(point.x+10, point.y);
    m_pDC->MoveTo(point.x, point.y-10); m_pDC->LineTo(point.x, point.y+10);
    m_pDC->SelectObject(pOld);
    DeleteObject(pen.Detach());

    // 更新記憶體 DC 保留紅標記
    m_memDC.BitBlt(0,0,m_cxScreen,m_cyScreen, m_pDC,0,0, SRCCOPY);
  } else {
    m_ptEnd = point;
    CalculateDistance();
    // 清理資源
    m_pDC->DeleteDC(); m_memDC.DeleteDC();
    DestroyWindow();
    // 通知父視窗顯示結果
    m_pParent->PostMessage(WM_MEASURE_DONE);
  }
  CWnd::OnLButtonDown(nFlags, point);
}

右鍵取消


void CTarget::OnRButtonUp(UINT nFlags, CPoint point) {
  m_pDC->DeleteDC(); m_memDC.DeleteDC();
  DestroyWindow();
  m_pParent->PostMessage(WM_MEASURE_CANCEL);
}

距離計算與單位換算


void CTarget::CalculateDistance() {
  double dx = m_ptEnd.x - m_ptStart.x;
  double dy = m_ptEnd.y - m_ptStart.y;
  m_nPixelDist = (int)sqrt(dx*dx + dy*dy); // 像素

  CDC* pDC = GetDC();
  // 公制:MM_LOMETRIC = 0.1 mm
  pDC->SetMapMode(MM_LOMETRIC);
  POINT ls = m_ptStart, le = m_ptEnd;
  pDC->DPtoLP(&ls); pDC->DPtoLP(&le);
  dx = le.x - ls.x; dy = le.y - ls.y;
  m_dMetricDist = sqrt(dx*dx + dy*dy) / 10.0; // mm

  // 英制:MM_LOENGLISH = 0.01 inch
  pDC->SetMapMode(MM_LOENGLISH);
  ls = m_ptStart; le = m_ptEnd;
  pDC->DPtoLP(&ls); pDC->DPtoLP(&le);
  dx = le.x - ls.x; dy = le.y - ls.y;
  m_dInchDist = sqrt(dx*dx + dy*dy) / 100.0; // inch
  ReleaseDC(pDC);
}

主對話框整合


class CElecRulerDlg : public CDialog {
  CTarget m_target;
  // ...
  void OnMeasure() {
    m_target.CreateTarget(_T("Electronic Ruler"), this);
  }
  // 處理 WM_MEASURE_DONE 讀取 m_target.m_nPixelDist 等顯示
};

關鍵技術點總結

技術用途
WS_EX_TOPMOST視窗永遠置頂,覆蓋所有應用
記憶體 DC 雙緩衝無閃爍重繪十字線、標記
SetMapMode + DPtoLP裝置座標轉邏輯座標,自動換算物理單位
自訂訊息 WM_MEASURE_DONE解耦測量視窗與主 UI
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved