在托管 C++ 应用程序中使用 GDI+。





4.00/5 (5投票s)
2001 年 5 月 31 日
3分钟阅读

129145

3022
此示例演示了在托管 C++ 应用程序中使用 GDI+ 的基本绘图技术。
引言
此示例演示了在托管 C++ 应用程序中使用 GDI+ 的基本绘图技术。 该应用程序实现了一个控件,该控件充当一个刻度器,该刻度器以恒定速度在窗口中滚动文本。 客户端可以控制滚动速度(刻度器移动的频率)、滚动平滑度(一步移动多少像素)以及要显示的文本。
该控件使用类似于绘制到内存设备上下文的“离屏”绘制技术。
让我们从托管 C++ 类库开始。
应用程序向导为我们创建了 TickerControl.h 和 TickerControl.cpp 文件,并创建了一个托管类 TickerControl。 我们不想从头开始编写控件,因此让我们从 System::Windows:Forms 命名空间中的 Control 类派生,并继承 Control 的功能。
为了做到这一点,我们应该添加代码来访问 .NET 框架类。 将以下行添加到控件头文件的顶部。 #using <System.dll> #using <System.Drawing.DLL> #using <System.Windows.Forms.dll> using namespace System; using namespace System::Drawing; using namespace System::Windows::Forms;
控件使用的类成员在控件头类的“控件成员”部分中定义。
在构造函数中,我们创建一个计时器并订阅 Tick 事件。
CTickerControl() : m_nScrollSmoothness(1), m_strTickerText(S"Ticker"), m_nOffset(0), m_nTickerWidth(0), m_bmpTicker(NULL) { // Create timer and set interval. m_tmTicker = new Timer(); m_tmTicker->set_Interval(10); // Subscribe for timer events. m_tmTicker->add_Tick(new EventHandler(this, &CTickerControl::OnTimer)); }
在析构函数中,我们取消订阅计时器事件。
// Destructor. ~CTickerControl() { // Unsubscribe from timer event. m_tmTicker->remove_Tick(new EventHandler(this, &CTickerControl::OnTimer)); }
其中一些我们需要公开为控件属性。 控件头类的“控件属性”部分演示了如何执行此操作。 所有属性都有 get_ 和 set_ 函数,因此可以进行读取和写入。 您可以省略其中一个函数,并获得只读或只写属性。 以下代码实现了 TickerText 属性。
// Text to show in the ticker. __property String* get_TickerText() { return m_strTickerText; } __property void set_TickerText(String *strTickerText) { m_strTickerText = strTickerText; // Recreate face image bitmap for the new Ticker text. // But we shouldn't do it before the first OnPaint call, // otherwise both of the bitmap dimensions will be 0 // and Bitmap constructor will throw the exception. if(m_bmpTicker != NULL) m_bmpTicker = CreateBitmap(); }
StartTicker
和 StopTicker
方法启动和停止文本移动。 在内部,它只是启动和停止计时器。
void StartTicker() { // Start timer. m_tmTicker->Start(); } void StopTicker() { // Stop timer. m_tmTicker->Stop(); }
我们用于控件表面的位图由 Create
位图函数创建。 首先,它创建一个图形对象,该对象由控件窗口用于绘制。
graphMeasure = Graphics::FromHwnd(this->get_Handle());
我们使用
sizeString = graphMeasure->MeasureString(m_strTickerText, font);
来定义 Ticket Text 字符串占用了多少像素。 之后,它创建一个位图,该位图的宽度等于控件客户区矩形的宽度加上 Ticker 文本字符串的宽度。 为了在位图上绘制,我们使用以下命令获取指向与位图关联的图形对象的指针
graphImage = Graphics::FromImage(bmpTicker);
其余代码只是填充位图背景并将 Ticker 文本字符串绘制到位图。
Bitmap* CreateBitmap() { Bitmap* bmpTicker; // Result bitmap. Graphics* graphImage; // Graphics used to draw to bitmap. Graphics* graphMeasure; // Graphics used to measure string. SizeF sizeString; // Size of Ticker text string. System::Drawing::Font* font; // Font used to draw string. SolidBrush* brush; // Brush to fill background. Rectangle rect; // Control client rectangle. int nBmpWidth; // Bitmap width. int nBmpHeight; // Bitmap height. int nRepeat; // How many time we should draw ticker // text string in the image. // Get a graphic object used by control window. graphMeasure = Graphics::FromHwnd(this->get_Handle()); // Create a font and brush to draw. font = new System::Drawing::Font("Courier", 10); brush = new SolidBrush(get_BackColor()); // Get a size that ticker text string occupies on the string. sizeString = graphMeasure->MeasureString(m_strTickerText, font); m_nTickerWidth = (int)sizeString.get_Width(); rect = this->get_ClientRectangle(); // Bitmap width equals size of the control client rectangle // plus width of the Ticker text string. nBmpWidth = rect.get_Width() + m_nTickerWidth; // Define how many time we have to draw the string to bitmap. nRepeat = (int)(nBmpWidth/m_nTickerWidth + 1); nBmpHeight = rect.get_Height(); // Create a bitmap to draw off screen. bmpTicker = new Bitmap(nBmpWidth, nBmpHeight, graphMeasure); // Get a graphic object to draw to bitmap. graphImage = Graphics::FromImage(bmpTicker); // Fill the background. graphImage->FillRectangle(brush, 0, 0, nBmpWidth, nBmpHeight); // Draw string, if it is short, draw it several times. for(int nCounter = 0; nCounter < nRepeat; nCounter++) graphImage->DrawString(m_strTickerText, font, Brushes::Black, nCounter * m_nTickerWidth, 0); return bmpTicker; }
OnPaint
是执行实际绘制作业的函数。 我们覆盖此虚拟函数来执行我们的绘制。 我们在这里所做的只是获取指向要绘制的图形对象的指针,并使用当前偏移量使用 DrawImageUnscaled 函数绘制我们的位图。
virtual void OnPaint(PaintEventArgs* pe) { Graphics* graph; // Get a Graphics object that is used to draw. graph = pe->get_Graphics(); // If face image bitmap is not exist, create it. if(m_bmpTicker == NULL) m_bmpTicker = CreateBitmap(); // Draw the face image bitmap. graph->DrawImageUnscaled(m_bmpTicker, Point((m_nOffset - m_nTickerWidth), 0)); }
绘制或位图覆盖所有控件客户区,因此为了避免闪烁,我们应该覆盖 OnPaintBackground 虚拟函数,并在其中不执行任何操作。
virtual void OnPaintBackground(PaintEventArgs* pe) { }
我们应该做的最后一件事是实现 Timer 事件处理程序。 在这里,我们只是调整位图偏移并重绘控件。
void OnTimer( Object* myObject, EventArgs* myEventArgs ) { // Adjust bitmap offset. m_nOffset -= m_nScrollSmoothness; // If we finish to scroll bitmap, recreate it. if(m_nOffset < 0) m_nOffset = m_nTickerWidth - m_nScrollSmoothness; // Redraw control. Invalidate(); Update(); }
要创建一个客户端,我们需要创建一个托管 C++ 应用程序,并添加一段代码,该代码在主源文件中实现一个 Windows 窗体,这与我们为控件所做的非常相似。 以下代码对此进行了说明。
#include "stdafx.h" // Add it to get an access to .NET Framework classes. #using <mscorlib.dll> #using <System.dll> #using <System.Drawing.DLL> #using <System.Windows.Forms.dll> // Import Ticker control information. #using "..\Bin\TickerControl.dll" using namespace System; using namespace System::ComponentModel; using namespace System::Drawing; using namespace System::Windows::Forms; // Class that implements simple windows form. __gc class CTickerClientForm : public Form { // Constructor. public: CTickerClientForm() { // Create ticker control. ctrlTicker = new CTickerControl(); InitForm(); } // Initializes a form. protected: void InitForm() { // Set window title and size. this->set_Text(S"Ticker"); this->set_Size(System::Drawing::Size(400, 300)); // Set control location and size on the form. ctrlTicker->set_Location(System::Drawing::Point(30, 100)); ctrlTicker->set_Size(System::Drawing::Size(300, 100)); // Set ticker properties and start ticker to move. ctrlTicker->set_TimerInterval(10); ctrlTicker->set_TickerText(S"This is very very ... long string."); ctrlTicker->set_ScrollSmoothness(1); ctrlTicker->StartTicker(); // Add ticker to form controls collection. this->get_Controls()->Add(this->ctrlTicker); } // Members. private: CTickerControl* ctrlTicker; // Ticker control. }; int main(void) { try { // Create Form. Application::Run(new CTickerClientForm()); } catch(Exception* e) { Console::Write("Error occured: "); Console::WriteLine(e->get_Message()); } return 0; }
历史
2001 年 10 月 19 日 - 更新了 Beta 2 的源文件