OCI object references in forms

greenspun.com : LUSENET : OCI Best Practices : One Thread

TOCIForm forms have a reference to their "primary" object, and may have references to "secondary" TOCIObject objects as well. The primary object is the object which is edited by the form. Secondary objects are objects which are the source of some data on the form, but are not being edited by the form.

For example, TMiscellaneousForm displays and edits data for a TMiscellaneousObject object. The TMiscellaneousObject is the primary object.

TMiscellaneousForm also displays the names of an employee associated with the miscellaneous file*. This is a secondary object.

These object references are implemented similarly in all forms. For each object reference you will:

  1. Create a private instance variable used to store the reference to the primary object
  2. Create a setter procedure used to assign a value to the private instance variable
  3. Create a property which reads the private instance variable and writes to the setter

The setters for primary and secondary objects are very similar. However, primary object setters must use certain procedure names because those procedures are called by the form ancestor when the user clicks on the "Locked" check box.

A reference to the "primary" object -- this is the object edited by the form

procedure TMiscellaneousIndividualForm.RefreshUI;
begin
  inherited;
  RefreshDOB;
  RefreshSSN;
  RefreshEmployeeID;
  RefreshNameHistoryCollection;
  end;

procedure TMiscellaneousIndividualForm.RemoveListeners;
begin
  inherited;
  MiscellaneousIndividualObject.DOB.Broadcaster.RemoveAll(Self);
  MiscellaneousIndividualObject.SSN.Broadcaster.RemoveAll(Self);
  MiscellaneousIndividualObject.EmployeeID.Broadcaster.RemoveAll(Self);
  MiscellaneousIndividualObject.NameHistoryCollection.Broadcaster.RemoveAll(Self);
  end;

procedure TMiscellaneousIndividualForm.AddListeners;
begin
  inherited;
  MiscellaneousIndividualObject.DOB.Broadcaster.AddListener(DOBListener);
  MiscellaneousIndividualObject.SSN.Broadcaster.AddListener(SSNListener);
  MiscellaneousIndividualObject.EmployeeID.Broadcaster.AddListener(EmployeeIDListener);
  MiscellaneousIndividualObject.NameHistoryCollection.Broadcaster.AddListener(NameHistoryCollectionListener);
end;

procedure TMiscellaneousIndividualForm.SetMiscellaneousIndividualObject(aMiscellaneousIndividualObject: TMiscellaneousIndividualObject);
begin
  if (aMiscellaneousIndividualObject <> MiscellaneousIndividualObject)
    then begin
      if (MiscellaneousIndividualObject <> NIL)
        then begin
          // Remove listeners to the object currently being referenced, and
          // tell the object we're not referencing it anymore.
          RemoveListeners;
          MiscellaneousIndividualObject.FreeReference(Self);  
          end; // then begin
      // Change the reference to the new object
      FMiscellaneousIndividualObject := aMiscellaneousIndividualObject;
      if (MiscellaneousIndividualObject <> NIL)
        then begin
          // Add listeners to the new object being referenced
          AddListeners;
          end; // then begin
      // Refresh the user interface to reflect the new reference
      RefreshLocked; // This runs RefreshUI
      end; // then begin
end;

In a nutshell, the setter:

This code works when the form is first created because InitializeForm assigns a reference to the object (passed in ID) into the OCI object property. When the form is closed FinalizeForm assigns a NIL to the property, which cleans things up.

procedure TMiscellaneousIndividualForm.InitializeForm;
begin
  inherited;
  MiscellaneousIndividualObject := (TMiscellaneousIndividualObject.CreateReference(Self, ID) as TMiscellaneousIndividualObject);
end;

procedure TMiscellaneousIndividualForm.FinalizeForm;
begin
  MiscellaneousIndividualObject := NIL; // Reference created in InitializeForm
  EmployeeObject := NIL; // Reference created in RefreshEmployeeID
  inherited;
end;

A reference to a "secondary" object -- data from the object is shown on the form, but not edited by the form

These setters are structured like those for the primary object. However, insteam of running overridden methods named SetListeners, RemoveListeners, and RefreshUI, you put the related code directly in the setter. In this example, the form displays an employee object's FullName property. Therefore, when the reference to the employee changes the form must remove listeners to the old employee object, add listeners to FullName on the new object, and refresh the user interface.

procedure TMiscellaneousIndividualForm.SetEmployeeObject(anEmployeeObject: TEmployeeObject);
begin
  if (anEmployeeObject <> EmployeeObject)
    then begin
      if (EmployeeObject <> NIL)
        then begin
          // Remove listeners to the object currently being referenced, and
          // tell the object we're not referencing it anymore.
          EmployeeObject.FullName.Broadcaster.RemoveAll(Self);
          EmployeeObject.FreeReference(Self);  
          end; // then begin
      // Change the reference to the new object
      FEmployeeObject := anEmployeeObject;
      if (EmployeeObject <> NIL)
        then begin
          // Add listeners to the new object being referenced
          EmployeeObject.FullName.Broadcaster.AddListener(EmployeeObjectFullNameListener);
          end; // then begin
      // Refresh the user interface to reflect the new reference
      RefreshEmployeeObjectFullName;
      end; // then begin
end;

procedure TMiscellaneousIndividualForm.RefreshEmployeeObjectFullName;
begin
  if (EmployeeObject = NIL)
    then begin
      EditExaminerLink.Text := '(Unassigned)';
      SetAsDeadLink(EditExaminerLink); // Defined in UnitCommonRoutines
      end // then begin
    else begin
      if (ExaminerObject.FullName.Value = '')
        then EditExaminerLink.Text := '(Blank name)'
        else EditExaminerLink.Text := ExaminerObject.FullName.Value;
      SetAsActiveLink(EditExaminerLink); // Defined in UnitCommonRoutines
      end; // else begin

In this case the object reference is created when the EmployeeID listener is run.

procedure TMiscellaneousIndividualForm.RefreshEmployeeID;
begin
  if (MiscellaneousObject.EmployeeID = 0)
    then EmployeeObject := NIL
    else EmployeeObject := (TEmployeeObject.CreateReference(Self,ID) as TEmployeeObject);
end;


* TMiscellaneousIndividualForm doesn't really have an employee reference -- I'm just saying it does to provide an example.

-- Anonymous, July 14, 1998


Moderation questions? read the FAQ