TChart : The check/unchecked event not working in ExtraLegendTool - delphi-xe2

I displayed an ExtraLegendTool with checkboxes but the checkbox event is not working.
Here is the code to display the ExtraLegend:
procedure TFRChart.TCChartAfterDraw(Sender: TObject);
begin
if CKDisplay.Checked then
begin
with ExtraLegend do
begin
Active:= True;
Series := TBarSeries(Self.FindComponent('SeriesTotal'));
with Legend do
begin
LegendStyle := lsAuto;
CheckBoxes := True;
//MaxNumRows := 3;
CustomPosition := True;
Left:= TCChart.Legend.Left;
Top:= TCChart.Legend.ShapeBounds.Bottom + 10;
Width := TCChart.Legend.Width;
ShapeBounds.Right := TCChart.Legend.ShapeBounds.Bottom;
DrawLegend;
end;
end;
end;
end;
Please check the folowing image for more details :
As you can see in the image, I have 2 legends one of type 'Chart1.Legend.LegendStyle := lsSeriesGroups' and the other one is an ExtraLegend.
How can I NOT display all the blue bars for all the series groups when I uncheck the blue series in the Extralegend?

You can use the ExtraLegendTool Clicked() function at the chart OnClick event to get the item of the legend that has been clicked. Then, you can activate/deactivate any series you desire.
This simple example seems to work fine for me here:
procedure TForm1.Chart1Click(Sender: TObject);
var MousePos: TPoint;
index: Integer;
begin
MousePos:=Chart1.GetCursorPos;
index:=ChartTool1.Legend.Clicked(MousePos);
while (index>-1) and (index<Chart1.SeriesCount) do
begin
Chart1[index].Active:=not Chart1[index].Active;
index:=index+3;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
Chart1.SeriesGroups.Add;
Chart1.SeriesGroups.Add;
Chart1.SeriesGroups[0].Name:='This is Group 1';
Chart1.SeriesGroups[1].Name:='This is Group 2';
for i:=0 to 9 do
with Chart1.AddSeries(TBarSeries) as TBarSeries do
begin
FillSampleValues(5);
if (i<3) then
begin
Chart1.SeriesGroups[0].Add(Chart1[i]);
StackGroup:=0;
end
else
begin
Chart1.SeriesGroups[1].Add(Chart1[i]);
StackGroup:=1;
end;
MultiBar:=mbStacked;
end;
Chart1.Legend.LegendStyle := lsSeriesGroups;
Chart1.Draw;
with ChartTool1 do
begin
Active:= True;
//Series := TBarSeries(Self.FindComponent('SeriesTotal'));
Series := Chart1[0];
with Legend do
begin
LegendStyle := lsAuto;
CheckBoxes := True;
MaxNumRows := 3;
CustomPosition := True;
Left:= Chart1.Legend.Left;
Top:= Chart1.Legend.ShapeBounds.Bottom + 10;
Width := Chart1.Legend.Width;
ShapeBounds.Right := Chart1.Legend.ShapeBounds.Bottom;
DrawLegend;
end;
end;
end;

Related

Inno Setup: Custom page to select Update or Remove/Uninstall

