Managed C++ and .NET Development: Visual Studio .NET 2003 Edition
There's a second way of processing Paint events: the protected virtual OnPaint() method. Unlike what you've seen before, you don't call the OnPaint() method. Instead, you need to override it and then let the system handle it when it's called. Listing 11-2 shows the "Hello, World!" program again, this time using the virtual OnPaint () method.
Listing 11-2: "Hello, World!" Using OnPaint()
namespace HelloGDI_OnPaint { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; public __gc class Form1 : public System::Windows::Forms::Form { public: Form1(void) //... protected: void Dispose(Boolean disposing) //... private: System::ComponentModel::Container * components; void InitializeComponent(void) { this->ClientSize = System::Drawing::Size(300, 300); this->Name = S"Form1"; this->Text = S"Hello GDI+"; } protected: virtual void OnPaint(System::Windows::Forms::PaintEventArgs *e) { Form::OnPaint(e); Graphics *g = e->Graphics; g->DrawString(S"Hello World!", new Drawing::Font(new FontFamily(S"Arial"), 16), Brushes::Black, 75.0, 110.0); } }; }
The results of HelloGDI_OnPaint.exe when run are identical to the PaintEventHandler version. Most of the code is the same as well. The first difference is that there's no handling of the Paint event within the InitializeComponent() method. It isn't needed because the OnPaint() method will handle the Paint events for you. That isn't to say that you can't have the handler. I see a possibility where a static set of graphic rendering activities are placed within the OnPaint() method and then a set of other graphic rendering activities are placed in multiple Paint event handlers and, based on conditions, dynamically delegated to the appropriate handler. However, you could do the same thing using an OnPaint() or a Paint event handler alone.
So what's the difference (if any) between the OnPaint() method and the handler PaintEventHandler? Isn't the OnPaint() method just a prepackaged PaintEventHandler? I thought so, like many other people (I assume), but I was wrong. The fact is that the Control class's OnPaint() method is actually in charge of the executing of all the delegated Paint event handlers. This means the only way you can be assured that a Paint event happens is by overriding the OnPaint() method, because it's possible to disable the Paint event handlers from actually firing within the OnPaint() method. It's a very simple thing to do—you just have to not call the base class Form::OnPaint() within the OnPaint() method.
As you can see, the first statement within the OnPaint() method is to call the base class version of itself:
virtual void OnPaint(System::Windows::Forms::PaintEventArgs *e) { Form::OnPaint(e); //...Do stuff }
Placing the OnPaint() method first was a conscious decision on my part, as where the base method call is placed within the implementation of the method can make a difference. Placing it first, as shown in the preceding code, indicates that you must handle all the other delegated Paint events first or, in other words, do the rendering specified within this OnPaint() method last. Now if you place the base method call after doing the rendering of the method:
virtual void OnPaint(System::Windows::Forms::PaintEventArgs *e) { //...Do stuff Form::OnPaint(e); }
this indicates render first what is in this method, and then handle all other delegated Paint events. Both might be legitimate depending on what you want to do. Try the code in Listing 11-3 first by placing Form:: OnPaint() as the first line in the overloaded method and then as the last.
Listing 11-3: Placing the OnPaint Base Class Method
namespace OnPaintWhere { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; public __gc class Form1 : public System::Windows::Forms::Form { public: Form1(void) //... protected: void Dispose(Boolean disposing) //... private: System::ComponentModel::Container * components; void InitializeComponent(void) { this->AutoScaleBaseSize = System::Drawing::Size(6, 15); this->ClientSize = System::Drawing::Size(292, 265); this->Name = S"Form1"; this->Text = S"Hello GDI+"; this->Paint += new System::Windows::Forms::PaintEventHandler(this,Form1_Paint); } protected: virtual void OnPaint(System::Windows::Forms::PaintEventArgs *e) { // Form::OnPaint(e); Graphics *g = e->Graphics; g->DrawString(S"Hello GDI+", new Drawing::Font(new FontFamily(S"Arial"), 16), Brushes::Black, 75.0, 110.0); Form::OnPaint(e); } private: System::Void Form1_Paint(System::Object * sender, System::Windows::Forms::PaintEventArgs * e) { Graphics *g = e->Graphics; g->DrawString(S"Hello GDI+", new Drawing::Font(new FontFamily(S"Arial"), 16), Brushes::Red, 75.0, 110.0); } }; }
Figure 11-2 shows OnPaintWhere.exe in action where the text "Hello GDI+" is in red in this black-and-white image. Guess you'll have to take my word on it.
When Form::OnPaint() is placed on the first line, the text turns out black, as the OnPaint() method's version of the DrawString() method is handled last. When Form::OnPaint() is placed at the end, on the other hand, the text is red because the PaintEventHandler version of DrawString() method is handled last. By the way, if you remove all the logic within the OnPaint() method, no text is displayed, because the PaintEventHandler is never triggered as Form::OnPaint() was not called to execute the delegated Paint events.
Now after saying all this, does it really matter if your OnPaint() method calls its base class version? The usual answer to this is "Not really." If you don't plan on using the Paint event handler yourself and the form that you created is never inherited (both normally being the case), then calling OnPaint() makes no difference. In fact, it might speed things up minutely if you don't call it because it isn't doing any unneeded method calls. (This is my take on it, though. The .NET Framework documentation says you should always call the base class method, so maybe you should take Microsoft's word, as there might be some hidden reason that I'm unaware of. That said, so far I haven't come across any problems.)
Which should you use: the OnPaint() method or the Paint event handler? I think the OnPaint() method, as it doesn't have the event delegate implementation overhead. But because it's easier to use than the Paint event (you only have to double-click the event handler in the Properties dialog box to add it) and the cost of the overhead is so minute, I use the Paint handler from here on in.
Категории