65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (5投票s)

2001 年 5 月 31 日

3分钟阅读

viewsIcon

129145

downloadIcon

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();
}

StartTickerStopTicker 方法启动和停止文本移动。 在内部,它只是启动和停止计时器。

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 的源文件

© . All rights reserved.