I need to create a custom uninstall page that let the user choose if he wants to update the software or uninstall it (if the software is already installed).
I have already done my custom page and is like this:
How can I get the values of those radio buttons when the user click the Next Button?
And, how can I update or uninstall the program?
UPDATE:
procedure InitializeWizard();
var
InstallPath: String;
BackgroundBitmapImage: TBitmapImage;
BmpFileName : String;
Temp : String;
AppId : String;
Color : String;
begin
AppId:=ExpandConstant('{#AppId}');
if(AppIsInstalled(AppId, InstallPath)) Then
begin
UpdateRemovePageID := RepairRemove_CreatePage(wpWelcome);
end;
BmpFileName:= ExpandConstant('{src}\Background.bmp');
if FileExists(BmpFileName) then begin
BackgroundBitmapImage := TBitmapImage.Create(MainForm);
BackgroundBitmapImage.Align := alClient;
BackgroundBitmapImage.Autosize := True;
BackgroundBitmapImage.Center := True;
BackgroundBitmapImage.Bitmap.LoadFromFile(BmpFileName);
end;
BackgroundBitmapImage.BackColor := StringToColor('8cceff');
BackgroundBitmapImage.Parent := MainForm;
WizardForm.Caption := MainForm.Caption;
if(FileExists(ExpandConstant('{src}\WizImage.bmp'))) then begin
WizardForm.WizardBitmapImage.Bitmap.LoadFromFile(ExpandConstant('{src}') + '\WizImage.bmp');
end
if(FileExists(ExpandConstant('{src}\WizSmallImage.bmp'))) then begin
WizardForm.WizardSmallBitmapImage.Bitmap.LoadFromFile(ExpandConstant('{src}') + '\WizSmallImage.bmp');
end
end;
function RepairRemove_CreatePage(PreviousPageId: Integer): Integer;
var
Page: TWizardPage;
UpdateBmpFileName : String;
RemoveBmpFileName : String;
begin
Page := CreateCustomPage(PreviousPageId, ExpandConstant('{cm:RepairRemove_Caption}'), ExpandConstant('{cm:RepairRemove_Description}'));
BitmapImageUpdate := TBitmapImage.Create(Page);
UpdateBmpFileName := ExpandConstant('{tmp}\Update.bmp');
if not FileExists(UpdateBmpFileName) then begin
ExtractTemporaryFile(ExtractFileName(UpdateBmpFileName));
end;
BitmapImageUpdate.Bitmap.LoadFromFile(UpdateBmpFileName);
with BitmapImageUpdate do
begin
Parent := Page.Surface;
Left := ScaleX(64);
Top := ScaleY(64);
Width := ScaleX(32);
Height := ScaleY(32);
end;
Label1 := TLabel.Create(Page);
with Label1 do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:RepairRemove_Label1_Caption0}');
Left := ScaleX(120);
Top := ScaleY(72);
Width := ScaleX(243);
Height := ScaleY(13);
end;
BitmapImageRemove := TBitmapImage.Create(Page);
RemoveBmpFileName := ExpandConstant('{tmp}\TrashCan.bmp');
if not FileExists(RemoveBmpFileName) then begin
ExtractTemporaryFile(ExtractFileName(RemoveBmpFileName));
end;
BitmapImageRemove.Bitmap.LoadFromFile(RemoveBmpFileName);
with BitmapImageRemove do
begin
Parent := Page.Surface;
Left := ScaleX(64);
Top := ScaleY(120);
Width := ScaleX(32);
Height := ScaleY(32);
end;
Label2 := TLabel.Create(Page);
with Label2 do
begin
Parent := Page.Surface;
Caption := ExpandConstant('{cm:RepairRemove_Label2_Caption0}');
Left := ScaleX(120);
Top := ScaleY(128);
Width := ScaleX(243);
Height := ScaleY(13);
end;
UpdateButton := TRadioButton.Create(Page);
with UpdateButton do
begin
Parent := Page.Surface;
Caption := ExpandConstant('');
Left := ScaleX(32);
Top := ScaleY(72);
Width := ScaleX(17);
Height := ScaleY(17);
TabOrder := 0;
end;
RemoveButton := TRadioButton.Create(Page);
with RemoveButton do
begin
Parent := Page.Surface;
Caption := ExpandConstant('');
Left := ScaleX(32);
Top := ScaleY(128);
Width := ScaleX(17);
Height := ScaleY(17);
Checked := True;
TabOrder := 1;
TabStop := True;
end;
with Page do
begin
OnActivate := #RepairRemove_Activate;
OnShouldSkipPage := #RepairRemove_ShouldSkipPage;
OnBackButtonClick := #RepairRemove_BackButtonClick;
OnNextButtonClick := #RepairRemove_NextButtonClick;
OnCancelButtonClick := #RepairRemove_CancelButtonClick;
end;
Result := Page.ID;
end;
function RepairRemove_NextButtonClick(Page: TWizardPage): Boolean;
begin
Result := True;
//What I have to do here to correctly handle the user choice?
end;
how can I update or uninstall the program?
Update - That's what the installer does by default.
Uninstall - See How to detect old installation and offer removal?
You will also want to abort the installer after uninstallation:
Exit from Inno Setup Installation from [code].
function RepairRemove_NextButtonClick(Page: TWizardPage): Boolean;
begin
if RemoveButton.Checked then
begin
{ Uninstall here }
{ And abort installer }
ExitProcess(1);
end;
Result := True;
end;

