Solved

Seeking for tutorials, advices or something else...

Posted on 2001-08-24
27
348 Views
Last Modified: 2010-04-06
Hi all!

I want improve my skills in writing visual (graphical) components. Until now I wrote a few very simple mainly TGraphicControl descendants and one little advanced technique I've used was double-buffering method to eliminate flicker during repaints.

Could you point me to some tutorials about advanced techniques for writing visual controls (either non-windowed or windowed (TCustomControl descendants)) or offer your own hand-made advanced components for exploring the source & teaching? Yes, I graded this question with only one hundred points but I can say that I'll reward individually perhaps everyone who will propose useful link, tutorial, advice or a component.

Thanks for your participation dear experts.

Ivo
0
Comment
Question by:ivobauer
  • 16
  • 6
  • 4
  • +1
27 Comments
 
LVL 5

Expert Comment

by:scrapdog
Comment Utility
Is there any specific area in component writing you could use help with?
0
 
LVL 9

Expert Comment

by:ITugay
Comment Utility
Hi ivobauer,

the best "samples" shipped with Delphi, just take a look at %delphi\source\vcl directory. Exploring source code of known components you can get a lot of knowelege from there. This is not a joke, really :-)

-----
Igor.
0
 
LVL 8

Expert Comment

by:TOndrej
Comment Utility
In addition to Igor's excellent suggestion (hi, Igor! ;-) to use the VCL source I would also highly recommend Ray Konopka's work:

article http://community.borland.com/article/0,1410,20569,00.html
book "Developing Custom Delphi Components" http://www.raize.com/DCDC/Edition2.htm

In short, anything from that guy is perls & diamonds, well worth reading...
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Hi all!

The Delphi VCL source is the first place I look every time I need to learn something new but I had on my mind if you can me propose your own advices, hints, samples with some minor explanation & description of them. I mean when you wrote some visual control and some special technique was used in making of this control so you can share it with me... Right?

Best regards, Ivo.
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
> Is there any specific area in component writing you could use help with?

Yes. I'd like to focus on the following techniques:

1. When the control must be repainted, how to redraw only the parts of the control that need to be updated instead of redrawing entire control.

2. In case only part of the big control must be updated periodically in short time intervals (up to 10 times per second) is it possible to draw into control bypassing Paint method?

3. When I need to build a control with a lot of sub-controls, for example a panel with a lot of leds. Is better approach to design it as windowed parent with child TGraphicControl descendants (a kind of composite control) or make it as single control and drawing of the leds handle in control's Paint method?

Thanks in advance, Ivo.
0
 
LVL 9

Expert Comment

by:ITugay
Comment Utility
Hi Ivo,

// Ondrej :-), didn't saw your comments for a long time.

> 1. When the control must be repainted, how to redraw only the parts
> of the control that need to be updated instead of redrawing entire control.

You can use clipping technique:

--- TCanvas.ClipRect ----
Use ClipRect to determine where the canvas needs painting. ClipRect limits the drawing region of the canvas so that any drawing that occurs at coordinates outside the ClipRect is clipped and doesn?t appear in the image.
---

To avoid of flikering, you can make some drawing in memory bitmap and then copy result to the canvas.

> 2. In case only part of the big control must be updated
> periodically in short time intervals (up to 10 times per second)
> is it possible to draw into control bypassing Paint method?

Yes, it's possible. You can draw into control about any time you want (inside main VCL thread).
Just use control's canvas to draw a part of image you want.

> 3....make it as single control and drawing of the leds handle in control's Paint method?

If you do not need to use leds as standalone controls, then preferable way is to have single control. Especcialy if control is full of drawing and time critical. Also controls inside other control is not good components developing technique. Did you see that Borland use it? :-)

------
Igor
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Hi Igor!

> Use ClipRect to determine where the canvas needs painting. ClipRect limits the drawing region of the
canvas so that any drawing that occurs at coordinates outside the ClipRect is clipped and doesn?t appear
in the image.

Is this technique applicable to both windowed and also non-windowed controls? Can you show me some sample code for both control kinds?

> Also controls inside other control is not
good components developing technique. Did you see that Borland use it?

And how to accomplish the task when you have for example five leds in a control and want to display a led-specific hint when leaving mouse under a led?

