Solved

Drawing on a scrollbox and redrawing when necessary

Posted on 2010-11-11
14
640 Views
Last Modified: 2012-05-10
Two questions:


1.) Im currently drawing TPanels on a TScrollbox (aligned alClient on my main form)  based off from records in a table.  The Tpanels will not work for me.

I need a control i can write some info on. a.) Id  b.) name, and c.) status.  It must be a control that has a tag (where i can store the "id") and it must have a onClick event.  ATPanel has all these except that it will only allow me to write in the caption. I need to write anywhere on its canvas. Need to be able to change font size , type, and color as well

2.) My controls (currently Tpanels) are drawn left to right and when it reaches the client width, it drops down and starts the drawing all over again.

I currenly have the Vertical scrollbar visible , but not the Horizontal.

How do i redraw everything once the form is resized. Example, when a user clicks the maximize on the form?

Placing the redraw in the OnResize event, works, but with some annoying flashing, like its being redrawn a number of times

procedure TfrmMain.FormResize(Sender: TObject);
begin
  UpdateLocations;
end;
0
Comment
Question by:Looking_4_Answers
  • 6
  • 5
  • 2
  • +1
14 Comments
 
LVL 24

Expert Comment

by:jimyX
ID: 34111449
Not sure if I fully understand your question:

I read your previous question before you delete and prepared this as a reply (just in case it might help)
What you can do is create one of the components that have tag, onClick event and change font [size , type, color] at the run time on your panel such as:
TLabels which can be created at the run time and placed any where on your Panel or TSpeed button.

Regarding the current question you have 4 options:
Option 1: disable maximize option.
Option 2: you can rearrange your components on the form when maximized by calculating the height and width of your form then calculate that and divide equally the width of your components so they appear on the form no matter what is the size of it.
option 3: limit the maximization option between two states one medium and one large and adjust your components on the current state of the form (you will be having different components position for each state).
Option 4: just let the form be resized without affecting your components.

I suggest you go with option 2 or 3.
0
 

Author Comment

by:Looking_4_Answers
ID: 34111797
"I read your previous question before you delete and prepared this as a reply (just in case it might help)
What you can do is create one of the components that have tag, onClick event and change font [size , type, color] at the run time on your panel such as:
TLabels which can be created at the run time and placed any where on your Panel or TSpeed button."

Not an option, since if you then click on that component placed on your component, the onlcick event of the component you want will not work.

"Option 1: disable maximize option." - sorry, not an option

"Option 2:" I already do this with the code i show above

UpdateLocations;

which is called when the program first starts, after each new record is added in the database, etc.

I even call it on resize....but i dont think that is the place to call it. It gets flashy (or called more than once)

"Option 3"  Sorry, not an option

"Option 4" - Sorry, not an option
0
 
LVL 32

Expert Comment

by:ewangoya
ID: 34112031

You can still use the TPanel and instead of changing the caption, use Canvas.TextOut to write to the canvas of the TPanel.

The other thing is that rerunning UpdatePanels, erases all the tabs. You only need to clear the panels in the active Tab and redraw them. Redraw panels in the other tabs only when the user changes to those tabs using the OnPageChange event. have a variable that indicates whether a tab needs refresh or not.

0
 
LVL 25

Expert Comment

by:epasquier
ID: 34113605
each time I had such needs as yours, I went for frames to represent those 'objects' of yours.

a frame is much like a panel, but it can be designed separately in the IDE, then created dynamically as you are with your TPanels. That is much better to visualize the result you'll have. Also, if you put buttons or other components on that frame, you don't have to set manually all events as you would with creating all objects dynamically on a panel.

On that frame, you can put whatever labels you want. you can easily circumvent the onClick problem by setting the same onClick event on all your frame's label (I generalize to all controls  that do not have an onClick event set already)

aFrame:=TMyFrame.Create(ParenPanel);
aFrame.Parent:=ParenPanel;
aFrame.SetOnClick(onMyFrameClick);
aFrame.Left:=...
procedure TMyFrame.SetOnClick(onClickEvent:TNotifyEvent);

var i:integer;

