?
Solved

Drawing on a scrollbox and redrawing when necessary

Posted on 2010-11-11
14
Medium Priority
?
657 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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:Ephraim Wangoya
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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
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
 

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 2000 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:Ephraim Wangoya
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

On Demand Webinar - Networking for the Cloud Era

This webinar discusses:
-Common barriers companies experience when moving to the cloud
-How SD-WAN changes the way we look at networks
-Best practices customers should employ moving forward with cloud migration
-What happens behind the scenes of SteelConnect’s one-click button

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
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…
Monitoring a network: how to monitor network services and why? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the philosophy behind service monitoring and why a handshake validation is critical in network monitoring. Software utilized …
If you’ve ever visited a web page and noticed a cool font that you really liked the look of, but couldn’t figure out which font it was so that you could use it for your own work, then this video is for you! In this Micro Tutorial, you'll learn yo…
Suggested Courses

770 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