很多天没更新了,实在惭愧。这些天有些懒,而且还养成了睡午觉的坏习惯,而且一睡还是一下午……

好,先把那事暂搁一边,之后会加快更新频率的。这次给大家介绍一下,怎样让Static控件支持响应鼠标消息。static控件包括static文本控件、picture控件等(其他的,比如组框控件,与之差不多,就不介绍了)。

最终效果如下:

static控件演示

(文后附有demo源码)

static控件默认是不支持响应鼠标消息的,如果把其ID从IDC_STATIC改成其他的,可见其可以响应BN_CLICKED消息,但这远远不够。但是MS也没有把路堵死,我们可以利用SetWindowLong,动态增加 其SS_NOTIFY风格,使其支持鼠标响应。

1、不重载CStatic,使静态文本控件支持鼠标消息的办法

1
2
3
4
5
6
7
8
9
        //可以在InitDialog中添加
	DWORD dwStyle = m_static1.GetStyle();
	dwStyle |= SS_NOTIFY;
	SetWindowLong(m_static1.GetSafeHwnd(), GWL_STYLE, dwStyle);
	SetWindowLong(m_static2.GetSafeHwnd(), GWL_STYLE, dwStyle);
 
	dwStyle = m_skBmp.GetStyle();//m_skBmp指的是picture控件(关联的CStatic的变量)
	dwStyle |= SS_NOTIFY;
	SetWindowLong(m_skBmp.GetSafeHwnd(), GWL_STYLE, dwStyle);

当然,也可以不关联CStatic的成员变量,直接GetDlgItem是一样的。

对其添加消息响应,以双击为例:

分别添加

1
2
3
4
5
6
7
8
9
10
11
12
afx_msg void OnStaticDblClick();
……
ON_STN_DBLCLK(IDC_STATIC2, OnStaticDblClick)
……
最后实现
void CTestDlg::OnStaticDblClick()
{
        //在这里添加操作
	MessageBox(_T("静态控件被DblClicked啦!"));
}
 
利用这个,还可用picture控件做一个简单的按钮,比较简单,详见附件代码。

2、为实现更强大的功能,最好的办法是重载CStatic类。这里以CHyperLinker类为例,给大家讲讲实现方法

增加SS_NOTIFY属性方法类似,可以在PreSubClass中完成, 这里代码略去

实现滑过效果、点击效果(颜色变化等效果),主要是在对应消息响应函数中调用Invalidate, 在CtlColor实现重画。

在CtlColor(注意响应的是WM_CTLCOLOR_REFLECT)里面无非是对各种状态的判断,并SetTextColor等而已

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
HBRUSH CHyperLinker::CtlColor(CDC* pDC, UINT nCtlColor)
{
	ASSERT(nCtlColor == CTLCOLOR_STATIC);
 
	DWORD dwStyle = GetStyle();
/*	if (!(dwStyle & SS_NOTIFY))
	{
		// Turn on notify flag to get mouse messages and STN_CLICKED.
		// Otherwise, I'll never get any mouse clicks!
		::SetWindowLong(m_hWnd, GWL_STYLE, dwStyle | SS_NOTIFY);
	}*/
 
	HBRUSH hbr = NULL;
	if ((dwStyle & 0xFF) <= SS_RIGHT)
	{
		// Modify the font to be underline
		if (!((HFONT) m_Font))
		{
			LOGFONT lf;
			GetFont()->GetObject(sizeof(lf), &lf);
 
			lf.lfUnderline = m_bUnderLine;
			m_Font.CreateFontIndirect(&lf);
		}
		pDC->SelectObject(&m_Font);
 
		//set the text colors
		if(m_bVisited==TRUE)
		{
			pDC->SetTextColor(m_VisitedColor);
//			AfxMessageBox("Click");
		}
		else
		{
			if(m_bAboveControl==TRUE)
			{
				pDC->SetTextColor(m_CoverColor);
//				AfxMessageBox("Above");
			}
			else
			{
				pDC->SetTextColor(this->m_InitColor);
//				AfxMessageBox("init");
			}
		}
		pDC->SetBkMode(TRANSPARENT);
		// return hollow brush to preserve parent background color
		hbr = (HBRUSH)::GetStockObject(HOLLOW_BRUSH);
	}
	return hbr;
}

再重点介绍一下响应鼠标滑动消息。先看一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void CHyperLinker::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: Add your message handler code here and/or call default
	CRect rect;
	GetClientRect(rect);
//	static BOOL bIsIn=FALSE;	//判断是否前一此鼠标就已经在static控件区域类
	if (rect.PtInRect(point))
	{
		m_bAboveControl=TRUE;
 
/*以下被注释的几行为无效代码, C瓜哥注*/
		//if(bIsIn==FALSE)
 		//{
			SetCapture();
			bIsIn=TRUE;
			Invalidate();
		//}
	}
	else
	{
		m_bAboveControl=FALSE;
		//if (bIsIn==TRUE)
		//{
			ReleaseCapture();
			bIsIn=FALSE;
			Invalidate();
		//}
	}
	CStatic::OnMouseMove(nFlags, point);
}

C瓜哥
适当修改了一下原作者的代码,被注释掉的代码是赘余代码。他用的点在区域里面的判断函数PtInRect来判断鼠标进入与移出控件窗口区域。其实也可以用分发WM_MOUSEHOVER、WM_MOUSELEAVE的办法实现。(具体实现办法,请参看C瓜哥的前几篇重绘按钮、文本框的文章)

这里有个细节一定要注意,就是一定要用鼠标捕获(SetCapture、RealeaseCapture)的办法实现。默认只会响应响应鼠标在控件区域内的MouseMove情况,移出之后就失效了。所以在进入其中时,就SetCapture,使控件一直捕获鼠标消息,等到鼠标移出之后,再ReleaseCapture,停止捕获。这样就能够弥补这个缺陷了!

单击事件中就主要调用ShellExcute函数和Invalidate了一下,比较简单,这里略去。

C瓜哥把单击事件中的直接ShellExcute,变成判断m_sURL是否为空。这样就可在在主窗口类中也单独对其添加事件响应!
————————–

 附件下载

Static_test.rar