Remy Lebeau (TeamB)
2008-07-03 01:20:47 UTC
For this, I use TStringGrid and its Objects properties.
I would not suggest storing the actual objects in the grid directly. Youshould store them in your own TList or other container, and then you can add
just pointers/indexes for your list into the Grid for easier access. This
way, you are completely in control of the memory of the objects, and when
and how that memory is cleaned up.
now I start to get unexspected Access Violations
Then you are likely using the memory incorrectly, or accessing it after ithas already been freed.
I seriously consider not bothering about memory cleanup
ALWAYS cleanup any memory you allocate yourself.just because I cannot find the errors to make any sense
Have you tried using the debugger to help you? When an AV occurs, you havea call stack leading up to the AV. You can back trace what code causes the
AV. There are also plenty of third-party tools that will help you
troubleshoot AVs as well, such as EurekaLog or MadExcept.
Maybe I'm overlooking the strange choice of the methods for
TStrings already doing the Objects cleanup for me??
No, it does not. You are responsible for the memory.TStrings already doing the Objects cleanup for me??
TStringList* SLP = new TStringList(); SLP->Add(FilePath);
StringGrid1->Objects[0][StringGrid1->RowCount-1]=SLP;
SLP = new TStringList(); SLP->LoadFromFile(FilePath+FileName);
StringGrid1->Objects[1][StringGrid1->RowCount-1]=SLP;
As I understand it, new Objects created in this way must
Yes, they do.StringGrid1->Objects[0][StringGrid1->RowCount-1]=SLP;
SLP = new TStringList(); SLP->LoadFromFile(FilePath+FileName);
StringGrid1->Objects[1][StringGrid1->RowCount-1]=SLP;
As I understand it, new Objects created in this way must
it seems there is no published method for deleting rows from the
stringgrid
TCustomGrid does have a DeleteRow() method, but it is protected. You canstringgrid
use an accessor class to reach it, though:
class TStringGridAccess : public TStringGrid
{
public:
void __fastcall DeleteRow(int ARow)
{
TCustomGrid::DeleteRow(ARow);
}
};
((TStringGridAccess*)StringGrid1)->DeleteRow(StringGrid1->Row);
Take e.g. the case where three files have just been added.
Objects[0][1], Objects[0][2] and Objects[0][3] will point to newly created
StringList objects that I choose to call SL1, SL2 and SL3 here.
If then row 2 is removed, my FileDropExecute event will first delete the
StringList SL2.
After that, StringGrid1->Rows[2]=StringGrid1->Rows[3] and this is where
the access violation occurs.
Simply accessing the Rows[] property will not enough to cause an AV. AreObjects[0][1], Objects[0][2] and Objects[0][3] will point to newly created
StringList objects that I choose to call SL1, SL2 and SL3 here.
If then row 2 is removed, my FileDropExecute event will first delete the
StringList SL2.
After that, StringGrid1->Rows[2]=StringGrid1->Rows[3] and this is where
the access violation occurs.
you sure your StringGrid pointer is actually valid at the time your code is
running?
Surely it cannot be that way that the Assign / operator = methods
themselves check for objects and delete them?
No, they do not.themselves check for objects and delete them?
But I cannot explain the Access Violation any other way.
You are either accessing wrong indexes or invalid pointers.By the way, this doesn't explain at all why there is no
problem with deleting Objects[1][i]!
Is a non-NULL pointer being returned?problem with deleting Objects[1][i]!
Try this code instead:
class TForm1 : public TForm
{
...
private:
TList *Files;
...
};
struct MyFileInfo
{
AnsiString FilePath;
TStringList *Contents;
MyFileInfo() : Contents(new TStringList) {}
~MyFileInfo() { delete Contents; }
};
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Files = new TList;
Defaults = new TStringList;
...
}
__fastcall TForm1::~TForm1()
{
for(int i = 0; i < Files->Count; ++i)
delete (MyFileInfo*) Files->Items[i];
delete Files;
delete Defaults;
}
void __fastcall TForm1::AssignProperties(int R, const AnsiString & P,
const AnsiString &N)
{
MyFileInfo *FileInfo = NULL;
try
{
FileInfo = new MyFileInfo;
FileInfo->FilePath = P;
FileInfo->Contents->LoadFromFile(P+N);
Files->Add(FileInfo);
}
catch(...)
{
delete FileInfo;
throw;
}
StringGrid1->Rows[R] = Defaults;
StringGrid1->Cells[0][R] = N;
StringGrid1->Objects[0][R] = (TObject*) FileInfo;
}
void __fastcall TForm1::FileOpen1Accept(TObject *Sender)
{
int p;
AnsiString Path,Name;
TStringList* SLP;
if( (StringGrid1->RowCount == 2) &&
!StringGrid1->Options.Contains(goEditing) )
{
StringGrid1->Options = StringGrid1->Options << goEditing;
ToolButton2->Enabled = true;
}
else
{
StringGrid1->RowCount = StringGrid1->RowCount+1;
}
for(int i = 0; i < FileOpen1->Dialog->Files->Count; ++i)
{
if( i > 0 )
StringGrid1->RowCount = StringGrid1->RowCount + 1;
ExtractPath(FileOpen1->Dialog->Files->Strings[i], Path, Name);
AssignProperties(StringGrid1->RowCount-1, Path, Name);
}
}
void __fastcall TForm1::DeleteObjects(int R)
{
//If more Objects are assigned per row we need only change this
routine
// delete StringGrid1->Objects[0][R];
MyFileInfo *FileInfo = (MyFileInfo*) StringGrid1->Objects[0][R];
StringGrid1->Objects[0][R] = NULL;
Files->Remove(FileInfo);
delete FileInfo;
}
void __fastcall TForm1::FileDropExecute(TObject *Sender)
{
// First, delete the Object properties of the row to be removed
DeleteObjects(StringGrid1->Row);
// Then copy row contents from rows below (unless RowCount==2)
for(int row = StringGrid1->Row+1; row < StringGrid1->Row; ++row)
{
for(int col = StringGrid1->ColCount-1; col >= 0; --col)
StringGrid1->Cells[col][row-1] =
StringGrid1->Cells[col][row];
StringGrid1->Objects[0][row-1] = StringGrid1->Objects[0][row];
}
// If RowCount==2, create a blank non-editable row
// Else, decrease number of rows, and move cursor up one row if
necessary
if( StringGrid1->RowCount == 2 )
{
for(int i = 0; i < StringGrid1->ColCount; ++i)
StringGrid1->Cells[i][1] = "";
ToolButton2->Enabled = false;
StringGrid1->Options = StringGrid1->Options >> goEditing;
}
else
{
if( StringGrid1->Row == StringGrid1->RowCount-1)
StringGrid1->Row = StringGrid1->Row-1;
StringGrid1->RowCount = StringGrid1->RowCount - 1;
}
}
void __fastcall TForm1::StringGrid1GetEditText(TObject *Sender, int
ACol, int ARow, AnsiString &Value)
{
if( (ARow > 0) && StringGrid1->Options.Contains(goEditing) )
{
if( ACol > 0 )
StatusBar1->SimpleText = "Default value: " +
Defaults->Strings[ACol];
else
{
MyFileInfo *FileInfo = (MyFileInfo*)
StringGrid1->Objects[0][ARow];
StatusBar1->SimpleText = "File path: " + FileInfo->FilePath;
}
}
}
void __fastcall TForm1::StringGrid1SetEditText(TObject *Sender, int
ACol, int ARow, const AnsiString Value)
{
if( (ARow > 0) && !StringGrid1->Options.Contains(goEditing) &&
(Value != "") )
StringGrid1->Cells[ACol][ARow] = "";
}
void __fastcall TForm1::StringGrid1DblClick(TObject *Sender)
{
TPoint P = StringGrid1->ScreenToClient(Mouse->CursorPos);
int C, R;
StringGrid1->MouseToCell(P.x, P.y, C, R);
if( (R == 0) && (C > 1) )
Defaults->Strings[C] = InputBox("Change default value", "New
default value (" + StringGrid1->Cells[C][0] + ")", Defaults->Strings[C]);
}
Gambit