If the 100 pts. is not enough, just let me know & and I'll raise it.

Ivo
0
 
LVL 9

Accepted Solution

by:
ITugay earned 200 total points
Comment Utility
Hi Ivo,

you can use the same technique for windowed and non windowed controls. You just need to have access to it's Canvas property. Here is the way to define clipping region for control's canvas.

var
  H: HRgn;
begin
  // create rectangle region X1,Y1,X2,Y2
  // you can create not only rectangle, but more complex region
  // see API functions about regions
  H := CreateRectRgn(50, 50, 100, 100);

  // assign region to canvas
  SelectClipRgn(Canvas.Handle, H);

  // you shoud deallocate region's memory if you don't going to use it in further
  DeleteObject(H);

  // just fill entire area of control
  // but drawing will be performed only for clipped region
  Canvas.Brush.Color := clRed;
  Canvas.FillRect(ClientRect);

  // reset clipping region
  SelectClipRgn(Canvas.Handle, 0);
end;


Now about some items inside control and how I would do such control.
1. create nonvisual object (collection item) that able to keep led's necessary properties, e.g. color, size, hint text etc.
2. main control has a collection to keep all items.
3. provide some function that returns rectangle for the item with given index in collection. It will be necessary for item painting.
4. provide some method that allow recognize item by it's location. (simple iteration with function above). For example it must returns item's index or -1 if no items at given location.

Now you just need to handle MouseMove event and assign main control's hint to hint of item inside.


> f the 100 pts. is not enough, just let me know & and I'll raise it.
I ask never about more points for me :-)

-----
Igor.
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Hi Igor!

I have carefully read your last comment and decided to write a sample control which uses clipping technique to increase drawing performance. This feature can be controlled by the ClipOutput property. I'd like to ask you if you can try it. Simply put this control onto the form, ensure that ClipOutput is True and run the project. Now drag some another window over the form and you can see that the control is redrawn fine as expected. As you can see it is direct descendant of TCustomControl. Now change the control ancestor in the source to be direct descendant of TGraphicControl and repeat the same steps as described above. Now, when dragging some window over our form in random directions, you may encounter that control content is either not updated at all or incorrectly... There must be something wrong with the ClipRect when entering overriden Paint method. Can you help me to resolve this problem?

Best regards, Ivo.


Control source code dump follows...

=====
unit SampleControl;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type

  TSampleControl = class(TCustomControl)
  private
    FClipOutput: Boolean;
    procedure SetClipOutput(Value: Boolean);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property ClipOutput: Boolean read FClipOutput write SetClipOutput default True;
  end;

procedure Register;

implementation

{ TSampleControl }

constructor TSampleControl.Create(AOwner: TComponent);
begin
  inherited;
  Width := 200;
  Height := 200;
  FClipOutput := True;
end;

procedure TSampleControl.Paint;
var
  R: TRect;
  Rgn: HRGN;
begin
  R := ClientRect;
  with Canvas do
  begin
    if FClipOutput then
    begin
      Rgn := CreateRectRgnIndirect(ClipRect);
      SelectClipRgn(Handle, Rgn);
      DeleteObject(Rgn);
    end;
    Brush.Style := bsSolid;
    Brush.Color := clWhite;
    FillRect(R);
    Brush.Color := clBlack;
    while not IsRectEmpty(R) do
    begin
      FrameRect(R);
      InflateRect(R, -2, -2);
    end;
    if FClipOutput then SelectClipRgn(Handle, 0);
  end;
end;

procedure TSampleControl.SetClipOutput(Value: Boolean);
begin
  if FClipOutput <> Value then
  begin
    FClipOutput := Value;
    if not (csReading in ComponentState) then Repaint;
  end;
end;

procedure Register;
begin
  RegisterComponents('Samples', [TSampleControl]);
end;

end.

=====
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Igor (and also the others): Please look at my last comment.

Thanks in advance, Ivo.
0
 
LVL 9

Expert Comment

by:ITugay
Comment Utility
hi ivo,
sorry for keeping silence last few days. I lost a  lot of notifications from EE in meantime. I inform community support and now all seems fine. I'll send you comment asap.

Regards,
Igor.
0
 
LVL 9

Expert Comment

by:ITugay
Comment Utility
Hi ivo,

