MFC 自定义十六进制显示控件

cpp 复制代码
//MyHexView.h
#pragma once
class CMyHexView : public CWnd
{
	DECLARE_DYNAMIC(CMyHexView)

public:
	CMyHexView();
	virtual ~CMyHexView();

	void SetData(const CByteArray& data);  // 设置数据
	const CByteArray& GetData() const;    // 获取数据

protected:
	afx_msg void OnKillFocus(CWnd* pNewWnd); // 处理失去焦点事件
	DECLARE_MESSAGE_MAP()

private:
	CByteArray m_data;          // 存储数据的字节数组
	int m_nScrollPos;           // 滚动位置
	int m_nLinesPerPage;        // 每页显示的行数
	int m_nSelectedByte;        // 当前选中的字节索引
	bool m_bEditing;            // 是否正在编辑

	void DrawHexView(CDC* pDC); // 绘制十六进制视图
	void UpdateScrollBar();     // 更新滚动条
	void UpdateSelection();     // 更新选中状态
	void StartEditing();        // 开始编辑
	void EndEditing();          // 结束编辑

public:
	afx_msg void OnPaint();
	afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
	afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point); // 处理鼠标点击
	afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags); // 处理键盘输入
};
cpp 复制代码
//MyHexView.cpp
#include "stdafx.h"
#include "MyHexView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CMyHexView

IMPLEMENT_DYNAMIC(CMyHexView, CWnd)

CMyHexView::CMyHexView()
	: m_nScrollPos(0)
	, m_nLinesPerPage(0)
{
}

CMyHexView::~CMyHexView()
{
}

BEGIN_MESSAGE_MAP(CMyHexView, CWnd)
	ON_WM_PAINT()
	ON_WM_VSCROLL()
	ON_WM_MOUSEWHEEL()
	ON_WM_LBUTTONDOWN()
	ON_WM_CHAR()
	ON_WM_KILLFOCUS() // 添加失去焦点的消息处理
END_MESSAGE_MAP()

// CMyHexView 消息处理程序

void CMyHexView::SetData(const CByteArray& data)
{
	m_data.Copy(data);  // 使用 Copy 方法复制数据,而不是直接赋值
	m_nScrollPos = 0;
	UpdateScrollBar();
	Invalidate();
}

const CByteArray& CMyHexView::GetData() const
{
	return m_data;  // 返回 const 引用
}

void CMyHexView::OnPaint()
{
	CPaintDC dc(this); // 用于绘制的设备上下文

	CRect rect;
	GetClientRect(&rect);

	CDC memDC;
	memDC.CreateCompatibleDC(&dc);
	CBitmap memBitmap;
	memBitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
	CBitmap* pOldBitmap = memDC.SelectObject(&memBitmap);

	memDC.FillSolidRect(&rect, RGB(255, 255, 255));
	DrawHexView(&memDC);

	dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);

	memDC.SelectObject(pOldBitmap);
}

