Introduction
This Sample demonstrates basic drawing techniques using GDI+
in a Managed C++ application. The application implements a
control which acts as a ticker which scrolls
text across a window at a constant speed. The Client can
control the scroll speed (how often ticker moves), the scroll smoothness
(how many pixels it moves in one step), and the text to display.
The control uses an "off screen" painting technique similar to drawing
to memory device contexts.
Let's start from the Managed C++ Class Library.
The application wizard creates the TickerControl.h and TickerControl.cpp
files for us, and created a managed class TickerControl.
We do not want to write the control from scratch, so let's derive it
from the Control class in the System::Windows:Forms namespace and
inherit the functionality of Control.
In order to do that we should add code to access the .NET framework classes.
Add following lines to the top of your Control header file.
#using <System.dll>
#using <System.Drawing.DLL>
#using <System.Windows.Forms.dll>
using namespace System;
using namespace System::Drawing;
using namespace System::Windows::Forms;
Class members that are used by the control are defined in the Control Members section
of the control's header class.
In the constructor we create a timer and subscribe to a Tick event.
CTickerControl() : m_nScrollSmoothness(1),
m_strTickerText(S"Ticker"),
m_nOffset(0),
m_nTickerWidth(0),
m_bmpTicker(NULL)
{
m_tmTicker = new Timer();
m_tmTicker->set_Interval(10);
m_tmTicker->add_Tick(new EventHandler(this, &CTickerControl::OnTimer));
}
In the destructor we unsubscribe from the timer event.
~CTickerControl()
{
m_tmTicker->remove_Tick(new EventHandler(this, &CTickerControl::OnTimer));
}
Some of them we need to expose as control properties. The Control Properties
section of the Control header class demonstrates how to do this. All properties
have get_ and set_ functions, so they are accessible for reading and writing. You
can omit one of the function and get read-only or write-only properties. The
following code implements the TickerText property.
__property String* get_TickerText()
{
return m_strTickerText;
}
__property void set_TickerText(String *strTickerText)
{
m_strTickerText = strTickerText;
if(m_bmpTicker != NULL)
m_bmpTicker = CreateBitmap();
}
The StartTicker
and StopTicker
methods start and stop the text moving.
Internally it just starts and stops timer.
void StartTicker()
{
m_tmTicker->Start();
}
void StopTicker()
{
m_tmTicker->Stop();
}
The bitmap that we use for the control face is created by the Create
bitmap function.
At first it creates a graphics object that is used by the control window to paint.
graphMeasure = Graphics::FromHwnd(this->get_Handle());
We use
sizeString = graphMeasure->MeasureString(m_strTickerText, font);
to define how many pixels the Ticket Text string occupies.
After that it creates a bitmap that has a width equal to the width
of the control client rectangle plus the width of the Ticker text string.
In order to draw on the bitmap we get a pointer to the graphics object
associated with bitmap using
graphImage = Graphics::FromImage(bmpTicker);
The rest of the code just fills a bitmap background and draws the Ticker text
string to the bitmap.
Bitmap* CreateBitmap()
{
Bitmap* bmpTicker;
Graphics* graphImage;
Graphics* graphMeasure;
SizeF sizeString;
System::Drawing::Font* font;
SolidBrush* brush;
Rectangle rect;
int nBmpWidth;
int nBmpHeight;
int nRepeat;
graphMeasure = Graphics::FromHwnd(this->get_Handle());
font = new System::Drawing::Font("Courier", 10);
brush = new SolidBrush(get_BackColor());
sizeString = graphMeasure->MeasureString(m_strTickerText, font);
m_nTickerWidth = (int)sizeString.get_Width();
rect = this->get_ClientRectangle();
nBmpWidth = rect.get_Width() + m_nTickerWidth;
nRepeat = (int)(nBmpWidth/m_nTickerWidth + 1);
nBmpHeight = rect.get_Height();
bmpTicker = new Bitmap(nBmpWidth,
nBmpHeight,
graphMeasure);
graphImage = Graphics::FromImage(bmpTicker);
graphImage->FillRectangle(brush, 0, 0, nBmpWidth, nBmpHeight);
for(int nCounter = 0; nCounter < nRepeat; nCounter++)
graphImage->DrawString(m_strTickerText, font, Brushes::Black, nCounter * m_nTickerWidth, 0);
return bmpTicker;
}
OnPaint
is a function that do actual painting job. We overwrite this virtual
function to do our painting.
All we do here is just get a pointer to the graphics object to draw
and draw our bitmap using DrawImageUnscaled function using current offset.
virtual void OnPaint(PaintEventArgs* pe)
{
Graphics* graph;
graph = pe->get_Graphics();
if(m_bmpTicker == NULL)
m_bmpTicker = CreateBitmap();
graph->DrawImageUnscaled(m_bmpTicker, Point((m_nOffset - m_nTickerWidth), 0));
}
Drawing or bitmap we cover all control client area, so n order to avoid flickering we
should overwrite OnPaintBackground virtual function with doing nothing inside.
virtual void OnPaintBackground(PaintEventArgs* pe)
{
}
The last thing we should do is to implement Timer event handler.
Here we just adjust bitmap offset and redraw control.
void OnTimer( Object* myObject, EventArgs* myEventArgs )
{
m_nOffset -= m_nScrollSmoothness;
if(m_nOffset < 0)
m_nOffset = m_nTickerWidth - m_nScrollSmoothness;
Invalidate();
Update();
}
To create a client we need to create Managed C++ Application
and add a code that implements a windows form to the main
source file, it is very similar what we did for the control.
Following code illustrates it.
#include "stdafx.h"
#using <mscorlib.dll>
#using <System.dll>
#using <System.Drawing.DLL>
#using <System.Windows.Forms.dll>
#using "..\Bin\TickerControl.dll"
using namespace System;
using namespace System::ComponentModel;
using namespace System::Drawing;
using namespace System::Windows::Forms;
__gc class CTickerClientForm : public Form
{
public:
CTickerClientForm()
{
ctrlTicker = new CTickerControl();
InitForm();
}
protected:
void InitForm()
{
this->set_Text(S"Ticker");
this->set_Size(System::Drawing::Size(400, 300));
ctrlTicker->set_Location(System::Drawing::Point(30, 100));
ctrlTicker->set_Size(System::Drawing::Size(300, 100));
ctrlTicker->set_TimerInterval(10);
ctrlTicker->set_TickerText(S"This is very very ... long string.");
ctrlTicker->set_ScrollSmoothness(1);
ctrlTicker->StartTicker();
this->get_Controls()->Add(this->ctrlTicker);
}
private:
CTickerControl* ctrlTicker;
};
int main(void)
{
try
{
Application::Run(new CTickerClientForm());
}
catch(Exception* e)
{
Console::Write("Error occured: ");
Console::WriteLine(e->get_Message());
}
return 0;
}
History
Oct 19 2001 - updated source files for beta 2