begin

 Self.onClick:=onClickEvent; // set it for the frame itself

 for i:=0 to ControlCount-1 do 

  if Not Assigned(Controls[i].onClick) // check a design event is not set allready

   Then Controls[i].onClick:=onClickEvent; // set it for all controls of the frame 

end;

Open in new window

0
 
LVL 25

Expert Comment

by:epasquier
ID: 34113623
with a frame you don't have to manage redraw manually, it will like any other set of components would.
All you have to do is to calculate new positions of all those frames inside their client area on each resize of this area
0
 

Author Comment

by:Looking_4_Answers
ID: 34113933
"The other thing is that rerunning UpdatePanels, erases all the tabs. You only need to clear the panels in the active Tab and redraw them. Redraw panels in the other tabs only when the user changes to those tabs using the OnPageChange event. have a variable that indicates whether a tab needs refresh or not."

Oh shoot, this may be the problem  <smile>  i will investigate

0
 

Author Comment

by:Looking_4_Answers
ID: 34114308
find attached , the code i use to do my drawing

when the program starts, i update the locations - (there is a tabsheet for each location record)

procedure TfrmMain.FormActivate(Sender: TObject);
begin
 //
 UpdateLocations;
end;

The last thing I do in the UpdateLocations call is get the activepage and update the units on that page

 if pgeMain.PageCount > 0 then
 begin
  pgeMain.ActivePageIndex:= 0;
  UpdateUnits(pgeMain.Pages[0]);
 end;

anytime the page is changed, i update the units on the active page

procedure TfrmMain.pgeMainChange(Sender: TObject);
begin
 UpdateUnits(pgeMain.ActivePage);
end;

if the form is resized, i also update the units on the ative page

procedure TfrmMain.FormResize(Sender: TObject);
begin
 UpdateUnits(pgeMain.ActivePage);
end;


There are a number of issues here.

1.) I still get the flashing while drawing

2.) there is an issue with cleanup (freeing some something), cause i get access violations when i close the project
function TfrmMain.getScrollBox(APage: TTabSheet): TScrollBox;

var

 I: Integer;

begin

 result:= nil;

 for I:= 0 to APage.ControlCount-1 do

  if APage.Controls[I] is TScrollBox then

  begin

   result:= TScrollBox(APage.Controls[I]);

   Exit;

  end;

end;



procedure TfrmMain.RemoveLocations;

var

 I: Integer;

begin

 for I:= pgeMain.PageCount-1 downto 0 do

  pgeMain.Pages[I].Destroy;

end;



//procedure TfrmMain.UpdateUnits(ATabSheet: TTabSheet);

procedure TfrmMain.UpdateUnits(APage: TTabSheet);

var

 AUnit: TPanel;

 X, Y, W, H, S: Integer;

 P: Integer;

 AScrollBox: TScrollBox;

begin

 AScrollbox:= getScrollBox(APage);

 X:= 10; Y:= 10; W:= 75; H:= 75; S:= 5;

 frmDataMod.qryUnits.Active:= False;

 try

  frmDataMod.qryUnits.Parameters.ParamByName('locname').Value:= APage.Caption;

  frmDataMod.qryUnits.Active:= True;

  if frmDataMod.qryUnits.RecordCount > 0 then

  begin

   while not frmDataMod.qryUnits.EOF do

   begin

    AUnit:= TPanel.Create(AScrollBox);

    AUnit.Parent:= AScrollBox;

    AUnit.BringToFront;

    AUnit.ParentColor:= False;

    Aunit.ParentBackground:= False;

    AUnit.BevelOuter:= bvRaised;

    AUnit.BevelInner:= bvLowered;

    AUnit.Color:= clGreen;

    AUnit.Font.Color:= clYellow;

    AUnit.Font.Style:= [fsBold];

    AUnit.Left:= X;

    AUnit.Top:= Y;

    AUnit.Width:= W;

    AUnit.Height:= H;

    AUnit.Caption:= frmDataMod.qryUnits.fieldByName('unit_number').AsString + ' - ' + frmDataMod.qryUnits.fieldByName('unit_type').AsString;

    frmDataMod.qryUnits.Next;

     P:= X + W + S;

    if (P + W ) < AScrollBox.ClientWidth then

     X:= P //X = X + Width + space

    else

    begin

     X:= 10;

     Y:= Y + H + S; //Y = Y + Hieght + Space

    end;

   end;

  end;//if

 except

 begin

  ShowMessage('Error retrieving units to display on location pages!');

 end;

 end;//try