void CMyHexView::DrawHexView(CDC* pDC)
{
	CRect rect;
	GetClientRect(&rect);

	CFont font;
	font.CreatePointFont(100, _T("Courier New"));
	CFont* pOldFont = pDC->SelectObject(&font);

	int nLineHeight = pDC->GetTextExtent(_T("X")).cy;
	m_nLinesPerPage = rect.Height() / nLineHeight;

	int nLines = (m_data.GetSize() + 15) / 16;
	int nStartLine = m_nScrollPos;
	int nEndLine = min(nStartLine + m_nLinesPerPage, nLines);

	for (int i = nStartLine; i < nEndLine; ++i)
	{
		CString strLine;
		strLine.Format(_T("%08X  "), i * 16);

		for (int j = 0; j < 16; ++j)
		{
			int nIndex = i * 16 + j;
			if (nIndex < m_data.GetSize())
			{
				if (nIndex == m_nSelectedByte && m_bEditing)
				{
					pDC->SetTextColor(RGB(255, 0, 0)); // 选中字节高亮显示
				}
				else
				{
					pDC->SetTextColor(RGB(0, 0, 0));
				}

				strLine.AppendFormat(_T("%02X "), m_data[nIndex]);
			}
			else
			{
				strLine += _T("   ");
			}
		}

		strLine += _T(" ");
		for (int j = 0; j < 16; ++j)
		{
			int nIndex = i * 16 + j;
			if (nIndex < m_data.GetSize())
			{
				BYTE b = m_data[nIndex];
				if (b >= 32 && b <= 126)
				{
					strLine += (TCHAR)b;
				}
				else
				{
					strLine += _T(".");
				}
			}
			else
			{
				strLine += _T(" ");
			}
		}

		pDC->TextOut(10, (i - nStartLine) * nLineHeight, strLine);
	}

	pDC->SelectObject(pOldFont);
}
void CMyHexView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	int nScrollPos = m_nScrollPos;

	switch (nSBCode)
	{
	case SB_LINEUP:
		nScrollPos -= 1;
		break;
	case SB_LINEDOWN:
		nScrollPos += 1;
		break;
	case SB_PAGEUP:
		nScrollPos -= m_nLinesPerPage;
		break;
	case SB_PAGEDOWN:
		nScrollPos += m_nLinesPerPage;
		break;
	case SB_THUMBTRACK:
		nScrollPos = nPos;
		break;
	}

	int nLines = (m_data.GetSize() + 15) / 16;
	nScrollPos = max(0, min(nScrollPos, nLines - m_nLinesPerPage));

	if (nScrollPos != m_nScrollPos)
	{
		m_nScrollPos = nScrollPos;
		UpdateScrollBar();
		Invalidate();
	}
}

BOOL CMyHexView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	int nScrollPos = m_nScrollPos - zDelta / WHEEL_DELTA;
	int nLines = (m_data.GetSize() + 15) / 16;
	nScrollPos = max(0, min(nScrollPos, nLines - m_nLinesPerPage));

	if (nScrollPos != m_nScrollPos)
	{
		m_nScrollPos = nScrollPos;
		UpdateScrollBar();
		Invalidate();
	}

	return TRUE;
}

void CMyHexView::UpdateScrollBar()
{
	SCROLLINFO si;
	si.cbSize = sizeof(SCROLLINFO);
	si.fMask = SIF_ALL;
	si.nMin = 0;
	si.nMax = (m_data.GetSize() + 15) / 16 - 1;
	si.nPage = m_nLinesPerPage;
	si.nPos = m_nScrollPos;
	SetScrollInfo(SB_VERT, &si, TRUE);
}
void CMyHexView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// 计算点击的字节索引
	CRect rect;
	GetClientRect(&rect);

	CDC* pDC = GetDC();
	CFont font;
	font.CreatePointFont(100, _T("Courier New"));
	CFont* pOldFont = pDC->SelectObject(&font);

	int nLineHeight = pDC->GetTextExtent(_T("X")).cy;
	int nCharWidth = pDC->GetTextExtent(_T("X")).cx;

	int nLine = (point.y / nLineHeight) + m_nScrollPos;
	int nColumn = (point.x - 10) / (3 * nCharWidth); // 每个字节占 3 个字符宽度

	if (nColumn >= 0 && nColumn < 16 && nLine >= 0 && nLine < (m_data.GetSize() + 15) / 16)
	{
		m_nSelectedByte = nLine * 16 + nColumn;
		if (m_nSelectedByte >= m_data.GetSize())
		{
			m_nSelectedByte = -1;
		}
		else
		{
			StartEditing();
		}
	}
	else
	{
		m_nSelectedByte = -1;
	}

	pDC->SelectObject(pOldFont);
	ReleaseDC(pDC);

	UpdateSelection();
	CWnd::OnLButtonDown(nFlags, point);
}

