SOUI控件的自绘和消息处理

看了作者在B站的视频 做一下知识点的总结

用一个简单的自定义控件来总结下所用的知识点

扩展一个DUI窗口的基本步骤知识点:
Step1:从SWindow中继承一个DUI的窗口类
Step2: 定义新DUI窗口类的类名
Step3: 在main中向SApplication中注册新的DUI类
Step4: 在XML布局中使用你定义的DUI窗口类

一些准备工作
自绘需要一个关键的函数那就是Paint函数

在自绘的类中 这样设置
 

protected:
        SOUI_MSG_MAP_BEGIN()
            MSG_WM_PAINT_EX(OnPaint)
        SOUI_MSG_MAP_END()

protected:
    void OnPaint(IRenderTarget * pRT);    //绘制


在OnPaint中我使用的是GDI+进行绘制

GDI的初始化
首先包含头文件 #include <GdiPlus.h>
全局变量:ULONG_PTR m_gdiplusToken;
初始化操作:
    Gdiplus::GdiplusStartupInput StartupInput;
    GdiplusStartup(&m_gdiplusToken, &StartupInput, NULL);
资源回收:
    Gdiplus::GdiplusShutdown(m_gdiplusToken);

在函数OnPaint中使用GDI+
HDC hdc = pRT->GetDC();
Graphics g(hdc);

 

准备工作完成后 我们开始实例开发

功能:一个类似checkbox的按钮 记录状态,最后通过和Edit控件使用类似显示和隐藏密码的功能

步骤:

Step1: 编写控件代码
    这里使用的是OnpaitnEx中进行窗口的绘画


Step2: 增加控件的消息事件
    这个可以参考 EVT_CMD 类似的定义
    
Step3: 绑定消息
    一般在控件的构造函数中将消息加入到指定的一个类对象m_evtSet 窗口事件集合
    通过m_evtSet.addEvent(EVENTID(自定义事件类));
    然后在控件中触发该消息也就是Fire
    这里的例子在鼠标左键按下的时候触发
        EventCmd evt(this);
        FireEvent(evt);

Step4:触发消息
    类似于中心消息事件 
    两种方法:
        
    方法一:通过在初始化函数中调用(代码片段如下)
            SDrawRight *p = FindChildByName2<SDrawRight>("checktest");
            p->GetEventSet()->subscribeEvent(EVT_CHECKPINK, Subscriber(&CMainDlg::OnCheckTest, this));
            OnCheckTest这个函数的返回值是bool类型
    
    方法二:使用EVENT_NAME_HANDLER 来调用 通过自定义的消息,消息ID和消息函数 来组成
        或者使用EVENT_ID_HANDLER(使用这个需要在属性中加上一个id)
        OnCheckTestFun2这个函数的返回值是void类型

注意使用不同的方法 调用消息事件 函数定义的返回值也是不同的

控件实现的代码:(SDrawRight.h

#pragma once

#include <core/Swnd.h>
#include <GdiPlus.h>

namespace SOUI
{
	//自定义控件的事件处理
#define EVT_CHECKPINK (EVT_EXTERNAL_BEGIN + 999)
	class EventCheckPink : public TplEventArgs<EventCheckPink>
	{
		SOUI_CLASS_NAME(EventCheckPink, L"on_check")
	public:
		EventCheckPink(SOUI::SWindow* pSender) : TplEventArgs<EventCheckPink>(pSender){ }
		enum { EventID = EVT_CHECKPINK};
		BOOL bStatus = FALSE;
	};

	class SDrawRight : public SWindow
	{
		SOUI_CLASS_NAME(SDrawRight,L"check_right")
	public:
		SDrawRight();
		~SDrawRight();

		BOOL GetStatus() { return m_bflag; }

	private:
		void GetSelfAdaptionSize(Gdiplus::Rect &rc);
		void OnLButtonDown(UINT nFlags, CPoint point);

	protected:
		void OnPaint(SOUI::IRenderTarget * pRT);

		SOUI_MSG_MAP_BEGIN()
			MSG_WM_PAINT_EX(OnPaint)
			MSG_WM_LBUTTONDOWN(OnLButtonDown)
		SOUI_MSG_MAP_END()

	private:
		BOOL m_bflag;			//是否被选中的标志
		Gdiplus::Rect m_rect;	//圆点所在的区域
	};

}

实现部分: SDrawRight.cpp

#include "stdafx.h"
#include "SDrawRight.h"

ULONG_PTR token;

namespace SOUI
{
	SDrawRight::SDrawRight():m_bflag(FALSE)
	{
		Gdiplus::GdiplusStartupInput input;
		Gdiplus::GdiplusStartup(&token, &input, NULL);
		m_evtSet.addEvent(EVENTID(EventCheckPink));	//增加一个消息事件 (自定义的)
		//m_evtSet.addEvent(EVENTID(EventCmd));
	}

	SDrawRight::~SDrawRight()
	{
		Gdiplus::GdiplusShutdown(token);
	}

	//自适应窗口大小
	void SDrawRight::GetSelfAdaptionSize(Gdiplus::Rect & rc)
	{
		int width = rc.Width;
		int height = rc.Height;

		int xwidth = abs(width - height);
		int yheight = min(width, height);

		if (width >= height)
		{
			width = height;
		}
		else
		{
			height = width;
		}
		
		//获取一个80%的框
		rc = Gdiplus::Rect(rc.X + xwidth + 0.1*width, rc.Y + 0.1*height, 0.8*width, 0.8*height);
	}

	void SDrawRight::OnLButtonDown(UINT nFlags, CPoint point)
	{
		//SMessageBox(NULL, L"test", L"tip", MB_OK);
		//判断这个点 是否在区域内
		
		Gdiplus::Region reg(m_rect);
		if (reg.IsVisible(point.x, point.y))
		{
			m_bflag = !m_bflag;
			Invalidate();
		}

		//触发自定义消息	(自定义消息)
		EventCheckPink evt(this);
		evt.bStatus = m_bflag;
		FireEvent(evt);	//激活事件
		
		//使用系统定义的消息
		//EventCmd evt(this);
		//FireEvent(evt);
		
	}

	void SOUI::SDrawRight::OnPaint(SOUI::IRenderTarget * pRT)
	{
		SWindow::OnPaint(pRT);
		using namespace Gdiplus;
		Gdiplus::Graphics g(pRT->GetDC());
		
		CRect rc = GetClientRect();
		int alpha = m_bflag ? 80 : 200;
		SolidBrush blackBrush(Color(alpha, 255, 157, 206));
		Gdiplus::Rect layoutRect_min(rc.left, rc.top, rc.Width(), rc.Height());

		GetSelfAdaptionSize(layoutRect_min);	//自适应的一个大小调整
		g.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality); //描边时消除锯齿
		g.FillEllipse(&blackBrush, layoutRect_min);
		m_rect = layoutRect_min;
	}

}

这里需要补充一点,在点击的事件处理中 也可以使用系统自带的EVT_CMD这个消息事件 只需要在调用的地方 Fire就行

例如:

//使用系统定义的消息
        //EventCmd evt(this);
        //FireEvent(evt);

这个就是使用系统自带的点击消息事件

这里还将Edit控件扩展了一下 加一个长度限制的属性 然后加一个只允许接收数字(这个在Edit属性中有 number="1"就是只能允许输入数字)

SMyEdit.h

#pragma once

#include <core/Swnd.h>

namespace SOUI
{
	class SMyEdit : public SEdit
	{
		SOUI_CLASS_NAME(SMyEdit,L"Myedit")

	public:
		SMyEdit();
		~SMyEdit();


	protected:
		void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);

		SOUI_ATTRS_BEGIN()
			ATTR_INT(L"length", m_Length, FALSE)
		SOUI_ATTRS_END()

		SOUI_MSG_MAP_BEGIN()
			MSG_WM_CHAR(OnChar)
		SOUI_MSG_MAP_END()

	private:
		int m_Length;
	};

}

SMyEdit.cpp

#include "stdafx.h"
#include "SMyEdit.h"

namespace SOUI
{
	SMyEdit::SMyEdit() :m_Length(0)
	{
	}


	SMyEdit::~SMyEdit()
	{
	}

	void SOUI::SMyEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
	{
		if (this->GetWindowTextLengthW() < m_Length)
		{
			if (isdigit(nChar))	//设置成只能读取数字
			{
				__super::OnChar(nChar, nRepCnt, nFlags);
			}
		}
	}
}

在XML中的使用:

<Myedit pos="256,72" size="96, 24" colorBkgnd="#FFFFFF" colorText="#000000" colorBorder="#BCC0CB" margin-x="1" margin-y="1" password="1" name="TestPassword" length="8"/>
<check_right pos="352,72" size="24, 24" name="checktest" trackMouseEvent="1" />

调用方法两种:

1.在CMainDlg::OnInitDialog中 直接使用

SDrawRight *p = FindChildByName2<SDrawRight>("checktest");
 p->GetEventSet()->subscribeEvent(EVT_CHECKPINK, Subscriber(&CMainDlg::OnCheckTest, this));

2. 使用类似事件中心的方法进行调用

EVENT_NAME_HANDLER(L"checktest", EVT_CHECKPINK, OnCheckTestFun2)

实现的函数如下:

//方法一调用
bool CMainDlg::OnCheckTest(EventArgs * pEvt)
{
	//SMessageBox(NULL, L"test", L"tip", MB_OK);
	EventCheckPink* pEven = sobj_cast<EventCheckPink>(pEvt);
	SASSERT(pEven);
	SMyEdit *pEdit = FindChildByName2<SMyEdit>(L"TestPassword");
	pEdit->SetAttribute(L"password", SStringT().Format(L"%d", (pEven->bStatus) ? 0 : 1));	//修改Edit的属性
	pEdit->Invalidate();
	
	return false;
}

//方法二调用
void CMainDlg::OnCheckTestFun2(EventArgs * pEvt)
{
	EventCheckPink* pEven = sobj_cast<EventCheckPink>(pEvt);
	SASSERT(pEven);
	SMyEdit *pEdit = FindChildByName2<SMyEdit>(L"TestPassword");
	pEdit->SetAttribute(L"password", SStringT().Format(L"%d", (pEven->bStatus) ? 0 : 1));	//修改Edit的属性
	pEdit->Invalidate();
}

最后实现的效果图:

 

 

参考文章:https://github.com/SOUI2/soui/wiki/%E7%AC%AC%E4%BA%8C%E5%8D%81%E5%85%AB%E7%AF%87%EF%BC%9ASOUI%E4%B8%AD%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A7%E4%BB%B6%E5%BC%80%E5%8F%91%E8%BF%87%E7%A8%8B
参考文章:https://blog.csdn.net/u012814856/article/details/77943271

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值