OK a saw what you mean. The problem is that TGraphicControl is not windowed control (has no handle). SetClipRegion function doesn't work properly in this case, clipping region assigned relative parent windowed control (try to place control in left top corner of the form). I do not understand exactly this logic.

Btw, you do not need to change ClipRect while painting whole control, windows does it for you automatically. Clipping technique is usefull when you have to paint part of control after make some changes in control. And this is usefull only if you have to draw few overlapped elements in the same place or drawing mechanizm is very complicated. In case of few leds inside your control you just need to write procedure that draws led in desired location. This procedure should be called inside Paint method for each led, but after changing one led you just need to call led drawing procedure for changed led.

In additional, to avoid of flickering you can use DoubleBuffered = true. But it works properly for windowed controls only. TGraphicControl descendants should set DoubleBuffered for it's parent control instead of itself.

----
Igor.

PS: do not hesitate to ask me if something not clear.
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Hi Igor!

Thank you for the detailed explanation. But I thought that clipping can be useful when one needs to increase rendering performance of the control. Imagine that a form containing my control was partially obscured by another window. Now, when I bring my form to the front, a Paint method of my control gets called and I don't want to render entire control but only the part that was temporarily obscured by another window. The question is how to determine proper dimensions of the ClipRect in such case when my control descends from TGraphicControl instead  of TCustomControl? In case of windowed control, the ClipRect contains valid dimensions.

Best regards, Ivo.
0
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 
LVL 9

Expert Comment

by:ITugay
Comment Utility
Hi Ivo,


>>Now, when I bring my form to the front, a Paint method of my
>>control gets   called and I don't want to render entire control but
>>only the part that was temporarily obscured by another window.

Windows does it for you automatically in both cases, I mean for windowed and non-windowed controls.
Just try to show current ClipRect on the caption of form, and hover another window over your control. You will see that clipping region correctly assigned.

>>how to determine proper dimensions of the ClipRect in such
>>case when my control descends from TGraphicControl

as I noticed, ClipRect coordinates should be set relative parent control.

Cheers,
Igor.
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Hi Igor!

I have put the following lines at the beginning of the Paint method to adjust dimensions of the clipping rectangle in case my control was descendant of TGraphicControl, so it looks like:

var
  CR: TRect;

begin
  CR := ClipRect;
  OffsetRect(CR, Left, Top); // <-
  Rgn := CreateRectRgnIndirect(CR);
  SetClipRgn(Rgn);
  ...
end;

Now the clipping region seems to be pre-set fine. Btw, I discovered that setting of the clipping region is done automatically when a control is a windowed one.

However, although the clipping region is pre-set fine, it is set EVERYTIME according to dimensions of the entire control (0, 0, Width, Height)!!! Thus effort taken by extra clipping code has no sense. Do you know where to obtain TRUE dimensions of the clipping rectangle of the non-windowed control? This is probably the last question...

Best regards, Ivo.
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Hi Igor!

I have put the following lines at the beginning of the Paint method to adjust dimensions of the clipping rectangle in case my control was descendant of TGraphicControl, so it looks like:

var
  CR: TRect;

begin
  CR := ClipRect;
  OffsetRect(CR, Left, Top); // <-
  Rgn := CreateRectRgnIndirect(CR);
  SetClipRgn(Rgn);
  ...
end;

Now the clipping region seems to be pre-set fine. Btw, I discovered that setting of the clipping region is done automatically when a control is a windowed one.

However, although the clipping region is pre-set fine, it is set EVERYTIME according to dimensions of the entire control (0, 0, Width, Height)!!! Thus effort taken by extra clipping code has no sense. Do you know where to obtain TRUE dimensions of the clipping rectangle of the non-windowed control? This is probably the last question...

Best regards, Ivo.
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Sorry for double-posting...
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Hi Igor.

I'm just notifying you since E-E seemed to be down last days...

Ivo
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Notification still seems not to work as expected...
0
 
LVL 8

Expert Comment

by:TOndrej
Comment Utility
> Do you know where to obtain TRUE dimensions of the clipping rectangle of the non-windowed control?

Hi Ivo,

a non-windowed control has no window, and therefore no clipping region. Delphi's TControl simply recalculates its client rectangle into the Parent's coordinates and then invalidates relevant portions of the Parent window. Have a look at InvalidateControl...

