Objective Grid: Displaying the append row at the top of a browser grid instead of the bottom

Article ID: 520
Last updated: 05 Jan, 2012
Article ID: 520
Last updated: 05 Jan, 2012
Revision: 1
Views: 11100
Posted: 11 Jan, 2001
by Meltreger B.
Updated: 05 Jan, 2012
by Meltreger B.
Problem

CGXBrowserGrid and its derivative view and window classes allow the user to add rows to the data source. When the user enters data in the last row, a new record is appended. This append row is marked with an asterisk in the row header.

In some cases, it may be more convenient if this append row is placed at the top of the grid instead of at the bottom.

This article describes how you can change the behavior of CGXBrowserGrid and its derivatives to display the append row as the first row of the grid.

Normally, when the user begins entering data in the append row, the append row becomes the edit row (marked with a pencil icon in the row header) and a new append row is created. This behavior is less desirable when the append row is at the top. In this implementation, the append row becomes the edit row (the asterisk changes into a pencil) when the user begins to edit, but the new append row is not added until the user commits the new row of data (moves off of the append row).

The append row is frozen so that it is always visible. In the OnLoadCellStyle() override, the border and frame of the append row are modified to highlight this special row and separate it from the rest of the grid. You can adapt this feature to meet your needs.




Cause




Action

To display the append row at the top of the grid instead of the bottom, make the changes listed below. The class names correspond to classes in the GXQuery sample.

You may download this code in a usable form as AppendOnTop.cpp.

Or you can download a modified version of the GXQuery sample that implements these changes - AppendOnTop.zip.
This sample includes modifications to the CGXRecordInfoWnd and CGXRecordInfoSplitterWnd classes that adjust the record number and record count for the append row.

// Turning off m_bCacheCurrentCell will solve some redraw
// problems related to the append-row-on-top impl.

void CGxstudentView::OnInitialUpdate()
{
	........

	CMyRecordView::OnInitialUpdate();

	m_bCacheCurrentCell = FALSE;

	........
}

Add the following overrides to CGXRecordView-based class

// CMyRecordView header
	virtual void OnInitialUpdate();
	virtual BOOL OnLoadCellStyle(ROWCOL nRow, ROWCOL nCol, CGXStyle& style, LPCTSTR pszExistingValue);
	virtual BOOL OnFlushRecord(ROWCOL nRow, CString* ps = NULL);
	virtual ROWCOL GetAppendRow();
	virtual void DeleteRows(const CRowColArray& awRowStart, const CRowColArray& awRowEnd);
	virtual BOOL AddNew();
	virtual ROWCOL GetLastRow();


// CMyRecordView implementation

// Freeze the first row = append row
void CMyRecordView::OnInitialUpdate()
{
	CGXRecordView::OnInitialUpdate();

	SetFrozenRows(1, 0);
}

// Adjust the row/col indexes before passing it
// to the base class to hide the fact that the
// append row is actually the first row 
// rather than the usual bottom row.
BOOL CMyRecordView::OnLoadCellStyle(ROWCOL nRow, ROWCOL nCol, CGXStyle& style, LPCTSTR pszExistingValue)
{
	ROWCOL l_nRow;
// This section is not needed if the second 
// append/edit row is not displayed

// when the user starts to edit the append row
/*	if (GetBrowseParam()->m_nEditMode == addnew)
	{
		if (nRow == 1)
			l_nRow = GetRowCount();
		else if (nRow == 0)
			l_nRow = 0;
		else if (nRow == 2)
			l_nRow = GetRowCount() - 1;
		else
			l_nRow = nRow - 2;
	}
	else
*/	{
		if (nRow == 1)
			l_nRow = GetRowCount();
		else if (nRow == 0)
			l_nRow = 0;
		else
			l_nRow = nRow - 1;
	}
	BOOL bSuccess = CGXRecordView::OnLoadCellStyle(l_nRow, nCol,style,pszExistingValue);

	// Highlight/separate the append row
	if(nRow==1)
	{
		style.SetBorders(gxBorderBottom, CGXPen().SetStyle(PS_SOLID).SetColor(RGB(255,0,0)).SetWidth(3));
		style.SetDraw3dFrame(gxFrameInset);
	}

	return bSuccess;
}

