Calculated Fields in Rave

Category

Rave - General

Question

How do you create calculated Fields in Rave?

Solution

Most everyone knows that you can create calculated fields in Delphi and use those in your reports. However, those fields are also available to your visual form components such as DBGrids so it's not always a desirable option. Another problem is that you can't define calculated fields for memo or graphic field types. Here's a method to define calculated fields in Delphi that will only be available to your reports, won't impact the rest of your application and works fine for memo or graphic fields. (The example used demonstrates one solution for printing addresses where some of the lines may be blank and you don't want them printed. You could also do this using DataMirrors.)

First, define an OnGetCols event on your data connection component:

Delphi Example:

procedure TForm1.CustomerCXNGetCols(Connection: TRPCustomConnection);
begin
  With Connection do begin
    DoGetCols;
    WriteField('FullAddress', dtMemo, 30, '', '');
  end; { with }
end;

C++Builder Example

void __fastcall TForm1::CustomerCXNGetCols(TRPCustomConnection *Connection)
{
  Connection->DoGetCols();
  Connection->WriteField("FullAddress", dtMemo, 30, "", "");
}

The call to DoGetCols would create the default columns (fields) that would have been created if you hadn't defined this event. The only step left for this event is to call WriteField to define the meta-information for each field that you want to define. See Tutorial 26 in the ReportPrinter Pro manual for detailed instructions on WriteField. Next you need to define the OnGetRow event for the same data connection component.

Delphi Example:

procedure TForm1.CustomerCXNGetRow(Connection: TRPCustomConnection);

var
  Stream: TMemoryStream;
  Memo: TMemo;
  s1, s2: string;
  i1: integer;

begin
  With Connection do begin
    DoGetRow; // get data for table fields

    For i1 := 0 to 3 do begin
      case i1 of
        0: s2 := Table1.FieldByName('Company').AsString;
        1: s2 := Table1.FieldByName('Addr1').AsString;
        2: s2 := Table1.FieldByName('Addr2').AsString;
        3: begin
          s2 := Table1.FieldByName('City').AsString;
          s2 := s2 + ', ' + Table1.FieldByName('State').AsString +
               ' ' + Table1.FieldByName('Zip').AsString;
        end;
      end; // case
      if s2 <> '' then begin
        if s1 = '' then begin
          s1 := s2;
        end else begin
          s1 := s1 + #13#10 + s2;
        end;// else
      end;// if
    end;// for

    Stream := TMemoryStream.Create;
    Memo := TMemo.create(self);
    try
      Memo.Text := s1;
      Memo.Lines.SaveToStream(Stream);
      Stream.Position := 0;
      WriteBlobData(Stream.Memory^, Stream.Size);
    finally
      Stream.Free;
      Memo.Free;
    end;// tryf
  end; { with }
end;

C++Builder Example:

void __fastcall TForm1::CustomerCXNGetRow(TRPCustomConnection *Connection)
{
  TMemoryStream* Stream;
  TMemo* Memo;
  AnsiString s1, s2;

  Connection->DoGetRow(); // get data for table fields

  for (int i1=0; i1 < 4; i1++) {
    switch (i1) {
      case 0: s2 = Table1->FieldByName("Company")->AsString;
      break;
      case 1: s2 = Table1->FieldByName("Addr1")->AsString;
      break;
      case 2: s2 = Table1->FieldByName("Addr2")->AsString;
      break;
      case 3: {
        s2 = Table1->FieldByName("City")->AsString;
        s2 = s2 + ", " + Table1->FieldByName("State")->AsString +
             " " + Table1->FieldByName("Zip")->AsString;
      }
      break;
    }// switch
    if (s2 != "") {
      if (s1 == "") {
        s1 = s2;
      }// if
      else {
        s1 = s1 + "\n" + s2;
      }// else
    }// if
  }// for

  Stream = new TMemoryStream();
  Memo = new TMemo(this);
  try {
    Memo->Text = s1;
    Memo->Lines->SaveToStream(Stream);
    Stream->Position = 0;
    Connection->WriteBlobData(Stream->Memory, Stream->Size);
  } // try
  __finally {
    delete Stream;
    delete Memo;
  }// tryf

}

Once again we need to execute the default behavior for this event by calling DoGetRow. Then we get the address information and store it in a string variable. We then write the data for our calculated field by calling WriteBlobData (since it is a memo field). See Tutorial 26 in the ReportPrinter Pro manual for detailed instructions on WriteBlobData and the other WriteXxxxData methods. That's pretty much it. You can add more calculated fields to your data connection by adding more calls to WriteField and WriteXxxxData in each of these events. As with custom connections, you must have your application running and the data connection must be set to visible for these fields to show up since the code you have written must be executable.