Home End User
New Blog Posts: Merging Reports - Part 1 and Part 2

Need to save additional information about reports

edited May 2003 in End User
Hi,
I would like to save some additional information along with the reports.
Is there an analog to "OnAfterReportSave" event, where I can save something
to the database as well? Also, how do I know report id - of the just created
report? Or the id of the report that has just been modified?

Thanks,
MB

Comments

  • edited June 2003
    In the end user project, in the Report.Template.OnSaveStart event, check the
    rbItem dataset's field value for 'ItemID' to get the current record. For a
    new report use the Report.Template.OnNew event if you want to check it in
    that event.

    Cheers,

    Jim Bennett
    Digital Metaphors


  • edited June 2003
    Hi,
    Thanks, the method for getting id when editing the report works just fine.
    But I have some problems with getting id of the report that is just being
    created.
    Let's say I create a new report. How do I know an Id of the report I just
    created?
    If I try to get ITEM_ID on Report.Template.OnNew event it just returns me id
    of the last accessed report.
    Also could you please explain me what do I need to do in case when I use
    OnLoadStart or OnSaveEnd events for saving custom information into file? The
    problem is - I don't know how to work with streams there.
    Also is there some analog to OnBeforeDelete event? Let's say I need to
    delete something from database before (or after) deleting the report. Is
    there a way?

    Thanks,
    MB


  • edited June 2003
    You should skip trying to get it in the OnNew event, but rather try to
    leverage the OnLoadEnd and OnSaveStart events. You don't need the stream,
    just let RB handle that. I was thinking that you had extra info from your
    app, in a Delphi variable or something outside of the report that you wanted
    to save to the database when the report was saved. When the report is loaded
    from database, I was thinking you were trying to initialize your variables
    external to the report. If your data is stored in another table, then in the
    Report.Template.OnSaveStart event, grab the current ItemID from the rbItem
    dataset (assuming this is a foreign key in the other table to find the data
    you want to change) and fire another query to update the value in the other
    table.

    My question is, what are your trying to save? If you need to access the
    report, then there is also a template offset area you can use to store extra
    info. I don't know if you were aware of this feature or not, so you don't
    have to use extra fields on a database table. Here is an article explaining
    this further:

    -----------------------------------------------------
    Tech Tip: Storing Custom Information with Templates
    -----------------------------------------------------

    Question:
    ---------

    How can I store descriptive information with my report template?


    Solution:
    ---------

    1. If you are storing the templates to a database, then you can use the
    Report.Template.OnSaveEnd and OnLoadEnd events to store and load the custom
    information to appropriate data fields for the record. (This is the
    technique used by the ReportExplorer.)

    2. If you are storing templates to .rtm files, then you will need to store
    the custom information in an Offset area of the template file. The
    Report.Template object enables you do this by utilizing the Template.Offset
    property and the Template.OnLoadStart and OnLoadEnd events. Below is a
    detailed example.



    Example:
    --------
    - Create a new application.
    - Add to the main form one TButton, one TppReport and one TppDesigner.
    - Set the Designer's Report property to ppReport1.
    - Add ppTypes to the uses clause.


    type
    TForm1 = class(TForm)
    Button1: TButton;
    ppReport1: TppReport;
    ppDesigner1: TppDesigner;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    private
    procedure LoadReportStreamEvent(Sender: TObject; Stream: TStream);
    procedure SaveReportStreamEvent(Sender: TObject; Stream: TStream);

    public

    end; { class TForm1 }


    {note: use compiler directive to declare strings as 'ShortStrings'
    this is required for record structures}

    {$H-}

    {record structure used to store information inside report template }
    TmyTemplateInfo = record
    HeaderID: Integer;
    Comments: String;
    DateTime: TDateTime;

    end; {record, TppTemplateRec}

    {$H+}


    const
    cHeaderID = 12345;

    implementation


    {-------------------------------------------------}
    { TForm1.FormCreate }

    procedure TForm1.FormCreate(Sender: TObject);
    begin

    {setup Template events }
    ppReport1.Template.Format := ftASCII;
    ppReport1.Template.Offset := SizeOf(TmyTemplateInfo);
    ppReport1.Template.OnLoadStart := LoadReportStreamEvent;
    ppReport1.Template.OnSaveEnd := SaveReportStreamEvent;

    end; {procedure, FormCreate}

    {-------------------------------------------------}
    { TForm1.Button1Click }

    procedure TForm1.Button1Click(Sender: TObject);
    begin

    ppDesigner1.Show;

    end; {procedure, Button1Click}

    {--------------------------------------------------}
    { TForm1.LoadReportStreamEvent }

    procedure TForm1.LoadReportStreamEvent(Sender: TObject; Stream: TStream);
    var
    lTemplateInfo: TmyTemplateInfo;
    lsMessage: String;

    begin

    {read string stored in template header }
    Stream.Seek(0, soFromBeginning);
    Stream.Read(lTemplateInfo, SizeOf(TmyTemplateInfo));

    {note: if not one of the templates with our header info, then set Offset
    to 0 }
    if lTemplateInfo.HeaderID = cHeaderID then
    begin
    lsMessage := 'Reading data from template file: ' + #13#10 + #13#10 +
    lTemplateInfo.Comments + ' (' +
    DateTimeToStr(lTemplateInfo.DateTime) + ')';

    MessageDlg(lsMessage, mtInformation, [mbOK],0);
    ppReport1.Template.Offset := SizeOf(TmyTemplateInfo);
    end
    else
    ppReport1.Template.Offset := 0;

    end; {procedure, LoadReportStreamEvent}


    {---------------------------------------------------------------------------
    ---}
    { TForm1.SaveReportStreamEvent }

    procedure TForm1.SaveReportStreamEvent(Sender: TObject; Stream: TStream);
    var
    lTemplateInfo: TmyTemplateInfo;
    lsMessage: String;

    begin

    lTemplateInfo.HeaderID := cHeaderID;
    lTemplateInfo.Comments := 'This comment is being stored inside the report
    template, ' + #13#10 +
    'along with the date and time written. ';
    lTemplateInfo.DateTime := Now;

    {write info to template header}
    Stream.Seek(0, soFromBeginning);
    Stream.Write(lTemplateInfo, SizeOf(TmyTemplateInfo));

    lsMessage := 'Writing data to template file: ' + #13#10 + #13#10 +
    lTemplateInfo.Comments + ' (' +
    DateTimeToStr(lTemplateInfo.DateTime) + ')';
    MessageDlg(lsMessage, mtInformation, [mbOK],0);

    end; {procedure, SaveReportStreamEvent}








    Cheers,

    Jim Bennett
    Digital Metaphors


  • edited June 2003
    Hi,
    Thank you for the explanation about streams and saving to the report
    template.
    I was aware of the template offset area but didn't quite know how to use it
    (now I know though). Unfortunately I need to use both database AND template
    offset area for my reoports.
    About saving additional information along with the report: When(After) the
    user creates a new report I must obtain Id of the report that has just been
    created and fill several other tables using this id (like assigning default
    rights and parameters for this report) as a reference. I can easily get this
    Id if the report is being modified, but if it is created anew then I am at a
    loss what to do.
    Also when the report is about to be deleted I need to be able to either not
    allow deleting of the report or delete some data from another tables using
    Id of the report to be deleted. The problem is that I don't know where do I
    have to write a handler for this(template doesn't have OnBeforeDelete event)

    Could you help me with those issues, please.

  • edited June 2003
    I think you'll need to create a custom report explorer form to be able to do
    extra operations based on user actions, such as detecting when deletion of a
    report is occurring.

    When a new report is created, a new record isn't posted. Only when the user
    tries to save a report is a new record posted to the database. Use the
    Report.Template.OnSaveStart event to be notified when a record is being
    saved. To determine that it is new, use the OnNew event of the template or
    the TppDesigner.OnCreateReport event. This way you'll know when it was
    created new or if a Report.Template.OnLoadStart was called telling you that
    the current report was loaded and already exists on the database.


    Cheers,

    Jim Bennett
    Digital Metaphors


  • edited June 2003
    Forgot to mention, there is an example of a custom report explorer form in
    the Tutorials section of the RB installation.


    Cheers,

    Jim Bennett
    Digital Metaphors


  • edited June 2003
    Well... I already use Custom ReportExplorer form, but I was hoping that
    there is some other - easier way. Hmm.., it really seems to be the only way
    to do it. Ok, thank you for the explanation, I will have to do it.


This discussion has been closed.