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
}