end;



procedure TfrmMain.UpdateLocations;

var

 Locations: TStringList;

 I: Integer;

 APage: TTabSheet;

 AScrollBox: TScrollBox;

begin

 RemoveLocations;

 Locations:= TStringList.Create;

 frmDataMod.GetLocations(Locations, False);

 for I:= 0 to Locations.Count-1 do

 begin

  APage:=  TTabSheet.Create(pgeMain);

  APage.PageControl:= pgeMain;

  APage.Parent:= pgeMain;

  APage.Caption:= Locations[I];

  AScrollBox:= TScrollBox.Create(APage);

  AScrollBox.Parent:= APage;

  AScrollBox.Align:= alClient;

  AScrollBox.VertScrollBar.Visible:= True;

 end;

 if pgeMain.PageCount > 0 then

 begin

  pgeMain.ActivePageIndex:= 0;

  UpdateUnits(pgeMain.Pages[0]);

 end;

 Locations.Free;

end;

Open in new window

0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:Looking_4_Answers
ID: 34114373
sorry, the UpdateUnits  method should have a test to see if the TabSheet is assign

 if assigned(APage) then
 begin
  //
 end;

0
 
LVL 25

Expert Comment

by:epasquier
ID: 34114450
> I still get the flashing while drawing
That is because you erase everything and recreate every component of your page each time.

You don't need to do that, your Update function should only create new ones since last update and delete those that are no longer there. You can do that by setting the Tag property of the panel (or frame) to a unique ID of the object it represent.

I could make you a sample application creating pages of frames inside a scrollbox that would redraw smartly, but monday only.
0
 
LVL 25

Expert Comment

by:epasquier
ID: 34114468
> there is an issue with cleanup (freeing some something), cause i get access violations when i close the project
a simpler/smarter update method would probably fix that as well.
0
 
LVL 25

Expert Comment

by:epasquier
ID: 34114494
that really is too bad I don't have time to do it now and will be out the next 3 days, such a sample application would need only one hour... You can count on me monday if you don't solve it by then
0
 

Author Comment

by:Looking_4_Answers
ID: 34114504
also, for question #1,  the TPanel does not have a exposed Canvas property and therefore i can't draw on the tpanel. I could hack the Tpanel and create a version that exposes the canvas, but then i would have to overide the Paint method.....this will not work for me.
0
 
LVL 25

Accepted Solution

by:
epasquier earned 500 total points
ID: 34114674
really use TFrame.
You can put all kinds of controls on it, or draw in a TImage if you need special drawing.  And with the SetOnClick trick...
0
 
LVL 32

Expert Comment

by:ewangoya
ID: 34117323
Use double buffered and set a variable to know when to refresh, try this code. no flickering and you have full control
unit main;



interface



uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs, StdCtrls, ComCtrls, ExtCtrls;



type

  TUnitPanel = class(TPanel)

  private

    FDescriptionLabel: TLabel;

    FID: Integer;

    FStatus: string;

    FUnitNumberLabel: TLabel;

    FUnitNumber: string;

    FUnitTypeLabel: TLabel;

    FUnitType: string;

    procedure SetUnitNumber(const Value: string);

    function GetDescription: string;

    procedure SetDescription(const Value: string);

    procedure SetUnitType(const Value: string);

  public

    constructor Create(AOwner: TComponent); override;

    property Description: string read GetDescription write SetDescription;

    property ID: Integer read FID write FID;

    property Status: string read FStatus write FStatus;

    property UnitNumber: string read FUnitNumber write SetUnitNumber;

    property UnitType: string read FUnitType write SetUnitType;

  end;



  TfrmMain = class(TForm)

    pgeMain: TPageControl;

    Button1: TButton;

    procedure FormCreate(Sender: TObject);

    procedure FormDestroy(Sender: TObject);

    procedure FormResize(Sender: TObject);

    procedure pgeMainChange(Sender: TObject);

  private

    FNeedResize: array of Boolean;

    FRedrawList: TList;

    FScrollWidth: Integer;

    function GetScrollBox(APage: TTabSheet): TScrollBox;

    procedure RemoveLocations;

    function ResizePending(AIndex: Integer): Boolean;

    procedure SetNeedsResize(AExcludeIndex: Integer);

    procedure UpdateLocations;

    procedure UpdateUnits(APage: TTabSheet);

  end;