Delphi image motion glitch

well, the problem might not be fixable but its really bothering me. I made a simple program that has three menus and used a timer to move the menu's left and right.
The problem comes when i move the images(menus) to the left of the screen(making the image.left negative. It makes this weird glitch, white blocks that show up out of nowhere. I'm not sure whats causing it, although the images(menus) are very high quality, if that's the cause....what can i do?
NB: amateur in Delphi
this is what i have
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, jpeg;
type
TForm1 = class(TForm)
pnl1: TPanel;
tmr1: TTimer;
btn1: TButton;
pnl2: TPanel;
pnl3: TPanel;
btn2: TButton;
btn3: TButton;
tmr2: TTimer;
tmr3: TTimer;
img1: TImage;
img2: TImage;
img3: TImage;
img4: TImage;
pnlmain: TPanel;
shp1: TShape;
procedure btn1Click(Sender: TObject);
procedure tmr1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btn2Click(Sender: TObject);
procedure tmr2Timer(Sender: TObject);
procedure btn3Click(Sender: TObject);
procedure tmr3Timer(Sender: TObject);
private
{ Private declarations }
iBar : Integer;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btn1Click(Sender: TObject);
begin
tmr1.Enabled := True;
btn1.Enabled := False;
btn2.Enabled := true;
btn3.Enabled := true;
end;
procedure TForm1.tmr1Timer(Sender: TObject);
begin
if iBar = 1 then
begin
if pnl2.Left <> 70 then
begin
pnl1.Left := pnl1.Left-72;
pnl2.Left := pnl2.Left-72;
pnl3.Left := pnl3.Left-72;
end
else
begin
pnl1.Left := pnl1.Left-70;
pnl2.Left := pnl2.Left-70;
pnl3.Left := pnl3.Left-70;
iBar := 2;
tmr1.Enabled := False;
end;
end;
if iBar = 3 then
begin
if pnl2.Left <> -70 then
begin
pnl3.Left := pnl3.Left+72;
pnl2.Left := pnl2.Left+72;
pnl1.Left := pnl1.Left+72;
end
else
begin
pnl3.Left := pnl3.Left+70;
pnl2.Left := pnl2.Left+70;
pnl1.Left := pnl1.Left+70;
iBar := 2;
tmr1.Enabled := False;
end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
iBar := 1;
end;
procedure TForm1.btn2Click(Sender: TObject);
begin
tmr2.Enabled := true;
btn2.Enabled := False;
btn1.Enabled := true;
btn3.Enabled := true;
end;
procedure TForm1.tmr2Timer(Sender: TObject);
begin
if iBar = 3 then
begin
if pnl1.Left <> -140 then
begin
pnl3.Left := pnl3.Left+144;
pnl1.Left := pnl1.Left+144;
pnl2.Left := pnl2.Left+144;
end
else
begin
pnl3.Left := pnl3.Left+140;
pnl1.Left := pnl1.Left+140;
pnl2.Left := pnl2.Left+140;
iBar := 1;
tmr2.Enabled := False;
end;
end;
if iBar = 2 then
begin
if pnl1.Left <> -70 then
begin
pnl2.Left := pnl2.Left+72;
pnl1.Left := pnl1.Left+72;
pnl3.Left := pnl3.Left+72;
end
else
begin
pnl2.Left := pnl2.Left+70;
pnl1.Left := pnl1.Left+70;
pnl3.Left := pnl3.Left+70;
iBar := 1;
tmr2.Enabled := False;
end;
end;
end;
procedure TForm1.btn3Click(Sender: TObject);
begin
tmr3.Enabled := True;
btn3.Enabled := False;
btn2.Enabled := true;
btn1.Enabled := true;
end;
procedure TForm1.tmr3Timer(Sender: TObject);
begin
if iBar = 1 then
begin
if pnl3.Left <> 140 then
begin
pnl1.Left := pnl1.Left-144;
pnl3.Left := pnl3.Left-144;
pnl2.Left := pnl2.Left-144;
end
else
begin
pnl1.Left := pnl1.Left-140;
pnl3.Left := pnl3.Left-140;
pnl2.Left := pnl2.Left-140;
iBar := 3;
tmr3.Enabled := False;
end;
end;
if iBar = 2 then
begin
if pnl3.Left <> 70 then
begin
pnl2.Left := pnl2.Left-72;
pnl3.Left := pnl3.Left-72;
pnl1.Left := pnl1.Left-72;
end
else
begin
pnl2.Left := pnl2.Left-70;
pnl3.Left := pnl3.Left-70;
pnl1.Left := pnl1.Left-70;
iBar := 3;
tmr3.Enabled := False;
end;
end;
end;
end.
You will never achieve smooth animation of any sort when driving the animation from a timer and/or relying on windows controls as your "sprites".
The standard timer component in Delphi uses Windows message based timer events, which means that the processing of these events relies on the message handling of your UI thread, which is also dealing with mouse movements, painting and a whole host of other messages required to keep your UI responsive.
Timer messages take the absolute lowest priority.
Similarly, windows controls are designed to manage interaction with a user. Animation and effects can be used as part of that interaction, but the system is not optimised for physically moving controls around the screen since this is an extremely unusual behaviour for a UI control.
You might remember some of the older Windows "joke" programs with a message box where the "OK" button jumps around the screen whenever you try to click on it ... ? Controls that move around are more often annoying than useful.
Animating in this way can be OK as a proof of concept or prototype to put in front of a user to elicit feedback, but you will always run into "glitches" of the sort you describe.
For your "production" code you should re-think your UI implementation.
If you really need an animated menu then design and implement a control that implements that as a behaviour within a single control.
Manage the animation of the elements in that control (i.e. updating the "model" of the positions of the elements) on a background thread. To render each "frame", incorporate a separate thread specifically to post messages to the control to paint itself as frequently as required to achieve smooth animation, this painting is then the only code that has to operate in concert with all the other UI messages your app is dealing with.
As an efficiency refinement, if you have a number of controls being animated in this way in your form/app, you can usually come up with a scheme that uses a single thread to post the paint messages to all the required controls.

Pascal: Error trying to rewrite array and assistance with printing my array

So i'm working on this pascal application which has a menu where you can do multiple things.
After entering an album (which is what my program does) and trying to edit it by writing over the current album I get an error as shown in the image.
There have been no errors when compiling except the warning:
(100,9) Warning: Function result variable does not seem to initialized
Here is my code:
program MusicPlayer;
uses TerminalUserInput;
type
// You should have a track record
TrackRec = record
name: String;
location: String;
end;
type TrackArray = array of TrackRec;
GenreType = (Pop, Rap, Rock, Classic);
AlbumRec = Record
name: String;
genre: GenreType;
location: array of TrackRec; // this and track should be track: array of TrackRec
numberOfTracks: Integer;
tracks: TrackArray;
end;
type AlbumArray = array of AlbumRec; // this should be an array of AlbumRec
function ReadGenre(prompt: String): GenreType;
var
option: Integer;
begin
WriteLn('Press 1 for Pop');
WriteLn('Press 2 for Rap');
WriteLn('Press 3 for Rock');
WriteLn('Press 4 for Classic');
option := ReadInteger(prompt);
while (option<1) or (option>3) do
begin
WriteLn('Please enter a number between 1-4');
option := ReadInteger(prompt);
end;
case option of
1: result := Pop;
2: result := Rap;
3: result := Rock;
else
result := Classic;
end;
end;
function CheckLength(prompt: string): Integer;
var
i: Integer;
begin
i := ReadInteger(prompt);
while (i < 0) or (i > 20) do
begin
WriteLn('Please enter a number between 1-20');
i := ReadInteger(prompt);
end;
result := i;
end;
function ReadTracks(count: Integer): TrackArray;
var
i: Integer;
begin
setLength(result, count);
for i := 0 to High(result) do
begin
result[i].name := ReadString('Track Name: ');
result[i].location := ReadString('Track Location: ');
end;
end;
function ReadAlbum(): AlbumRec;
begin
result.name := ReadString('What is the name of the album?');
result.genre := ReadGenre('What is the genre of the album?');
result.numberOfTracks := CheckLength('How many tracks are in the album?');
result.tracks := ReadTracks(result.numberOfTracks);
end;
function ReadAlbums(count: Integer): AlbumArray;
var
i: Integer;
begin
SetLength(result, count);
for i := 0 to High(result) do
begin
result[i] := ReadAlbum();
end;
end;
function ChangeAlbum(count: Integer): AlbumArray;
var
i: Integer;
begin
for i := count to count do
begin
result[i] := ReadAlbum();
end;
end;
procedure PrintAlbum(count: Integer; album: array of AlbumRec);
var
i: Integer;
begin
if count = 1 then
begin
for i := 0 to High(album) do
begin
WriteLn('Album Number: ', i);
WriteLn('Album name is: ', album[i].name);
WriteLn('Album genre is: ', album[i].genre);
end
end;
for i := 1 to count - 1 do
begin
WriteLn('Album name is: ', album[i].name);
WriteLn('Album genre is: ', album[i].genre);
end;
end;
procedure PrintTrack(tracks: TrackArray);
var
i: Integer;
begin
i := ReadInteger('Which track number do you wish to play?');
i := i - 1;
WriteLn('Now playing track: ', tracks[i].name);
WriteLn('Track location: ', tracks[i].location);
end;
function CheckIfFinished(): Boolean;
var answer: String;
begin
WriteLn('Do you want to enter another set of tracks? ');
ReadLn(answer);
LowerCase(answer);
case answer of
'no': result := true;
'n': result := true;
'x': result := true;
else
result := false;
end;
end;
procedure Main();
var
i, count, select, change: Integer;
albums: AlbumArray;
begin
WriteLn('Please select an option: ');
WriteLn('-------------------------');
WriteLn('1. Read Albums');
WriteLn('2. Display Albums');
WriteLn('3. Select an Album');
WriteLn('4. Update an Album');
WriteLn('5. Exit');
WriteLn('-------------------------');
repeat
i := ReadInteger('Your Option:');
case i of
1:
begin
count := ReadInteger('How many albums: ');
albums := ReadAlbums(count);
end;
2:
begin
WriteLn('1. Display All Albums');
WriteLn('2. Display All Albums by Genre');
select := ReadInteger('Your Option: ');
if i = 1 then
begin
PrintAlbum(select, albums);
end;
// if i = 2 then
// WriteLn('1. Pop');
// WriteLn('2. Rap');
// WriteLn('3. Rock');
// WriteLn('4. Classic');
// albums := ReadAlbums(count);
end;
3:
begin
select := ReadInteger('Which album would you like to play? ');
PrintAlbum(select, albums);
PrintTrack(albums[select-1].tracks);
end;
4:
begin
change := ReadInteger('Which album would you like to edit?');
albums := ChangeAlbum(change);
end;
end;
until i = 5;
end;
begin
Main();
end.
The function that the warning refers to, on line 100, is
function ChangeAlbum(count: Integer): AlbumArray;
var
i: Integer;
begin
for i := count to count do
begin
result[i] := ReadAlbum();
end;
end;
The warning says:
Warning: Function result variable does not seem to initialized
And indeed the result variable has not been initialized.
The design of the function is wrong though. You are trying to modify an existing element in an array. You should not be returning a new array. The function is not necessary though. You should simply remove it. Then you need to look at the one place where you call the function.
change := ReadInteger('Which album would you like to edit?');
albums := ChangeAlbum(change);
You should instead code that like this:
change := ReadInteger('Which album would you like to edit?');
albums[change] := ReadAlbum();
I've not checked anything else in your program. I would not be surprised if there are other problems. I've just tried to address the specific question that you asked.

TObjectList re-order

I need to re-order a TObjectList, according to some rules. How can I achieve this?
So I add panels to a ScrollBox dinamically.
When I add them, I also add them to the ObjectList in the order that they are added at runtime, for future use. Then I can re-organize the panels in the scrollBox by drag/drop.
I want the ObjectList to mirror the same order that is set at runtime by drag/drop.
Here is my code:
var
MainForm: TMainForm;
PanelList,PanelListTMP:TObjectList;
implementation
...
procedure TMainForm.FormCreate(Sender: TObject);
begin
PanelList:=TObjectList.Create;
PanelListTMP:=TObjectList.Create;
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
AddPanel('0');
AddPanel('1');
AddPanel('2');
AddPanel('3');
AddPanel('4');
end;
procedure TMainForm.Addpanel(what:string);
var
pan:TPanel;
bv:TShape;
begin
pan:=TPanel.Create(self);
pan.Parent:=TheContainer;
pan.Height:=50;
pan.BevelOuter:=bvNone;
pan.BorderStyle:=bsNone;
pan.Ctl3D:=false;
pan.Name:='LayerPan'+what;
pan.Caption:=what;
pan.Align:=alBottom;
pan.OnMouseDown:=panMouseDown;
end;
procedure TMainForm.panMouseDown(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer);
var
i:integer;
idu:String;
panui:TPanel;
begin
panui:=Sender as TPanel;
panui.ParentColor:=false;
panui.BringToFront;
// DRAG DROP STUFF
ReleaseCapture;
panui.Perform(wm_nclbuttondown,HTCAPTION,0);
for i := 0 to MainForm.ComponentCount - 1 do
begin
if MainForm.Components[i] is TWinControl then
if TWinControl(MainForm.Components[i]) is TPanel then
if (TWinControl(MainForm.Components[i]) as TPanel).Parent=MainForm.TheContainer then
begin
(TWinControl(MainForm.Components[i]) as TPanel).Align:=alBottom;
end;
end;
TheContainer.ScrollInView(panui);
ReOrderPanels;
end;
Procedure TMainForm.ReOrderPanels;
begin
end;
What should I do in the ReOrderPanels procedure?
I was thinking about feeding the panels of the ScrollBox from bottom to top into a new TObjectList (PanelListTMP), clear the PanelList and re-add them from the PanelListTMP, but when I do that, I get an error: Access Violation, and EInvalidPointer - Invalid Pointer Operation
So this is what I thought:
procedure TMainForm.ReOrderPanels;
var
ctrl:TControl;
pos:TPoint;
pan:TPanel;
bad:boolean;
ord,i:integer;
begin
memo2.Lines.Add('*** new order START');
panelListTMP.Clear;
// scroll top
TheContainer.VertScrollBar.Position := 0;
// scroll down
TheContainer.VertScrollBar.Position := TheContainer.VertScrollBar.Range;
// get panel
Pos:=TheContainer.ClientOrigin;
Pos.Y:=Pos.Y+TheContainer.Height-5;
ctrl := FindVCLWindow(pos) ;
if ctrl is TPanel then
if TPanel(ctrl).Parent = TheContainer then
begin
pan:=(ctrl as TPanel);
panelListTMP.Add(pan);
end;
ord:=1;
bad:=false;
repeat
repeat
Pos.Y:=pos.Y-1;
until (FindVCLWindow(pos) is TPanel)and(FindVCLWindow(pos)<>pan);
if (FindVCLWindow(pos) is TPanel)and(FindVCLWindow(pos).Name<>'LayerPan') then
begin
pan:=FindVCLWindow(pos) as TPanel;
containeru.VertScrollBar.Position := 0;
containeru.ScrollInView(pan);
ord:=ord+1;
panelListTMP.Add(pan);
end
else
bad:=true;
until bad=true;
// and now I do the swap between the ObjectLists...
panelList.Clear;
for i:=0 to PanelListTMP.Count-1 do
begin
(PanelListTMP.Items[i] as TPanel).Parent:=containeru;
panelList.Add(PanelListTMP.Items[i]);
end;
end;
So I assume that because the ObjectList is storing pointers to the actual objects, then when I clear the initial ObjectList, the actual objects are freed, so the second ObjectList contains a list of pointers that are no longer viable...
But then how can I achieve what I want?
So on ButtonClick, I get a ObjectList that contains panels in the following order:
PanelList[0] - Panel0
PanelList[1] - Panel1
PanelList[2] - Panel2
PanelList[3] - Panel3
PanelList[4] - Panel4
After I drag - drop panels inside the ScrollBox, I can end up with an order like this (in the ScrollBox)
Panel3
panel1
Panel4
Panel2
Panel0
But in the ObjectList, the order is the same as before...
Again, I want to be able to have the ObjectList ordered according to the order of the panels from the scrollBox.
In the re-order procedure I actually get all the panels in the desired order.
I just need to have them in the same order in my ObjectList.
Is there any other way of doing this? Other that with me creating a new class that would hold an index beside a TPanel and use that in the ObjectList to maintain the order?
TObjectList has an OwnsObjects property that is True by default. Make sure to set it to False since you don't want the list to auto-free the objects as they are owned by the Form.
As for the actual sorting of the TObjectList, consider using its Sort() or SortList() method for that. After you have repositioned the Panels as desired within their container, call Sort() or SortList(). The sorting callback you provide will be given two object pointers at a time while the sorting is iterating the list. Use the current positions of the objects relative to each other to tell the list what order they should appear in.
Try something like this:
var
MainForm: TMainForm;
PanelList: TObjectList;
implementation
...
procedure TMainForm.FormCreate(Sender: TObject);
begin
PanelList := TObjectList.Create(False);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
PanelList.Free;
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
AddPanel('0');
AddPanel('1');
AddPanel('2');
AddPanel('3');
AddPanel('4');
end;
procedure TMainForm.Addpanel(what: string);
var
pan: TPanel;
bv: TShape;
begin
pan := TPanel.Create(Self);
try
pan.Parent := TheContainer;
pan.Height := 50;
pan.BevelOuter := bvNone;
pan.BorderStyle := bsNone;
pan.Ctl3D := false;
pan.Name := 'LayerPan'+what;
pan.Caption := what;
pan.Align := alBottom;
pan.OnMouseDown := panMouseDown;
PanelList.Add(pan);
except
pan.Free;
raise;
end;
end;
procedure TMainForm.panMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
i: integer;
idu: String;
panui, pan: TPanel;
tmpList: TObjectList;
begin
panui := Sender as TPanel;
panui.ParentColor := false;
panui.BringToFront;
// DRAG DROP STUFF
ReleaseCapture;
panui.Perform(WM_NCLBUTTONDOWN, HTCAPTION, 0);
tmpList := TObjectList.Create(False);
try
for i := 0 to TheContainer.ControlCount - 1 do
begin
if TheContainer.Controls[i] is TPanel then
tmpList.Add(TPanel(TheContainer.Controls[i]));
end;
for i := 0 to tmpList.Count - 1 do
TPanel(tmpList[i]).Align := alBottom;
finally
tmpList.Free;
end;
TheContainer.ScrollInView(panui);
ReOrderPanels;
end;
function SortPanels(Item1, Item2: Pointer): Integer;
begin
Result := TPanel(Item2).Top - TPanel(Item1).Top;
end;
procedure TMainForm.ReOrderPanels;
begin
PanelList.Sort(SortPanels);
// Alternatively:
{
PanelList.SortList(
function(Item1, Item2: Pointer): Integer;
begin
Result := TPanel(Item2).Top - TPanel(Item1).Top;
end
);
}
end;
I think I found my answer using a temporary ObjectList and Extract(Object)
My code that seems to work is:
procedure TMainForm.ReOrderPanels;
var
ctrl:TControl;
pos:TPoint;
pan,panx:TPanel;
bad:boolean;
ord,i:integer;
begin
panelListTMP.Clear;
panelList.OwnsObjects:=false;
// scroll top
TheContainer.VertScrollBar.Position := 0;
// scroll down
TheContainer.VertScrollBar.Position := TheContainer.VertScrollBar.Range;
// get panel
Pos:=TheContainer.ClientOrigin;
Pos.Y:=Pos.Y+TheContainer.Height-5;
ctrl := FindVCLWindow(pos) ;
if ctrl is TPanel then
if TPanel(ctrl).Parent = TheContainer then
begin
pan:=(ctrl as TPanel);
panelListTMP.Add(PanelList.Extract(pan) as TPanel);
end;
ord:=1;
bad:=false;
repeat
repeat
Pos.Y:=pos.Y-1;
until (FindVCLWindow(pos) is TPanel)and(FindVCLWindow(pos)<>pan);
if (FindVCLWindow(pos) is TPanel)and(FindVCLWindow(pos).Name<>'LayerPan') then
begin
pan:=FindVCLWindow(pos) as TPanel;
TheContainer.VertScrollBar.Position := 0;
TheContainer.ScrollInView(pan);
ord:=ord+1;
panelListTMP.Add(PanelList.Extract(pan) as TPanel);
end
else
bad:=true;
until bad=true;
panelList.Clear;
panelListTMP.OwnsObjects:=false;
i:=0;
while (PanelListTMP.Count<>0) do
panelList.Add(PanelListTMP.Extract(PanelListTMP.Items[i]) as TPanel);
panelList.OwnsObjects:=true;
panelListTmp.Clear;
end;

ClientDataSet TBCDField rounding

I'm using Delphi 5 + BDE + Oracle. I have the following function:
class function TClientDataSetFactory.GetClientDataSet(
const qryGen: TDataSet): TClientDataSet;
var
dspDados: TDataSetProvider;
begin
Result := nil;
try
try
Result := TClientDataSet.Create(nil);
dspDados := TDataSetProvider.Create(Result);
dspDados.DataSet := qryGen;
qryGen.Active := True;
qryGen.First;
Result.Data := dspDados.Data;
Result.First;
except
on E: Exception do
begin
raise;
end;
end;
finally
end;
end;
so, when a run this:
var
qryGen: TQuery;
cdsGen: TClientDataSet;
begin
qryGen := nil;
try
try
qryGen := CriaQuery();
qryGen.SQL.Text :=
'SELECT SUM(TOTAL) AS TOTAL FROM MYTABLE';
cdsGen := TClientDataSetFactory.GetClientDataSet(qryGen);
ShowMessageFmt('Total: %f', [cdsGen.FieldByName('TOTAL').AsFloat]);
except
on E: Exception do
begin
raise;
end;
end;
finally
if Assigned(qryGen) then FreeAndNil(qryGen);
end;
end;
i got "159,00" but, if i run this:
ShowMessageFmt('Total: %f', [qryGen.FieldByName('TOTAL').AsFloat]);
i got "159,25".
can someone help me?
I solved the problem with another solution.
type
TInternalQuery = class(TQuery)
protected
procedure InternalInitFieldDefs; override;
public
constructor Create(AOwner: TComponent; const qryGen: TQuery); reintroduce;
end;
constructor TInternalQuery.Create(AOwner: TComponent; const qryGen: TQuery);
var
intCont: Integer;
begin
inherited Create(AOwner);
Self.DatabaseName := qryGen.DatabaseName;
Self.UpdateObject := qryGen.UpdateObject;
Self.SQL.Text := qryGen.SQL.Text;
for intCont := 0 to Self.ParamCount - 1 do
begin
Self.Params[intCont].Value := qryGen.Params[intCont].Value;
end;
end;
procedure TInternalQuery.InternalInitFieldDefs;
var
intCont: Integer;
begin
inherited InternalInitFieldDefs;
for intCont := 0 to FieldDefs.Count - 1 do
begin
if (FieldDefs[intCont].Size = 0) and (FieldDefs[intCont].DataType = ftBCD) then
begin
FieldDefs[intCont].Precision := 64;
FieldDefs[intCont].Size := 32;
end;
end;
end;
the problem is ((FieldDefs[intCont].Size = 0) and (FieldDefs[intCont].DataType = ftBCD)). when ClientDataSet is created, the field is truncated, because when oracle has a function like "SUM(TOTAL)" the result field is created with size 0, so the clientdataset handle the field as Integer field.
Try with
ShowMessageFmt('Total: %n', [cdsGen.FieldByName('TOTAL').AsFloat])
or this
cdsGen := TClientDataSetFactory.GetClientDataSet(qryGen);
**(cdsGen.FieldByName('Total') as TFloatField).DisplayFormat := '0.00';**
ShowMessageFmt('Total: %f', [cdsGen.FieldByName('TOTAL').AsFloat])

Resources