// Adjust the row/col indexes before passing 
// it to the base class to hide the fact that
// the append row is actually the first row 
// rather than the usual bottom row.
BOOL CMyRecordView::OnFlushRecord(ROWCOL nRow, CString* ps /* = NULL */)
{
	ROWCOL l_nRow;
// This section is not needed if the second append/edit row is not displayed
// when the user starts to edit the append row
/*	if (GetBrowseParam()->m_nEditMode == addnew)
	{
		if (nRow == 1)
			l_nRow = GetRowCount();
		else if (nRow == 0)
			l_nRow = 0;
		else if (nRow == 2)
			l_nRow = GetRowCount() - 1;
		else
			l_nRow = nRow - 2;
	}
	else
*/	{
		if (nRow == 1)
			l_nRow = GetRowCount();
		else if (nRow == 0)
			l_nRow = 0;
		else
			l_nRow = nRow - 1;
	}
	return CGXRecordView::OnFlushRecord(l_nRow, ps);
}

// Override the base class functionality 
// which would normally return the bottom row.
ROWCOL CMyRecordView::GetAppendRow()
{
	return 1;
}

// Adjust the row/col indexes before passing it 
// to the base class to hide the fact that the
// append row is actually the first row 
// rather than the usual bottom row.
void CMyRecordView::DeleteRows(const CRowColArray& awRowStart, const CRowColArray& awRowEnd)
{
	// Subtract 1 from the entries in the array.
	// Remove the const.
	CRowColArray* pawRowStart = const_cast(&awRowStart);
	CRowColArray* pawRowEnd = const_cast(&awRowEnd);

	for (int index = awRowEnd.GetUpperBound(); index >= 0; index--)
		(*pawRowEnd)[index]--;

	for (index = awRowStart.GetUpperBound(); index >= 0; index--)
		(*pawRowStart)[index]--;

	CGXRecordView::DeleteRows(awRowStart, awRowEnd);
}

// Override of AddNew() that prevents the 
// second append/edit row from appearing
// when the user starts to edit the append row
ROWCOL CMyRecordView::AddNew()
{
	CGXBrowseParam* pBrowseData = GetBrowseParam();

	if (!CanAppend())
		AfxThrowNotSupportedException();

	// empty edit buffer, change edit mode
	CancelRecord();
	OnTestGridDimension(GX_MAXROWCOL, 0);
	ROWCOL nAppendRow = GetAppendRow();

	if (!IsCurrentCell(nAppendRow, GX_INVALID))
		SetCurrentCell(nAppendRow, GetHeaderCols()+1);

	// Check if we could succesfully position to the new record.
	if (pBrowseData->m_nCurrentRow != nAppendRow)
	{
		TRACE0(CGXBrowserGrid::AddNew failed to position current cell.
);
		return GX_INVALID;
	}

	ASSERT(pBrowseData->m_nCurrentRow != GX_INVALID);
	pBrowseData->m_nEditMode = addnew;

	// if last row is not completely visible, 
	// scroll down one row
/*	BOOL bLastRowVisible;
	ROWCOL nBottomRow = CalcBottomRowFromRect(GetGridRect(), bLastRowVisible);

	BOOL bScroll = (!bLastRowVisible && nBottomRow >= pBrowseData->m_nCurrentRow - 1);

	// avoid flickering because of possible scrollbar interactions
	LockScrollbars(TRUE);
*/
	// update the screen with an appended row
//	UpdateInsertRows(pBrowseData->m_nCurrentRow + 1, 1, GX_INVALIDATE, TRUE);
	RedrawRowCol(pBrowseData->m_nCurrentRow, 0, GX_UPDATENOW, TRUE);

/*	if (bScroll)
		OnScrollBecauseOfEdit(GX_DOWN, 1);

	// reenable scrollbars and update them
	LockScrollbars(FALSE);
*/	return nAppendRow;
}

// Override of GetLastRow() that eliminates the 
// adjustment for the second append row
ROWCOL CMyRecordView::GetLastRow()
{
	// estimate record count (if not yet known)
	CGXBrowseParam* pBrowseData = GetBrowseParam();
	long lCount = OnGetRecordCount();

	// check if OnGetRecordCount() did return the last seen record
	if (lCount == LONG_MAX)
		lCount = pBrowseData->m_nRecordLastSeen+1;

	if (pBrowseData->m_bEOFSeen)
	{
		if (!IsPrinting())
		{
			if (CanAppend())
				lCount++;

// Don't adjust for second append row
// which we are not using
//			if (pBrowseData->m_nEditMode == addnew)
//				lCount++;
		}
	}

	return lCount + GetHeaderRows();
}

Created: 11/17/99

This article was:   Helpful | Not helpful
Report an issue
Article ID: 520
Last updated: 05 Jan, 2012
Revision: 1
Views: 11100
Posted: 11 Jan, 2001 by Meltreger B.
Updated: 05 Jan, 2012 by Meltreger B.

Others in this category