Reputation:
I'm currently having a major headache with typecasting of component properties.
On my form, i have a TPanel called "scene". Also on the form, i have a button that creates a TSelection
, and within that TSelection
creates a TImage
and then load a picture into that TImage. The name for the TSelection is assigned via a TEdit known as "ImgObjName". It then writes this name to an inifile. The events for the TSelection are assigned to procedures elsewhere in the code. As you know, the TSelection component can be moved around (and resized) at runtime. The TImage has it's HitTest turned off while the TSelection has it turned on.
The above works as i want it to, but the next part is where i'm stuck. Essentially, on a timer, i want to write a select few of the properties of each child component to an TMemIniFile. There's 2 ways i'm willing to do this;
1) Write the properties of each child to seperate TMemInifiles.
2) Write the properties of each child to a single TMemIniFile, but make the section
identify which component the values in that section relate to.
I've tried a few different methods, but all of them have caused me some major problems (usually "index out of bounds").
My current method is as such;
ChgPos
is a global boolean variable which is TRUE when the the mousedown event on one of the TSelection objects is fire, and FALSE when the MouseUp event is fired. This boolean procedure works perfectly for these purposes so no changes are needed there.
TimerBar
is a TTrackBar
that's been created at designtime. It's value changes based on a timer.
AnimIni
is the TMemIniFile which has been assigned earlier in the code. For this purpose, i've set it not to free up the file (so as there's no access violations).
var
i: Integer;
PosX, PosY: Integer;
begin
for i := 0 to Scene.ChildrenCount - 1 do
begin
if Scene.Components[i] is TSelection then
begin
PosX := AnimIni.ReadInteger(IntToStr(Round(TimerBar.Value)) + '_Object' +
IntToStr(i), 'PosX', PosX);
PosY := AnimIni.ReadInteger(IntToStr(Round(TimerBar.Value)) + '_Object' +
IntToStr(i), 'PosY', PosY);
end;
end;
if ChgPos = False then
begin
if Scene.Components[i] is TSelection then
begin
(Scene.Components[i] as TSelection).Position.X := PosX;
(Scene.Components[i] as TSelection).Position.Y := PosY;
end;
end
else if ChgPos = True then
begin
AnimIni.WriteInteger(IntToStr(Round(TimerBar.Value)) + '_Object' +
IntToStr(i), 'PosX', Round((Scene.Children[i] as TSelection).Position.X));
AnimIni.WriteInteger(IntToStr(Round(TimerBar.Value)) + '_Object' +
IntToStr(i), 'PosY', Round((Scene.Children[i] as TSelection).Position.Y));
end;
end;
I'm struggling to figure out where to go with this. I'm getting "Index out of range" errors with it. I'll also need to save the TImage component properties (particularly the parent and Bitmap location, but i feel it's important for me to get the code working with at least one component at the moment).
I'm somewhat new to typecasting (as all my previous projects worked without the need for it), but all my experience with it so far has been pleasently successful. It's just in this particular case it's proven to become more complicated than i can work out without some assistance.
I did try WriteComponent
and ReadComponent
and using multiple files to stream the data in realtime in relation to the value of TimerBar but it's much too slow for what i want to achieve (particularly on the write function). The inifile method does work from my earlier testing, but it's actually working with typecasting of multiple components that are created at runtime that i'm having issues with.
Can anyone shed some light on a potential solution or the direction i should head in?
Upvotes: 1
Views: 636
Reputation: 613382
Your loop counter and indexed properties don't match. ComponentCount
and Components[]
go together. And ChildrenCount
and Children[]
go together. You want to work with the latter pair since you are interesting in the children of the controls. The ComponentCount
and Components[]
properties refer to ownership which is a different concept.
What's more your loop ends but you continue using the loop variable after the loop variable. That's clearly wrong. It looks like it needs to be inside the loop and inside the Scene.Children[i] is TSelection
test.
As an aside ChildrenCount
is grammatically incorrect alongside ComponentCount
and ControlCount
. This property should have been named ChildCount
.
Upvotes: 2
Reputation: 43669
Components
property, but the Children
property. (I assume your code compiled and XE2 has a Children
property, otherwise I think you mean Controls
and ControlCount
).Like LU RD already commented, you are using the for-loop variable i
outside the for-loop. I am sure you want it inside. You are also warned by the compiler for this:
FOR-loop variable 'i' may be undefined after loop
Always make sure you have zero! compiler errors, warnings and hints.
ChgPos
is false.No guarantees given, but I think the routine should look like this (including a few syntax improvements):
var
i: Integer;
Selection: TSelection;
PosX, PosY: Integer;
begin
for i := 0 to Scene.ChildrenCount - 1 do
if Scene.Children[i] is TSelection then
begin
Selection := Scene.Children[i] as TSelection;
if ChgPos then
begin
AnimIni.WriteInteger(IntToStr(Round(TimerBar.Value)) + '_Object' +
IntToStr(i), 'PosX', Round(Selection.Position.X));
AnimIni.WriteInteger(IntToStr(Round(TimerBar.Value)) + '_Object' +
IntToStr(i), 'PosY', Round(Selection.Position.Y));
end
else
begin
PosX := AnimIni.ReadInteger(IntToStr(Round(TimerBar.Value)) +
'_Object' + IntToStr(i), 'PosX', PosX);
PosY := AnimIni.ReadInteger(IntToStr(Round(TimerBar.Value)) +
'_Object' + IntToStr(i), 'PosY', PosY);
Selection.Position.X := PosX;
Selection.Position.Y := PosY;
end;
end;
end;
Although I seriously doubt the default values for the AnimIni.ReadInteger
function, PosX
and PosY
, which are unassigned. If no section in the ini file is found, then PosX
and PosY
will have arbitrary values. You should initialize them to whatever makes sense.
Upvotes: 5