var

  frmMain: TfrmMain;



implementation



{$R *.dfm}



{ TUnitPanel }



constructor TUnitPanel.Create(AOwner: TComponent);

begin

  inherited Create(AOwner);

  CAption := '';

  Width := 75;

  Height := 75;

  Color := clGreen;

  BevelOuter:= bvRaised;

  BevelInner:= bvLowered;

  Font.Color:= clYellow;

  Font.Style:= [fsBold];



  FUnitNumberLabel := TLabel.Create(Self);

  FUnitNumberLabel.Parent := Self;

  FUnitNumberLabel.Top := 5;

  FUnitNumberLabel.Left := 2;



  FUnitTypeLabel := TLabel.Create(Self);

  FUnitTypeLabel.Parent := Self;

  FUnitTypeLabel.Top := 20;

  FUnitTypeLabel.Left := 2;



  FDescriptionLabel := TLabel.Create(Self);

  FDescriptionLabel.Parent := Self;

  FDescriptionLabel.Top := 35;

  FDescriptionLabel.Left := 2;

end;



function TUnitPanel.GetDescription: string;

begin

  Result := FDescriptionLabel.Caption;

end;



procedure TUnitPanel.SetDescription(const Value: string);

begin

  FDescriptionLabel.Caption := Value;

  Invalidate;  //really no need for this

end;



procedure TUnitPanel.SetUnitNumber(const Value: string);

begin

  if FUnitNumber <> Value then

  begin

    FUnitNumber := Value;

    FUnitNumberLabel.Caption := Value;

  end;

end;



procedure TUnitPanel.SetUnitType(const Value: string);

begin

  if FUnitType <> Value then

  begin

    FUnitType := Value;

    FUnitTypeLabel.Caption := Value;

  end;

end;



{TfrmMain}



procedure TfrmMain.FormCreate(Sender: TObject);

begin

  FRedrawList := TList.Create;

  FScrollWidth := GetSystemMetrics(SM_CXVSCROLL);

  DoubleBuffered := True;

end;



procedure TfrmMain.FormDestroy(Sender: TObject);

begin

  FreeAndNil(FRedrawList);

end;



procedure TfrmMain.FormResize(Sender: TObject);

var

  I: Integer;

  ScrollBox: TScrollBox;

  AUnit: TUnitPanel;

  P, X, Y, S: Integer;

begin

  if pgeMain.PageCount = 0 then

    exit;

  FRedrawList.Clear;

  ScrollBox := getScrollBox(pgeMain.ActivePage);

  if Assigned(ScrollBox) then

  begin

    for I := ScrollBox.ControlCount - 1 downto 0 do

      if ScrollBox.Controls[I] is TPanel then

      begin

        ScrollBox.Controls[I].Visible := False;

        FRedrawList.Add(ScrollBox.Controls[I]);

      end;



    X:= 10; Y:= 10; S:= 5;  I := 0;

    for I := FRedrawList.Count - 1 downto 0 do

    begin

      AUnit := TUnitPanel(FRedrawList[I]);

      AUnit.Left:= X;

      AUnit.Top:= Y;

      AUnit.Visible := True;

      P:= X + AUnit.Width + S;

      if (P + AUnit.Width + FScrollWidth ) < ScrollBox.ClientWidth then

       X:= P //X = X + Width + space

      else

      begin

       X:= 10;

       Y:= Y + AUnit.Height + S; //Y = Y + Hieght + Space

      end;

    end;



    SetNeedsResize(pgeMain.ActivePageIndex);

  end;

end;



function TfrmMain.getScrollBox(APage: TTabSheet): TScrollBox;

var

 I: Integer;

begin

 result:= nil;

 for I:= 0 to APage.ControlCount-1 do

  if APage.Controls[I] is TScrollBox then

  begin

   result:= TScrollBox(APage.Controls[I]);

   Exit;

  end;

end;



procedure TfrmMain.pgeMainChange(Sender: TObject);

