Search code examples
c++builderfastreport

FastReport (RAD Studio XE7 C++ Builder) - How do you create a sum formula?


I am using Embarcadero RAD Studio XE7, C++ Builder with FastReport 5. I create a very simple FastReport that adds up some values in a table and prints out a total. I cannot get the syntax for the sum formula correct, and as a result it keeps throwing an access violation error.

To reproduce this problem in RAD Studio XE7:

  1. Open RAD Studio XE7 and go to File -> New -> VCL Forms Application - C++Builder
  2. Drag a TClientDataSet component named ClientDataSet1 onto the form. Drag a TfrxReport component named frxReport1 onto the form. Drag a TfrxDBDataset named frxDBDataset1 onto the form.
  3. Drag a TButton named Button1 onto the form, and double-click it to create an OnClick event handler.
  4. Add the following lines to the Button1Click event handler:

    // Create a simple dataset.
    
    ClientDataSet1->FieldDefs->Clear();
    ClientDataSet1->FieldDefs->Add("ID", ftInteger, 0, false);
    ClientDataSet1->FieldDefs->Add("Status", ftString, 10, false);
    ClientDataSet1->FieldDefs->Add("Created", ftDate, 0, false);
    ClientDataSet1->FieldDefs->Add("Volume", ftInteger, 0, false);
    
    try
    {
      ClientDataSet1->CreateDataSet();
    }
    catch(Exception& e)
    {
      ShowMessage("ERROR: '" + e.Message + "'");
      return;
    }
    
    ClientDataSet1->Open();
    for (int i = 0; i < 10; ++i)
    {
      ClientDataSet1->Append();
      ClientDataSet1->FieldByName("ID")->AsInteger = i;
      ClientDataSet1->FieldByName("Status")->AsString = "Code" + String(i);
      ClientDataSet1->FieldByName("Created")->AsDateTime = Now();
      ClientDataSet1->FieldByName("Volume")->AsInteger = Random(1000);
    
      try
      {
        ClientDataSet1->Post();
      }
      catch(Exception& e)
      {
        ShowMessage("ERROR: '" + e.Message + "'");
        ClientDataSet1->Close();
        return;
      }
    }
    
    // Dataset created successfully, now create Fast Report that outputs that dataset
    
    frxReport1->Clear();
    frxDBDataset1->DataSet = (TDataSet*)ClientDataSet1;
    
    frxReport1->DataSets->Add(frxDBDataset1);
    
    TfrxDataPage* DataPage = new TfrxDataPage(frxReport1);
    DataPage->CreateUniqueName();
    
    TfrxReportPage* Page = new TfrxReportPage(frxReport1);
    Page->CreateUniqueName();
    
    // set sizes of fields, paper and orientation to defaults
    Page->SetDefaults();
    Page->Orientation = poPortrait;
    
    TfrxReportTitle* HeaderBand = new TfrxReportTitle(Page);
    HeaderBand->CreateUniqueName();
    HeaderBand->Top = 0;
    HeaderBand->Height = 20;
    
    TfrxMemoView* Memo = new TfrxMemoView(HeaderBand);
    Memo->CreateUniqueName();
    Memo->Text = "Generic Report";
    Memo->SetBounds(0, 0, 200, 20);
    
    TfrxHeader* ColumnHeaderBand;
    ColumnHeaderBand = new TfrxHeader(Page);
    ColumnHeaderBand->CreateUniqueName();
    ColumnHeaderBand->Top = HeaderBand->Top + HeaderBand->Height;
    ColumnHeaderBand->Height = 20;
    
    TfrxMasterData* DataBand = new TfrxMasterData(Page);
    DataBand->Name = "DataBand";
    DataBand->DataSet = frxDBDataset1;
    DataBand->Top = ColumnHeaderBand->Top + ColumnHeaderBand->Height;
    DataBand->Height = 20;
    
    TfrxMemoView* mField;
    for (int i = 0; i < DataBand->DataSet->FieldsCount(); ++i)
    {
      const String fieldname = ClientDataSet1->Fields->Fields[i]->FieldName;
    
      mField = new TfrxMemoView(ColumnHeaderBand);
      mField->CreateUniqueName();
      mField->SetBounds(i * 100, 0, 100, 20);
      mField->Text = fieldname;
      mField->HAlign = haCenter;
    
      // Now do the actual data
      mField = new TfrxMemoView(DataBand);
      mField->CreateUniqueName();
      mField->DataSet = DataBand->DataSet;
      mField->DataField = fieldname;
      mField->SetBounds(i * 100, 0, 100, 20);
      mField->HAlign = haRight;
    }
    
    // Now do footer band. This will hold the total
    TfrxBand* FooterBand = new TfrxFooter(Page);
    FooterBand->CreateUniqueName();
    FooterBand->Top = DataBand->Top + DataBand->Height;
    FooterBand->Height = HeaderBand->Height;
    
    TfrxMemoView* totals = new TfrxMemoView(FooterBand);
    totals->Top = 0;
    totals->Left = 0;
    totals->Height = 20;
    totals->Align = baWidth;
    
    bool is_error = false;
    try
    {
      // ALL OF THESE LINES CAUSE THE ACCESS VIOLATION
    
      // Create a summation function that displays the volume total
      totals->Text = "Totals: [Sum(<ClientDataSet1.Volume>, MyDataBand, 1)]";
      //totals->Text = "Totals: [Sum(<ClientDataSet1.'volume'>,MyDataBand,1)]";
      //totals->Text = "Totals: [Sum(<ClientDataSet1.\"volume\">,MyDataBand,1)]";
      //totals->Text = "Totals: [Sum(<ClientDataSet1.""volume"">,MyDataBand,1)]";
      //totals->Text = "Totals: [Sum(<ClientDataSet1.''volume''>,MyDataBand,1)]";
      //totals->Text = "Totals: [Sum(<ClientDataSet1.\'volume\'>,MyDataBand,1)]";
    }
    catch(Exception& e)
    {
      ShowMessage("ERROR: '" + e.Message + "'");
      is_error = true;
    }
    
    if (!is_error)
    {
      frxReport1->ShowReport(true);
    }
    
    ClientDataSet1->Close();
    
    ShowMessage("Program complete!");
    
  5. Compile and run the program. The code in the try block will throw an access violation. Why is this happening? What is the correct syntax to create the sum formula?

UPDATE:

I modified the code by explicitly setting a name for frxDBDataset1:

    frxReport1->Clear();
    frxDBDataset1->DataSet = (TDataSet*)ClientDataSet1;
    frxDBDataset1->Name = "frxDBDataset1"; // line added
    frxReport1->DataSets->Add(frxDBDataset1);

I also changed the formula line to the following:

    totals->Text = "Totals: [Sum(<frxDBDataset1.\"Volume\">, DataBand, 1)]";

I am still getting the access violation, though.


Solution

  • Go to Project --> Options --> Packages --> Runtime Packages. Set "Link with Runtime Packages" to False.

    The default for this option is True for C++Builder, False for Delphi. This explains why the equivalent code worked in Delphi, but didn't work in C++Builder.