void CMyHexView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if (m_bEditing && m_nSelectedByte >= 0)
	{
		// 只允许输入十六进制字符 (0-9, A-F, a-f)
		if ((nChar >= '0' && nChar <= '9') || (nChar >= 'A' && nChar <= 'F') || (nChar >= 'a' && nChar <= 'f'))
		{
			// 将字符转换为十六进制值
			BYTE b = 0;
			if (nChar >= '0' && nChar <= '9')
			{
				b = nChar - '0';
			}
			else if (nChar >= 'A' && nChar <= 'F')
			{
				b = nChar - 'A' + 10;
			}
			else if (nChar >= 'a' && nChar <= 'f')
			{
				b = nChar - 'a' + 10;
			}

			// 更新数据
			if (m_bEditing)
			{
				m_data[m_nSelectedByte] = (m_data[m_nSelectedByte] << 4) | b;
				Invalidate(); // 刷新显示
			}
		}
	}

	CWnd::OnChar(nChar, nRepCnt, nFlags);
}

void CMyHexView::StartEditing()
{
	m_bEditing = true;
	Invalidate();
}

void CMyHexView::EndEditing()
{
	m_bEditing = false;
	m_nSelectedByte = -1;
	Invalidate(); // 刷新显示
}

void CMyHexView::UpdateSelection()
{
	Invalidate();
}
void CMyHexView::OnKillFocus(CWnd* pNewWnd)
{
	if (m_bEditing)
	{
		EndEditing(); // 结束编辑
	}

	CWnd::OnKillFocus(pNewWnd);
}
cpp 复制代码
//主类中
int C改变Dlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CDialogEx::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码

	WNDCLASS wc;
	memset(&wc, 0, sizeof(WNDCLASS));
	wc.lpfnWndProc = ::DefWindowProc; // 使用默认窗口过程
	wc.hInstance = AfxGetInstanceHandle();
	wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.lpszClassName = _T("CMyHexView"); // 类名必须与控件类名一致
	if (!AfxRegisterClass(&wc))
	{
		AfxMessageBox(_T("Failed to register custom control class!"));
		return FALSE;
	}
	return 0;
}
BOOL C改变Dlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	// 将"关于..."菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	if (!m_hexView.SubclassDlgItem(IDC_CUSTOM2, this))
	{
		AfxMessageBox(_T("Failed to subclass custom control!"));
		return FALSE;
	}
	CFile file;
	if (file.Open(_T("D:\\miniout.txt"), CFile::modeRead))
	{
		CByteArray data;
		data.SetSize(file.GetLength());
		file.Read(data.GetData(), file.GetLength());
		file.Close();

		m_hexView.SetData(data);  // 传递 CByteArray 的引用
	}

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}


相关推荐
paterWang1 小时前
基于 Python 和 OpenCV 的酒店客房入侵检测系统设计与实现
开发语言·python·opencv
东方佑1 小时前
使用Python和OpenCV实现图像像素压缩与解压
开发语言·python·opencv
mit6.8242 小时前
[实现Rpc] 通信类抽象层 | function | using | 解耦合设计思想
c++·网络协议·rpc
我真不会起名字啊2 小时前
“深入浅出”系列之杂谈篇:(3)Qt5和Qt6该学哪个?
开发语言·qt
laimaxgg2 小时前
Qt常用控件之单选按钮QRadioButton
开发语言·c++·qt·ui·qt5
水瓶丫头站住2 小时前
Qt的QStackedWidget样式设置
开发语言·qt
小钊(求职中)3 小时前
Java开发实习面试笔试题(含答案)
java·开发语言·spring boot·spring·面试·tomcat·maven
ox00804 小时前
C++ 设计模式-命令模式
c++·设计模式·命令模式
慕诗客5 小时前
QT基于Gstreamer采集的简单示例
开发语言·qt
Blasit5 小时前
C++ Qt建立一个HTTP服务器
服务器·开发语言·c++·qt·http