var

  ScrollBox: TScrollBox;

begin

  ScrollBox := GetScrollBox(pgeMain.ActivePage);

  if Assigned(ScrollBox) then

  begin

    if (ScrollBox.ControlCount <= 0) then

      UpdateUnits(pgeMain.ActivePage)

    else if ResizePending(pgeMain.ActivePageIndex) then

      FormResize(Self);

  end;

end;



procedure TfrmMain.RemoveLocations;

var

 I: Integer;

begin

 for I:= pgeMain.PageCount-1 downto 0 do

    pgeMain.Pages[I].Destroy;

end;



function TfrmMain.ResizePending(AIndex: Integer): Boolean;

begin

  Result := (AIndex >= 0) and (AIndex < Length(FNeedResize)) and FNeedResize[AIndex];

end;



procedure TfrmMain.SetNeedsResize(AExcludeIndex: Integer);

var

  I: Integer;

begin

  for I := 0 to Length(FNeedResize) - 1 do

    FNeedResize[I] := I <> AExcludeIndex;

end;



procedure TfrmMain.UpdateUnits(APage: TTabSheet);

var

 AUnit: TUnitPanel;

 X, Y, S: Integer;

 P: Integer;

 AScrollBox: TScrollBox;

begin

 AScrollbox:= getScrollBox(APage);

 if not Assigned(AScrollbox) then

  Exit;



 X:= 10; Y:= 10; S:= 5;

 frmDataMod.qryUnits.Active:= False;

 try

  frmDataMod.qryUnits.Parameters.ParamByName('locname').Value:= APage.Caption;

  frmDataMod.qryUnits.Active:= True;

  if frmDataMod.qryUnits.RecordCount > 0 then

  begin

   while not frmDataMod.qryUnits.EOF do

   begin

    AUnit:= TUnitPanel.Create(AScrollBox);

    AUnit.Parent:= AScrollBox;

    AUnit.BringToFront;

    AUnit.ParentColor:= False;

    Aunit.ParentBackground:= False;

    AUnit.BevelOuter:= bvRaised;

    AUnit.BevelInner:= bvLowered;

    AUnit.Color:= clGreen;

    AUnit.Font.Color:= clYellow;

    AUnit.Font.Style:= [fsBold];

    AUnit.Left:= X;

    AUnit.Top:= Y;

    AUnit.UnitNumber := frmDataMod.qryUnits.fieldByName('unit_number').AsString;

    AUnit.UnitType := frmDataMod.qryUnits.fieldByName('unit_type').AsString;

    frmDataMod.qryUnits.Next;

     P:= X + AUnit.Width + S;

    if (P + AUnit.Width + FScrollWidth) < AScrollBox.ClientWidth then

     X:= P //X = X + Width + space

    else

    begin

     X:= 10;

     Y:= Y + AUnit.Height + S; //Y = Y + Hieght + Space

    end;

   end;

  end;//if

 except

 begin

  ShowMessage('Error retrieving units to display on location pages!');

 end;

 end;//try

end;



procedure TfrmMain.UpdateLocations;

var

 I: Integer;

 Locations: TStringList;

 APage: TTabSheet;

 AScrollBox: TScrollBox;

begin

 //RemoveLocations; no need for remove locations

 Locations:= TStringList.Create;

 try

   frmDataMod.GetLocations(Locations, False);

   for I:= 0 to Locations.Count-1 do

   begin

    APage:=  TTabSheet.Create(pgeMain);

    APage.PageControl:= pgeMain;

    APage.Parent:= pgeMain;

    APage.Caption:= Locations[I];

    AScrollBox:= TScrollBox.Create(APage);

    AScrollBox.Parent:= APage;

    AScrollBox.Align:= alClient;

    AScrollBox.VertScrollBar.Visible:= True;

   end;

   if pgeMain.PageCount > 0 then

   begin

    pgeMain.ActivePageIndex:= 0;

    UpdateUnits(pgeMain.Pages[0]);

   end;

   SetLength(FNeedResize, Locations.Count);

 finally

   FreeAndNil(Locations);

 end;

end;



end.

Open in new window

0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

760 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

23 Experts available now in Live!

Get 1:1 Help Now