📄 gridctrl.cpp
字号:
// GridCtrl.cpp : implementation file
//
// MFC Grid Control
//
// Written by Chris Maunder
// mailto:Chris.Maunder@cbr.clw.csiro.au)
//
// Copyright (c) 1998.
//
// The code contained in this file is based on the original
// WorldCom Grid control written by Joe Willcoxson,
// mailto:chinajoe@aol.com
// http://users.aol.com/chinajoe
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name and all copyright
// notices remains intact. If the source code in this file is used in
// any commercial application then a statement along the lines of
// "Portions copyright (c) Chris Maunder, 1998" must be included in
// the startup banner, "About" box or printed documentation. An email
// letting me know that you are using it would be nice as well. That's
// not much to ask considering the amount of work that went into this.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability for any damage/loss of business that
// this product may cause.
//
// Expect bugs!
//
// Please use and enjoy, and let me know of any bugs/mods/improvements
// that you have found/implemented and I will fix/incorporate them into
// this file.
//
// History: 1.0 20 Feb 1998 First release version.
// 1.01 24 Feb 1998 Memory leak fix (Jens Bohlmann <bohly@jelo.de>)
// (assert bug in CMemDC.h - Claus Arend-Schneider)
// Bug in GetSelectedCount - Lyn Newton
// 1.02 4 Mar 1998 Scrolling a little neater (less dead area)
// Cell selection via OnTimer correctly updates Focus cell
// 1.03 17 Mar 1998 Clipboard functions added, Intellimouse support
// Using 32 bit scroll pos functions instead of 16 bit
// Added OLE drag and drop.
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "MemDC.h"
#include "GridCtrl.h"
#include "InPlaceEdit.h"
#include "InPlaceList.h"
#include <afxadv.h> // For CSharedFile
#include <afxole.h> // For COleDataSource
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define USE_MEMDC // Handy to comment this out to show flicker, which can show up
// unnecessary redraws.
#define SLOP 3 // When resizing columns/row, the cursor has to be within +/-3
// pixels of the dividing line for resizing to be possible
#define HEADER_HEIGHT 2 // For printing
#define FOOTER_HEIGHT 2
#define LEFT_MARGIN 4
#define RIGHT_MARGIN 4
#define TOP_MARGIN 1
#define BOTTOM_MARGIN 1
#define GAP 1
IMPLEMENT_DYNCREATE(CGridCtrl, CWnd)
// TODO:
// - Implement CGridDropTarget::OnDragScroll
// - OnOutOfMemory function instead of exceptions
// - Decrease timer interval over time to speed up selection over time
//
// NOTE: Grid data is stored row-by-row, so all operations on large numbers
// of cells should be done row-by-row as well.
void AFXAPI DDX_GridControl(CDataExchange* pDX, int nIDC, CGridCtrl& rControl)
{
if (rControl.GetSafeHwnd() == NULL) // not subclassed yet
{
ASSERT(!pDX->m_bSaveAndValidate);
HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
if (!rControl.SubclassWindow(hWndCtrl))
{
ASSERT(FALSE); // possibly trying to subclass twice?
AfxThrowNotSupportedException();
}
#ifndef _AFX_NO_OCC_SUPPORT
else
{
// If the control has reparented itself (e.g., invisible control),
// make sure that the CWnd gets properly wired to its control site.
if (pDX->m_pDlgWnd->GetSafeHwnd() != ::GetParent(rControl.GetSafeHwnd()))
rControl.AttachControlSite(pDX->m_pDlgWnd);
}
#endif //!_AFX_NO_OCC_SUPPORT
}
}
// Get the number of lines to scroll with each mouse wheel notch
// Why doesn't windows give us this function???
UINT GetMouseScrollLines()
{
int nScrollLines = 3; // reasonable default
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Control Panel\\Desktop"),
0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
{
TCHAR szData[128];
DWORD dwKeyDataType;
DWORD dwDataBufSize = sizeof(szData);
if (RegQueryValueEx(hKey, _T("WheelScrollLines"), NULL, &dwKeyDataType,
(LPBYTE) &szData, &dwDataBufSize) == ERROR_SUCCESS)
{
nScrollLines = _tcstoul(szData, NULL, 10);
}
RegCloseKey(hKey);
}
return nScrollLines;
}
/////////////////////////////////////////////////////////////////////////////
// CGridCtrl
CGridCtrl::CGridCtrl(int nRows, int nCols, int nFixedRows, int nFixedCols)
{
RegisterWindowClass();
// Initialize OLE libraries (if necessary)
_AFX_THREAD_STATE* pState = AfxGetThreadState();
if (!pState->m_bNeedTerm)
if (!AfxOleInit())
AfxMessageBox(_T("OLE initialization failed. Make sure that the OLE libraries are the correct version"));
// Store the system colours in case they change. The gridctrl uses
// these colours, and in OnSysColorChange we can check to see if
// the gridctrl colours have been changed from the system colours.
// If they have, then leave them, otherwise change them to reflect
// the new system colours.
m_crWindowText = ::GetSysColor(COLOR_WINDOWTEXT);
m_crWindowColour = ::GetSysColor(COLOR_WINDOW);
m_cr3DFace = ::GetSysColor(COLOR_3DFACE);
m_crShadow = ::GetSysColor(COLOR_3DSHADOW);
m_nRows = 0;
m_nCols = 0;
m_nFixedRows = 0;
m_nFixedCols = 0;
m_nDefCellHeight = 10; // These will get changed to something meaningful
m_nDefCellHeight = 30; // when the window is created or subclassed
m_nVScrollMax = 0; // Scroll position
m_nHScrollMax = 0;
m_nMargin = 0; // cell padding
m_nRowsPerWheelNotch = GetMouseScrollLines(); // Get the number of lines
// per mouse wheel notch to scroll
m_MouseMode = MOUSE_NOTHING;
m_nGridLines = GVL_BOTH;
m_bEditable = TRUE;
m_bListMode = FALSE;
m_bAllowDraw = TRUE; // allow draw updates
m_bEnableSelection = TRUE;
m_bAllowRowResize = TRUE;
m_bAllowColumnResize = TRUE;
m_bSortOnClick = TRUE; // Sort on header row click if in list mode
m_bAscending = TRUE; // sorting stuff
m_SortColumn = -1;
m_nTimerID = 0; // For drag-selection
m_nTimerInterval = 25; // (in milliseconds)
m_pImageList = NULL; // for drag and drop
m_bAllowDragAndDrop = FALSE;
m_pDropWnd = NULL;
m_pDragImage = NULL;
// Initially use the system message font for the GridCtrl font
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(NONCLIENTMETRICS);
VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0));
m_Font.CreateFontIndirect(&(ncm.lfMessageFont));
// Set up the initial grid size
SetRowCount(nRows);
SetColumnCount(nCols);
SetFixedRowCount(nFixedRows);
SetFixedColumnCount(nFixedCols);
// Set the colours
SetTextColor(m_crWindowText);
SetTextBkColor(m_crWindowColour);
SetBkColor(m_crShadow);
SetFixedTextColor(m_crWindowText);
SetFixedBkColor(m_cr3DFace);
// set initial selection range (ie. none)
m_SelectedCellMap.RemoveAll();
m_PrevSelectedCellMap.RemoveAll();
}
CGridCtrl::~CGridCtrl()
{
if (m_pDragImage) delete m_pDragImage;
if (m_Font.m_hObject) m_Font.DeleteObject();
DeleteAllItems();
DestroyWindow();
}
// Register the window class if it has not already been registered.
BOOL CGridCtrl::RegisterWindowClass()
{
WNDCLASS wndcls;
HINSTANCE hInst = AfxGetInstanceHandle();
if (!(::GetClassInfo(hInst, GRIDCTRL_CLASSNAME, &wndcls)))
{
// otherwise we need to register a new class
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
wndcls.hInstance = hInst;
wndcls.hIcon = NULL;
wndcls.hCursor = NULL;
wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = GRIDCTRL_CLASSNAME;
if (!AfxRegisterClass(&wndcls)) {
AfxThrowResourceException();
return FALSE;
}
}
return TRUE;
}
BOOL CGridCtrl::Create(const RECT& rect, CWnd* pParentWnd, UINT nID, DWORD dwStyle)
{
ASSERT(pParentWnd->GetSafeHwnd());
if (!CWnd::Create(GRIDCTRL_CLASSNAME, NULL, dwStyle, rect, pParentWnd, nID))
return FALSE;
OnSetFont( (LPARAM)((HFONT)m_Font), 0);
// The number of rows and columns will only be non-zero if the constructor
// was called with non-zero initialising parameters. If this window was created
// using a dialog template then the number of rows and columns will be 0 (which
// means that the code below will not be needed - which is lucky 'cause it ain't
// gonna get called in a dialog-template-type-situation.
try {
m_arRowHeights.SetSize(m_nRows); // initialize row heights
m_arColWidths.SetSize(m_nCols); // initialize column widths
m_arColType.SetSize(m_nCols);
}
catch (CMemoryException *e) {
e->ReportError();
e->Delete();
return FALSE;
}
for (int i = 0; i < m_nRows; i++) m_arRowHeights[i] = m_nDefCellHeight;
for (i = 0; i < m_nCols; i++)
{
m_arColWidths[i] = m_nDefCellWidth;
m_arColType[i] = GVET_NOEDIT;
}
ResetScrollBars();
return TRUE;
}
void CGridCtrl::PreSubclassWindow()
{
CWnd::PreSubclassWindow();
OnSetFont( (LPARAM)((HFONT)m_Font), 0);
m_DropTarget.Register(this);
ResetScrollBars();
}
BOOL CGridCtrl::SubclassWindow(HWND hWnd)
{
if (!CWnd::SubclassWindow(hWnd)) return FALSE;
OnSetFont( (LPARAM)((HFONT)m_Font), 0);
m_DropTarget.Register(this);
ResetScrollBars();
return TRUE;
}
BEGIN_MESSAGE_MAP(CGridCtrl, CWnd)
//{{AFX_MSG_MAP(CGridCtrl)
ON_WM_PAINT()
ON_WM_HSCROLL()
ON_WM_VSCROLL()
ON_WM_SIZE()
ON_WM_LBUTTONUP()
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_WM_TIMER()
ON_WM_CAPTURECHANGED()
ON_WM_GETDLGCODE()
ON_WM_KEYDOWN()
ON_WM_CHAR()
ON_WM_LBUTTONDBLCLK()
ON_WM_ERASEBKGND()
ON_WM_SETCURSOR()
ON_WM_SETTINGCHANGE()
ON_WM_SYSCOLORCHANGE()
ON_WM_MOUSEWHEEL()
//}}AFX_MSG_MAP
ON_MESSAGE(WM_SETFONT, OnSetFont)
ON_MESSAGE(WM_GETFONT, OnGetFont)
ON_MESSAGE(IPLM_FILL, OnFillList)
ON_NOTIFY_REFLECT_EX(LVN_ENDLABELEDIT, OnEndInPlaceEdit)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CGridCtrl message handlers
void CGridCtrl::OnPaint()
{
CPaintDC dc(this); // device context for painting
OnDraw(dc);
}
BOOL CGridCtrl::OnEraseBkgnd(CDC* /*pDC*/)
{
return TRUE; // Don't erase the background.
}
// Custom background erasure. This gets called from within the OnDraw function,
// since we will (most likely) be using a memory DC to stop flicker. If we just
// erase the background normally through OnEraseBkgnd, and didn't fill the memDC's
// selected bitmap with colour, then all sorts of vis problems would occur
void CGridCtrl::EraseBkgnd(CDC* pDC)
{
CRect VisRect, ClipRect, rect;
if (pDC->GetClipBox(ClipRect) == ERROR) return;
GetVisibleNonFixedCellRange(VisRect);
// Draw Fixed columns background
int nFixedColumnWidth = GetFixedColumnWidth();
if (ClipRect.left < nFixedColumnWidth && ClipRect.top < VisRect.bottom)
pDC->FillSolidRect(ClipRect.left, ClipRect.top,
nFixedColumnWidth - ClipRect.left+1,
VisRect.bottom - ClipRect.top, GetFixedBkColor());
// Draw Fixed rows background
int nFixedRowHeight = GetFixedRowHeight();
if (ClipRect.top < nFixedRowHeight &&
ClipRect.right > nFixedColumnWidth && ClipRect.left < VisRect.right)
pDC->FillSolidRect(nFixedColumnWidth-1, ClipRect.top,
VisRect.right - nFixedColumnWidth+1,
nFixedRowHeight - ClipRect.top, GetFixedBkColor());
// Draw non-fixed cell background
if (rect.IntersectRect(VisRect, ClipRect))
{
CRect CellRect(max(nFixedColumnWidth, rect.left), max(nFixedRowHeight, rect.top),
rect.right, rect.bottom);
pDC->FillSolidRect(CellRect, GetTextBkColor());
}
// Draw right hand side of window outside grid
if (VisRect.right < ClipRect.right)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -