【VC++】SDIにマウスイベントで四角形を描画する
<機能>
(1)MFCのSDIアプリケーションにマウスイベントを追加します
WM_MOUSELEAVE : ウィンドウのクライアント領域を離れた
WM_MOUSEMOVE : マウスカーソルが動いた
WM_RBUTTONDOWN : マウスの右ボタンが押下された
WM_RBUTTONUP : マウスの右ボタンが解放された
(2)マウスの右ボタンでクライアント領域をドラッグすると四角形を描画します
CDC->Rectangle
(3)メニューで描画する四角形の塗りつぶし色、線の種類を変更できます
塗りつぶし色
・赤系の色
・緑系の色
・青系の色
線のスタイル
・実線(PS_SOLID)
・破線(PS_DASH)
・点線(PS_DOT)
<実行イメージ>
メニューを指定すると描画する四角形の色と線のスタイルが変更できます
マウスの右ボタン押下するとスタート位置セット、
マウスの右ボタンUPすると終了位置をセットして四角形を描画します
<動作検証&開発環境>
Visual Studio Community 2017
<使い方>
1.MFCアプリケーションでプロジェクトを作成
2.アプリケーションの種類でシングルドキュメントを選択
※その他の設定は下図参照
3.ユーザーインターフェイス機能でクラシックメニューを使用するを選択
※その他の設定は下図参照
4.高度な機能で不要なチェックを外す
5.プロジェクト構成を確認してソースを追加する
6.クラスウィザードでCChileViewクラスにマウスイベントハンドラを追加
WM_MOUSELEAVE : クライアント領域の外にカーソルが移動したとき座標を初期化
WM_MOUSEMOVE : 移動中のマウスカーソルを変更する
WM_RBUTTONDOWN : マウスの右ボタンでスタート座標取得
WM_RBUTTONUP : マウスの右ボタンUPで終了座標取得して描画
7.リソースの[Menu]->[IDR_MAINFRAME]を修正してメニュー項目を追加
以下項目を追加します
赤系の色 : ID_32771
緑系の色 : ID_32772
青系の色 : ID_32773
――境界線をセット――
実線 : ID_32774
破線 : ID_32775
点線 : ID_32776
※塗りつぶし色と線のスタイルはCheckMenuRadioItemでそれぞれグループ化します
メニューのIDはここではID_32771~ID32776ですがソース内もそれぞれの環境に応じて変更してください
8.クラスウィザードでCMainFrameにOnCommandを追加します
メニューが操作されたとき塗りつぶし色、線のスタイルを変更する
MFCApplication1.h
class CMFCApplication1App : public CWinApp
{
//〜〜省略〜〜
//グローバル変数
public:
//メニューでの選択値
COLORREF selClr;
COLORREF selLinfClr;
int iSelStyle;
};
extern CMFCApplication1App theApp;
MainFrm.h
class CMainFrame : public CFrameWnd
{
//〜〜省略〜〜
private:
//塗りつぶしの色
COLORREF clr[3] = {
RGB(216,130,187),
RGB(146,208,80),
RGB(155,194,230)
};
//線の色
COLORREF lineclr[3] = {
RGB(199,73,157),
RGB(97,148,40),
RGB(47,117,181)
};
//線のスタイル
int iStyle[3] = {
PS_SOLID,
PS_DASH,
PS_DOT
};
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
};
MainFrm.cpp
#include "stdafx.h"
#include "MFCApplication1.h"
#include "MainFrm.h"
//CMainFrameオブジェクト
extern CMFCApplication1App theApp;
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
//〜〜省略〜〜
CMenu *pMenu = this->GetMenu();
//ON_COMMAND ハンドラーが自動的に無効になるのを停止
CFrameWnd::m_bAutoMenuEnable = FALSE;
//全てのメニューを有効化
pMenu->EnableMenuItem(ID_32771, MF_BYCOMMAND | MF_ENABLED);
pMenu->EnableMenuItem(ID_32772, MF_BYCOMMAND | MF_ENABLED);
pMenu->EnableMenuItem(ID_32773, MF_BYCOMMAND | MF_ENABLED);
pMenu->EnableMenuItem(ID_32774, MF_BYCOMMAND | MF_ENABLED);
pMenu->EnableMenuItem(ID_32775, MF_BYCOMMAND | MF_ENABLED);
pMenu->EnableMenuItem(ID_32776, MF_BYCOMMAND | MF_ENABLED);
//色のメニューをグループ化して赤系にチェックOn
//CheckMenuRadioItemについてはDeveloperNetwork参照
pMenu->CheckMenuRadioItem(ID_32771, ID_32773, ID_32771, MF_BYCOMMAND);
//線のメニューをグループ化して実線にチェックOn
pMenu->CheckMenuRadioItem(ID_32774, ID_32776, ID_32774, MF_BYCOMMAND);
//初期値をセット
theApp.selClr = clr[0];
theApp.selLinfClr = lineclr[0];
theApp.iSelStyle = iStyle[0];
return 0;
}
/***********************************************************
OnCommand
メニューが変更されたら塗りつぶし色、線の種類を変更
※環境に応じてメニューのIDは変更してください
***********************************************************/
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
int iSelClr = 0;
int iSelStyle = 0;
CMenu *pMenu = this->GetMenu();
//現在(変更前)のメニューの状態を取得
if (pMenu->GetMenuState(ID_32771, MF_BYCOMMAND | MF_CHECKED) & MF_CHECKED) iSelClr = 0;
if (pMenu->GetMenuState(ID_32772, MF_BYCOMMAND | MF_CHECKED) & MF_CHECKED) iSelClr = 1;
if (pMenu->GetMenuState(ID_32773, MF_BYCOMMAND | MF_CHECKED) & MF_CHECKED) iSelClr = 2;
if (pMenu->GetMenuState(ID_32774, MF_BYCOMMAND | MF_CHECKED) & MF_CHECKED) iSelStyle = 0;
if (pMenu->GetMenuState(ID_32775, MF_BYCOMMAND | MF_CHECKED) & MF_CHECKED) iSelStyle = 1;
if (pMenu->GetMenuState(ID_32776, MF_BYCOMMAND | MF_CHECKED) & MF_CHECKED) iSelStyle = 2;
//変更されたメニュー項目(iItem)で更新
UINT iItem = LOWORD(wParam);
switch (iItem)
{
case ID_32771:
iSelClr = 0;
break;
case ID_32772:
iSelClr = 1;
break;
case ID_32773:
iSelClr = 2;
break;
case ID_32774:
iSelStyle = 0;
break;
case ID_32775:
iSelStyle = 1;
break;
case ID_32776:
iSelStyle = 2;
break;
default:
;
}
//塗りつぶしグループの値を変更
if (iItem == ID_32771 || iItem == ID_32772 || iItem == ID_32773)
{
pMenu->CheckMenuRadioItem(ID_32771, ID_32773, iItem, MF_BYCOMMAND);
theApp.selClr = clr[iSelClr];
theApp.selLinfClr = lineclr[iSelClr];
}
//線の種類グループの値を変更
if (iItem == ID_32774 || iItem == ID_32775 || iItem == ID_32776)
{
pMenu->CheckMenuRadioItem(ID_32774, ID_32776, iItem, MF_BYCOMMAND);
theApp.iSelStyle = iStyle[iSelStyle];
}
TRACE(L"iSelClr=%d iSelStyle=%d¥r¥n",iSelClr, iSelStyle);
return CFrameWnd::OnCommand(wParam, lParam);
}
ChildView.h
class CChildView : public CWnd
{
//〜〜省略〜〜
public:
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnMouseLeave();
private:
void DrawRectangle();
//描画中フラグ(描画中のときTRUE)
BOOL bDrawing;
//座標情報
CPoint stPoint, edPoint;
};
ChildView.cpp
#include "stdafx.h"
#include "MFCApplication1.h"
#include "ChildView.h"
//CMainFrameオブジェクト
extern CMFCApplication1App theApp;
BEGIN_MESSAGE_MAP(CChildView, CWnd)
//〜〜省略〜〜
ON_WM_RBUTTONDOWN()
ON_WM_RBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_MOUSELEAVE()
END_MESSAGE_MAP()
/***********************************************************
OnRButtonDown
子ウィンドウでマウスの右ボタンが押下されたイベント
***********************************************************/
void CChildView::OnRButtonDown(UINT nFlags, CPoint point)
{
bDrawing = TRUE;
SetCursor(LoadCursor(NULL,IDC_SIZEALL));
//開始位置を終了位置を初期化
stPoint.SetPoint(0, 0);
edPoint.SetPoint(0, 0);
//開始位置をセット
stPoint = point;
CWnd::OnRButtonDown(nFlags, point);
}
/***********************************************************
OnRButtonUp
子ウィンドウでマウスの右ボタンが離されたイベント
***********************************************************/
void CChildView::OnRButtonUp(UINT nFlags, CPoint point)
{
if (bDrawing)
{
SetCursor(LoadCursor(NULL,IDC_ARROW));
//終了位置をセット
edPoint = point;
//四角形を描画する
DrawRectangle();
}
bDrawing = FALSE;
CWnd::OnRButtonUp(nFlags, point);
}
/***********************************************************
OnMouseMove
マウスが移動中に発生するイベント
***********************************************************/
void CChildView::OnMouseMove(UINT nFlags, CPoint point)
{
if (bDrawing)
{
SetCursor(LoadCursor(NULL,IDC_SIZEALL));
}
//WM_MOUSELEAVEイベントを受け取れるようにしておく
//TrackMouseEventについてはDeveloperNetwork参照
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = WM_NCMOUSELEAVE;
tme.hwndTrack = this->GetSafeHwnd();
tme.dwHoverTime = 0; //関係ないので0をセットしておく
TrackMouseEvent(&tme);
CWnd::OnMouseMove(nFlags, point);
}
/***********************************************************
OnMouseLeave
子ウィンドウのクライアント領域をマウスが離れたイベント
描画フラグとアイコンをリセットする
***********************************************************/
void CChildView::OnMouseLeave()
{
bDrawing = FALSE;
SetCursor(LoadCursor(NULL, IDC_ARROW));
TRACE(L"OnMouseLeave¥r¥n");
CWnd::OnMouseLeave();
}
/***********************************************************
DrawRectangle
開始Pointと終了Pointに対して四角形を描画する
塗りつぶし色、線の色、線のスタイルはメニューの選択値を取得
***********************************************************/
void CChildView::DrawRectangle()
{
CDC *pDC = this->GetDC();
//四角形を塗りつぶすブラシ
CBrush brs(theApp.selClr);
CBrush *pOldBrush = pDC->SelectObject(&brs);
//四角形を描画するペン
//1より大きな幅を指定するとPS_SOLIDスタイルになるため幅は0とする
CPen pen;
pen.CreatePen(theApp.iSelStyle, 0, theApp.selLinfClr);
CPen *pOldPen = pDC->SelectObject(&pen);
//四角形を描画する領域
CRect rect(stPoint,edPoint);
//参考情報として出力ウィンドウに座標表示
CString cs;
cs.Format(L"st.x=%d st.y=%d ed.x=%d ed.y=%d¥r¥n",
stPoint.x, stPoint.y, edPoint.x, edPoint.y);
TRACE(cs);
//四角形を描画
pDC->Rectangle(rect);
//オブジェクトを戻す
pDC->SelectObject(pOldBrush);
pDC->SelectObject(pOldPen);
}
よろしければポチッと押してください