Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Problem with TValueListEditor

520 views
Skip to first unread message

Patrick Casey

unread,
Jan 15, 2002, 5:12:28 PM1/15/02
to

Hi Folks,

I've been trying to use a tvaluelisteditor as part of a form designer
project I've finally gotten time to work on.

Tvaluelisteditor has a list of Key/Value pairs stored in the strings
field in the format key=value.

I seem to be crashing whenever I access the strings list after having
emptied it first.

Example try running these three lines in sequence:

valuelist.strings.add('hello=world'); // runs fine
valuelist.strings.clear(); // no problem with this line, BUT
valuelist.strings.add('hello=world'); // instant GPF

Has anyone else found this issue? If so is there a known workaround or
patch? I'd really hate to have to write my own valuelist editor if I could
just use this one.

Thanks,

--- Pat


Michael Skachkov

unread,
Jan 16, 2002, 3:35:01 AM1/16/02
to
> Has anyone else found this issue? If so is there a known workaround or
> patch? I'd really hate to have to write my own valuelist editor if I could
> just use this one.
This is the copy of my message sent on 11.25.2001:

----------------------------------------------------------------------
Hi, All!

I'm a bit disappointed in TValueListEditor component.
Today I started to use it and found that:

when I want to add new row in run-time I get:
1. Everything is OK, when row count > 0
2. Access violation when row count = 0

I spent a lot of time debugging my single code line :-) :
{code}
vleDeviceProps.Strings.Add('test=1');
{end of code}

... and found that there is an incorrect behavior of
TValueListStrings.InsertItem method:

Let's see what's happening when my code line is being executed
(I decided to provid it under MPL 1.1 ;-)):
1. TStringList.Add
2. TStringList.AddObject
3. TStringList.Find; {this one is called only when Sorted = True}
4. TValueListStrings.InsertItem; {The beautifull example of
polymorphism :-)}

So, lets examine TValueListStrings.InsertItem:
{ code by Borland}
procedure TValueListStrings.InsertItem(Index: Integer; const S: string;
AObject: TObject);
var
OldCount: Integer;
begin
KeyIsValid(ExtractName(S));
Changing; {Point A1. Remember It}
OldCount := Count;
inherited;
SetLength(FItemProps, Count);
if Index < OldCount then
System.Move(FItemProps[Index], FItemProps[Index + 1], (OldCount -
Index) * SizeOf(TItemProp));
FItemProps[Index] := nil;
Changed; {Point B1. Remember It}
end;
{end of code}

From the first sight everything is clear:
[1]. "Begin update": Changing method
[2]. Call inherited method - TStringList.InsertItem.
[3]. Change the size of array of TItemProp
[4]. Set current ItemProp to nil(BTW, what's about AObject parameter? ;-))
[5]. "End update": Change method;

(Concerning Item [4] - I'll return to it later.)

But lets "expand" those 'inherited' TStringList.InsertItem method:

{ code by Borland}
procedure TStringList.InsertItem(Index: Integer; const S: string; AObject:
TObject);
begin
Changing;{Point A2. Remember It}
if FCount = FCapacity then Grow;
if Index < FCount then
System.Move(FList^[Index], FList^[Index + 1], (FCount - Index) *
SizeOf(TStringItem));
with FList^[Index] do
begin
Pointer(FString) := nil;
FObject := AObject;
FString := S;
end;
Inc(FCount);
Changed; {Point B2. Remember It} {"Mama, it's a rock'n'roll!!!" (c) DDT
;-)}
end;
{end of code}

Did you get that? No? OK, I'll try to explain.
Let's "include" inherited method into a overriden version:

{code}
procedure TValueListStrings.InsertItem(Index: Integer; const S: string;
AObject: TObject);
var
OldCount: Integer;
begin
KeyIsValid(ExtractName(S));
Changing; {Point A1. Remember It}
OldCount := Count;
{inherited begin}
Changing;{Point A2. Remember It}
if FCount = FCapacity then Grow;
if Index < FCount then
System.Move(FList^[Index], FList^[Index + 1], (FCount - Index)
* SizeOf(TStringItem));
with FList^[Index] do
begin
Pointer(FString) := nil;
FObject := AObject;
FString := S;
end;
Inc(FCount);
*****Changed; {Point B2. Remember It}
{inherited end}
SetLength(FItemProps, Count);
if Index < OldCount then
System.Move(FItemProps[Index], FItemProps[Index + 1], (OldCount -
Index) * SizeOf(TItemProp));
FItemProps[Index] := nil;
Changed; {Point B1. Remember It}
end;
{end of code}

Ok, I give up:
in the line of Point B2 (those with '*****') due to polymorphism
THE TVALUESTRINGS.CHANGED METHOD IS CALLED!
That means that updates (grid updates) comese BEFORE these lines
{code}
SetLength(FItemProps, Count);
if Index < OldCount then
System.Move(FItemProps[Index], FItemProps[Index + 1], (OldCount -
Index) * SizeOf(TItemProp));
FItemProps[Index] := nil;
{code}
are executed.

So the memory for TItemProp reference is not got.

in some of TValueListEditor methods (GetEditStyle) the
TValueListStrings.FindItemProp method is called.
This method tries to get a reference to TItemProp instance:
{line: 1037} Result := FItemProps[Index];

and, of course, here comes an access violation exception:
length of FItemProps array is 0.

SOLVING:
It seems that the most easy way for solving this problem is to rewrite
TValueListStrings.InsertItem method this way:
{code}
procedure TValueListStrings.InsertItem(Index: Integer; const S: string;
AObject: TObject);
var
OldCount: Integer;
begin
KeyIsValid(ExtractName(S));
Changing; {Point A1. Remember It}
OldCount := Count;
{ do not call inherited method but paste some of its code here }
{ WITHOUT Point A2 and Point B2 lines}
SetLength(FItemProps, Count);
if Index < OldCount then
System.Move(FItemProps[Index], FItemProps[Index + 1], (OldCount -
Index) * SizeOf(TItemProp));
FItemProps[Index] := nil;
Changed; {Point B1. Remember It}
end;
{end of code}

----------------------------
Though this message is about Point B2, but there is a need to
say some "good" words about Point A2 line:
TValueListStrings.Changing method is called twice in three(!!!) lines of
code:
{code}
Changing; {Point A1, line: 1054}
OldCount := Count;{this line does not influence grid behavior}
inherited;{ inherited method starts with Changing - Point A2}
{end of code}

That could probably be OK(though I don't think so), but
TValueListStrings.Changing method calls:
* inherited Changing method
* TValueListEditor.StringsChanging
* TCustomGrid.HideEdit
* TInplaceEdit.Hide
* TCustomGrid.UpdateText
* TCustomMaskEdit.GetText
* TValueListEditor.SetEditText
* Some more methods
* TInplaceEdit.Invalidate;
* SetFocus(Grid.Handle);

Well, the same actions being executed twice without any changed of a grid.
It must be to save cpu time and resources usage ;-)
---------------------

I have Delphi 6, Ent, Update Pack 1 installed.

Maybe some of Delphi developers can comment this message?
(Maybe I get wrong in some of my code lines ;-)?)

Regards,
Michael Skachkov
Author of Property Editors Project
http://delphiplus.spils.lv/downloads/tips/mspropedit_d6.zip


Patrick Casey

unread,
Jan 16, 2002, 6:01:06 PM1/16/02
to

Thanks Michael, this will help.

--- Pat
"Michael Skachkov" <ze...@dagobah.mk.ua> wrote in message
news:3c453b18_2@dnews...

0 new messages