tags:

views:

506

answers:

2

I'm having trouble to change size of fielddefs in a TClientDataSet after i use LoadFromFile. Does anybody have a good way to change the size of a given ClientDataSet but still keep all the other things.

+3  A: 

The way the TDataSet is implemented, there are virtual methods to allocate and free the record buffer and also to get and set values from that buffer. Most dataset descendants keep a block of contiguous memory for the buffer, so if your three fields are four, six and twenty bytes in length, field 1 will start at position 0, field 2 at position four and field 3 at position ten.

If you want to change a field size while the dataset is open, all those buffers must be resized and adjusted or you'll run into trouble. There is no virtual method to resize the buffer, so I'd say it is probably not doable.

The way to go about this, is to copy your content from the original CDS to a new one with different field sizes. Assign it value by value, or use a TDataSetProvider. If you use the provider (best approach) and you don't know the exact definition, you'll need to still iterate through the source dataset fields and add them to the destination with the correct size.

Cobus Kruger
+1  A: 

You can create a new dataset, using the old dataset's FieldDefs The key point is TFieldDefs.Assign. Here's a small example:

// insert test data
procedure TForm1.InsertRecord(DataSet: TDataSet; ID: Integer; const Name: string);
begin
  DataSet.Insert;
  try
    DataSet.Fields[0].AsInteger := ID;
    DataSet.Fields[1].AsString := Name;
    DataSet.Post;
  except
    DataSet.Cancel;
    raise;
  end;
end;

// create the original dataset
procedure TForm1.Button1Click(Sender: TObject);
var
  DataSet: TClientDataSet;
begin
  DataSet := TClientDataSet.Create(nil);
  try
    DataSet.FieldDefs.Add('ID', ftInteger);
    DataSet.FieldDefs.Add('NAME', ftString, 20);
    DataSet.CreateDataSet;
    DataSet.LogChanges := False;
    InsertRecord(DataSet, 1, 'Hello');
    InsertRecord(DataSet, 2, 'World!');
    DataSet.SaveToFile(ExtractFilePath(Application.ExeName) + 'old.xml', dfXML);
  finally
    DataSet.Free;
  end;
end;

// create the new dataset
procedure TForm1.Button2Click(Sender: TObject);
var
  OldDataSet, NewDataSet: TClientDataSet;
begin
  OldDataSet := nil;
  NewDataSet := nil;
  try
    OldDataSet := TClientDataSet.Create(nil);
    OldDataSet.LoadFromFile(ExtractFilePath(Application.ExeName) + 'old.xml');

    NewDataSet := TClientDataSet.Create(nil);
    NewDataSet.FieldDefs.Assign(OldDataSet.FieldDefs);
    NewDataSet.FieldDefs[1].Size := 30;
    NewDataSet.CreateDataSet;
    NewDataSet.LogChanges := False;

    OldDataSet.First;
    while not OldDataSet.EOF do
    begin
      InsertRecord(NewDataSet, OldDataSet.Fields[0].AsInteger, OldDataSet.Fields[1].AsString);

      OldDataSet.Next;
    end;

    NewDataSet.SaveToFile(ExtractFilePath(Application.ExeName) + 'new.xml', dfXML);
  finally
    OldDataSet.Free;
    NewDataSet.Free;
  end;
end;
TOndrej
Thank you TOndrej. A little earlier I solved it in a similar way. A 'pre' CDS will open an xml file. Then a procedure will copy the fielddefs(assign method) from the pre CDS->destination CDS. Then the fielddefs are checked for fieldnames which might have the wrong size when found this is corrected. After that the values of the pre CDS are copied to the the destination CDS. In this copy process the events are set to nil and back again at the end. The same with the readonly fields. The destination CDS saves to the XML file.Checking this file this procedure seems to work nicely.
xsintill