In general, TWinControl descendants can simply use InvalidateRect, TControl will have to perform a little bit of calculation, also taking into account other (sibling) controls on the same Parent.
In some cases this calculation can take too long then it may be faster to simply Invalidate the whole control.

HTH
TOndrej
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Huh. I'm now a bit confused. Could you tell me whether the clipping technique has any sense when working with non-windowed controls or not? For what can be clipping useful at all? Is the complex drawing with clipping faster than without clipping?

Thanks in advance, Ivo.
0
 
LVL 8

Expert Comment

by:TOndrej
Comment Utility
> Huh. I'm now a bit confused. Could you tell me whether the clipping technique has any sense when working
with non-windowed controls or not?

Ivo,
in a Windows GUI application you are always using windowed controls, right?
When you're writing a non-windowed control (TControl or TGraphicControl descendant) then you still need a Parent which must be windowed. Clipping can only be done on the Parent because only it has window.
But because you have to recalculate your control's coordinates into the Parent's, and take into account other children that may coexist (and possibly overlap one another) on the same Parent, it just makes the task a bit more complicated, that's all I wanted to say.

Just keep in mind that clipping is useful only as long as the optimized painting is actually faster than unoptimized painting; if your calculations involve complex regions you may find that your clipping optimization actually slows down the painting of your control; because the calculations of what has to be invalidated simply take longer than the painting itself. I hope I said it more clearly this time.

I'm by no means an expert on painting/clipping; I have only touched this area of Windows programming briefly.
In my non-windowed controls, most of the time I determine the closest bounding rectangle of what needs to be redrawn and use a simple InvalidateRect; when I'm lazy I just call Invalidate; I've never tried to optimize more than that.

> Is the complex drawing with clipping faster than without clipping?

Depends... as I've tried to explain above. I don't have enough experience to be more exact, sorry.

Also sorry if I confused you. I already have the feeling that I'd better keep my mouth shut in this thread, and listen. ;-)
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
TOndrej: My confusion has just gone away... One more question: When you have a windowed control and you know that some its property will be changed very often and a portion of the control must be repainted to see the change effects. Do you prefer to draw directly into control canvas to reflect the property change or set some internal flag indicating that specific portion of the control must be repainted, then invalidate that portion and subsequently in overriden Paint method checks and updates only the portion that needs to be updated?

This was my last question.

Best regards, Ivo.
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Just attempting to send a notification...
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Hello, TOndrej & ITugay are you there? Please dedicate me a little of your time.

Thanks, Ivo.
0
 
LVL 8

Expert Comment

by:TOndrej
Comment Utility
> When you have a windowed control and you know that some
> its property will be changed very often and a portion of
> the control must be repainted to see the change effects.
> Do you prefer to draw directly into control canvas to
> reflect the property change or set some internal flag
> indicating that specific portion of the control must be
> repainted, then invalidate that portion and subsequently
> in overriden Paint method checks and updates only the
> portion that needs to be updated?

I've used both, and I don't have any guideline for you, sorry. Use what works best for you/is easiest to do for the specific task.
Sorry not to be more specific but I don't think there's any generic rule of thumb here.
For example, in my current project, in a splashscreen I used direct drawing of a rotating logo, while some time consuming initialization is performed; for a custom label I'm working on which is underlining its text according to the current playing position of its associated wave file I currently use direct drawing, too - in xor mode - but I'm just thinking about using InvalidateRect and normal paint processing instead, to see if it works better.

The problem with direct drawing in this case is that you cannot be assured that what you've drawn directly to canvas is still there, Windows may have invalidated and your normal Paint overwritten the part. This creates a problem for me in xor mode when I move another window on top of my label - the second xor draw is supposed to erase the first one. But if the first one is not there anymore (because of invalidate/paint) then the second xor draws again... I hope you understand what I mean.

I'm not good enough in these things to give you a better answer, this question would better be asked in one of the Borland's newsgroups where the real gurus are.

Good luck
TOndrej
0
 
LVL 2

Author Comment

by:ivobauer
Comment Utility
Well, thanks a lot for your time spent here with my question. I decided to grade Igor's comment(s) but I will post a question for TOndrej since he also helped me a lot with this q.

Best regards, Ivo.
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Suggested Solutions

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now