На этом шаге мы рассмотрим отображение данных в приложении STUpload.
Теперь все готово для реализации графического отображения данных, относящихся к выбранной ценной бумаге. Сначала нужно определить размер документа, чтобы класс CScrollView правильно отображал полосы прокрутки. После загрузки очередного текстового файла количество данных приложения STUpload увеличивается, однако размеры представления всегда остаются неизменными - в нем отображается только одна диаграмма. Этот размер соответствует печатной странице с альбомной ориентацией.
sizeTotal.cx = sizeTotal.cy = 100; SetScrollSizes(MM_TEXT, sizeTotal);
sizeTotal.cx = 1100; sizeTotal.cy = 850; SetScrollSizes(MM_LOENGLISH, sizeTotal);
Теперь изаменим функцию CView::OnDraw(). Эта функция считывает из файла данные, относящиеся к определенной ценной бумаге, и помещает их во временный массив. На основе этих данных вычисляются масштабы для оси времени (ось х) и оси цен (ось у). Данные представлены в виде ломаной линии, позволяющей оператору легко находить ошибки. Обратите внимание, как в приведенном ниже коде выполняется вывод данных в контекст устройства средствами MFC-классов инструментов рисования и графических GDI-функций.
void CSTUploadView::OnDraw(CDC* pDC) { CSTUploadDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // Сохраняем текущее состояние контекста устройства int nDC = pDC->SaveDC(); const CStockDataList & pData = pDoc->GetDocList(); // Создаем небольшой массив для // хранения записей текущей ценной бумаги. // Он нужен для индексного доступа к данным. CArray< CStockData, CStockData & > arrFundData; POSITION pos = pData.GetHeadPosition(); while( pos ) { CStockData sd = pData.GetNext( pos ); if( sd.GetFund() == pDoc->GetCurrentFund() ) arrFundData.Add( sd ); } int nPrices = arrFundData.GetSize(); if( nPrices == 0 ) return; // Константы для размеров (в единицах устройства) const int AXIS_DIVIDER_LENGTH = 6; const int AXIS_FONT_HEIGHT = 24; const int HEADING_FONT_HEIGHT = 36; // Создаем шрифт для меток на осях CFont AxisFont; if( AxisFont.CreateFont( AXIS_FONT_HEIGHT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, FF_ROMAN, 0 )) pDC->SelectObject( &AxisFont ); else { AfxMessageBox( "Unable to create Axis font" ); return; } CPen AxisPen; if( AxisPen.CreatePen( PS_SOLID, 1, RGB(0,0,0) ) ) pDC->SelectObject( &AxisPen ); else { AfxMessageBox( "Unable to create Axis Pen" ); return; } // Массив координат диаграммы CArray< CPoint, CPoint& > CoordArray; for( int i = 0; i < nPrices; i++ ) CoordArray.Add( CPoint( 0, 0) ); // Устанавливаем начало области просмотра // в нижний угол окна CPoint ptBottomLeft( 0, -850 ); pDC->LPtoDP( &ptBottomLeft ); pDC->SetViewportOrg( ptBottomLeft ); // Базовые координаты осей const CPoint ORIGIN(100, 100); const CPoint Y_EXTENT( ORIGIN.x, ORIGIN.y + 650); const CPoint X_EXTENT(ORIGIN.x + 900, ORIGIN.y); // Оси рисования pDC->MoveTo( Y_EXTENT ); pDC->LineTo( ORIGIN ); pDC->LineTo( X_EXTENT ); int nLabelPos = Y_EXTENT.y + ((ORIGIN.y - Y_EXTENT.y) / 2); pDC->TextOut( ORIGIN.x - 50, nLabelPos, '$' ); // Разбиваем ось x на число записей для цен, // взятых из файла int nXIncrement = (X_EXTENT.x - ORIGIN.x) / nPrices; double nMaxPrice = 0; double nMinPrice = 0; for( i = 0; i < nPrices; i++ ) { int xPoint = (ORIGIN.x + (i * nXIncrement)); CoordArray[i].x = xPoint; pDC->MoveTo( xPoint, ORIGIN.y ); pDC->LineTo( xPoint, ORIGIN.y + AXIS_DIVIDER_LENGTH ); COleDateTime aDate = arrFundData[ i ].GetDate(); double aPrice = arrFundData[ i ].GetPrice(); nMaxPrice = max( nMaxPrice, aPrice ); nMinPrice = nMinPrice == 0 ? nMaxPrice : min( nMinPrice, aPrice ); CString strDate = aDate.Format( "%m/%d/%y" ); if( i == 0 || i == (nPrices-1) ) pDC->TextOut( xPoint-2, ORIGIN.y - AXIS_FONT_HEIGHT / 2, strDate ); else { CString strDay = strDate.Mid( strDate.Find( '/' ) + 1); strDay = strDay.Left( strDay.Find( '/' ) ); pDC->TextOut( xPoint-6, ORIGIN.y - AXIS_FONT_HEIGHT / 2, strDay ); } } // Устанавливаем для оси y масштаб, основанный на // разнице между максимальной и минимальной ценой nMaxPrice += 2.0; nMinPrice -= 1.0; int iScale = int(nMaxPrice) - int(nMinPrice); int nYIncrement = ( ORIGIN.y - Y_EXTENT.y ) / iScale; for( i = 0; i < iScale; i++ ) { int yPoint = (ORIGIN.y - (i * nYIncrement)); pDC->MoveTo( ORIGIN.x, yPoint); pDC->LineTo( ORIGIN.x - AXIS_DIVIDER_LENGTH, yPoint); int iCurrentPrice = int(nMinPrice) + i; for( int j = 0; j < nPrices; j++ ) { double aPrice = arrFundData[ j ].GetPrice(); if( aPrice >= double(iCurrentPrice) && aPrice < double(iCurrentPrice) + 1.0 ) { double dFraction = aPrice - double(iCurrentPrice); CoordArray[j].y = yPoint - int(dFraction * double(nYIncrement)); } } CString strPrice; strPrice.Format( "%d", iCurrentPrice ); int nTextSize = pDC->GetTextExtent( strPrice ).cx; nTextSize += 10; pDC->TextOut( ORIGIN.x - nTextSize, yPoint+12, strPrice ); } // Отображаем данные из массива CoordArray CPen GraphPen; if( GraphPen.CreatePen( PS_SOLID, 1, RGB(255,0,0) ) ) // Красное перо { pDC->SelectObject( &GraphPen ); } else { AfxMessageBox( "Unable to create Graph Pen" ); return; } // Рисуем диаграмму // Отображаем значения цены около вершин диаграммы // (голубым цветом) COLORREF crOldText = pDC->SetTextColor( RGB( 0,0,255 ) ); pDC->MoveTo( CoordArray[0] ); for( i = 0; i < nPrices; i++ ) { pDC->LineTo( CoordArray[i] ); CPoint TextPoint; if( (i+1) < nPrices ) { if( CoordArray[i+1].y >= CoordArray[ i ].y ) TextPoint = CoordArray[i] + CPoint( 5, 0 ); else TextPoint = CoordArray[i] + CPoint( 5, AXIS_FONT_HEIGHT ); } else TextPoint = CoordArray[i] + CPoint( 5, 0); CString strPrice; strPrice.Format( "%.2f", arrFundData[i].GetPrice() ); pDC->TextOut( TextPoint.x, TextPoint.y, strPrice ); } pDC->SetTextColor( crOldText ); // Создаем заголовок CFont HeadingFont; if( HeadingFont. CreateFont( HEADING_FONT_HEIGHT, 0, 0, 0, FW_BOLD, 1, 0, 0, 0, 0, 0, 0, FF_ROMAN, 0 )) pDC->SelectObject( &HeadingFont ); else { AfxMessageBox( "Unable to create Heading Font" ); return; } CString strHeading = pDoc->GetCurrentFund(); strHeading += " - Closing Prices "; COleDateTime aDate = arrFundData[ 0 ].GetDate(); strHeading += aDate.Format( "%m/%d/%y" ); strHeading += " to "; aDate = arrFundData[ nPrices - 1 ].GetDate(); strHeading += aDate.Format( "%m/%d/%y" ); CSize sizeText = pDC->GetTextExtent( strHeading ); pDC->TextOut( X_EXTENT.x - sizeText.cx, Y_EXTENT.y + sizeText.cy, strHeading ); // Восстанавливаем первоначальный контекст устройства pDC->RestoreDC( nDC ); }
Рис.1.Приложение STUpload
Текст измененного приложения можно взять здесь (62,5 Кб).
На следующем шаге мы рассмотрим основные методы класса CButton.