The Streaming Mechanism

   

C++Builder is referred to as a Rapid Application Development (RAD) environment. This is partly because of the GUI interface to the developer and the object-orientated nature of the language itself. In addition, C++Builder uses a streaming (read and write) mechanism to maintain property settings.

During design time, you set various properties of components. The IDE stores these settings (described in more detail later) as part of the form on which these components belong. The forms are saved as part of the project in a file with a .dfm extension. What is stored in the form's file is loaded again at runtime. In other words, the properties are persistent.

The __published area is used to define properties that will be persisted . Such " storable " properties are written to the form file at design time save.

When a component is created, the developer gives it a set of default values for its published properties. These defaults are assigned in the constructor of the component. At runtime, the user modifies these properties via the Object Inspector. In fact, every property shown in the Object Inspector is a published property. Properties can be declared in the public section of a component's definition (the class), but these will be available only at runtime.

Not all properties that are published are required to be stored, however. Imagine two properties, one with a default value of 10 and the other defined not to be stored.

 __property int SomeProperty1 = {read=FProp1, write=FProp1, default=10};  __property AnsiString SomeProperty2 = {read=FProp2, stored=false}; 

The declaration of SomeProperty1 does not set its value to 10 . This is always done in the constructor. The default keyword is used to tell the IDE to store the value of this property in the form file only if it has a value other than 10 .

The second property, SomeProperty2 , is declared to not store its value in the form file. An example of this might be a property to indicate the current version for the component. Because the version number will not change, it does not need to be stored in the form.

When the component is saved, the property information that differs from the default is written to the form file. When a project is opened again, an instance of each component is created, the component properties are set to their defined defaults, and the stored, nondefault values are read and assigned.

Component construction and the associated property-streaming processes are something that the programmer often doesn't have to worry about.

Advanced Streaming Requirements

Component properties can be numerical, character, strings, enumerated types (including Boolean), sets, or more complex objects such as custom-defined structs and classes. C++Builder has built-in handling for the streaming of simple data types. Support for streaming custom objects, such as class properties, is provided if that class is derived from TPersistent .

TPersistent provides the capability to assign objects to other objects and enable the reading and writing of their properties to and from a stream. Additional information can be found in the index of the online help under the topic "TPersistent."

Some property types, such as arrays, require their own property editor. Without an editor, the Object Inspector is unable to provide the programmer an interface to edit the contents of the property. Refer to Chapter 5, for more information on property editors.

Streaming Unpublished Properties

So far we have learned that the Object Inspector provides the programmer a design-time interface to the published properties of a component. This is the default behavior of components, but we are not limited to this. We have also discovered that properties derived from TPersistent have the capability to stream to and from a form file. This means we have the ability to create persistent properties that do not appear in the Object Inspector. Additionally, we can create streaming methods for properties that C++Builder does not know how to read or write.

Saving an unpublished property is achieved by adding code to tell C++Builder how to read and write the property's value. This is accomplished in a two-step process.

  • Override the DefineProperties() method. The previously defined methods are passed to what is known as a filer object .

  • Create methods to read and write the property value.

We have already ascertained that published properties are automatically streamed to the form file. Using read and write methods defined for the various property types handles this. The property names and the methods used to perform the streaming are defined by the DefineProperties() method. When you want to stream a nonpublished property, you need to tell C++Builder. This is done by overriding the DefineProperties() method.

Listing 4.61 provides an example component, TSampleComp , which has three unpublished properties. The component is capable of streaming the properties by the methods provided. It creates an instance of a second component called TComp at runtime and is referenced via the property Comp3 . Because this component is not dropped onto a form by the developer, the properties for it are not automatically streamed to the form file. We will provide our component with the code it requires to stream this information as well.

Listing 4.61 A Component to Stream Unpublished Properties
 // A minimum class declaration to get the example to compile  class TComp : public TComponent  {  public:    __fastcall TComp::TComp(TComponent *Owner) : TComponent(OWner) {}  };  class TSampleComp : public TComponent  {  private:     int FProp1;     AnsiString FProp2;     TComp *FComp3;     void __fastcall ReadProp1(TReader *Reader);     void __fastcall WriteProp1(TWriter *Writer);     void __fastcall ReadProp2(TReader *Reader);     void __fastcall WriteProp2(TWriter *Writer);     void __fastcall ReadComp3(TReader *Reader);     void __fastcall WriteComp3(TWriter *Writer);  protected:     void __fastcall DefineProperties(TFiler *Filer);  public:     __fastcall TSampleComp(TComponent* Owner);     __fastcall ~TSampleComp(void);     __property int Prop1 = {read = FProp1, write = FProp1, default = 10};     __property AnsiString Prop2 = {read = FProp2, write = FProp2, nodefault};     __property TComp *Comp3 = {read = FComp3, write = FComp3};  };  void __fastcall TSampleComp::TSampleComp(TComponent* Owner) :  TComponent(Owner)  {  FProp1 = 10;              // The default  FComp3 = new TComp(NULL); // we will need to stream this  }  void __fastcall TSampleComp::~TSampleComp (void)  {  if(FComp3)     delete FComp3;  }  void __fastcall TSampleComp::DefineProperties(TFiler *Filer)  {  // Call the base method first  TComponent::DefineProperties(Filer);  Filer->DefineProperty("Prop1", ReadProp1, WriteProp1, (FProp1 != 10));  Filer->DefineProperty("Prop2", ReadProp2, WriteProp2, (FProp2 != ""));  // need to determine if the properties for Comp3 need to be written  bool WriteValue;  if(Filer->Ancestor) // check for inherited value    {    TSampleComp *FilerComp = dynamic_cast<TSampleComp *>(Filer->Ancestor);    if(FilerComp->Comp3 == NULL)      WriteValue = (Comp3 != NULL);    else      {      if((Comp3 == NULL)  (FilerComp->Comp3->Name != Comp3->Name))        WriteValue = true;      else        WriteValue = false;      }    }  else    // no inherited value, write property if not null    WriteValue = (Comp3 != NULL);  Filer->DefineProperty("Comp3", ReadComp3, WriteComp3, WriteValue);  }  void __fastcall TSampleComp::ReadProp1(TReader *Reader)  {  Prop1 = Reader->ReadInteger();  }  void __fastcall TSampleComp::WriteProp1(TWriter *Writer)  {  Writer->WriteInteger(FProp1);  }  void __fastcall TSampleComp::ReadProp2(TReader *Reader)  {  FProp2 = Reader->ReadString();  }  void __fastcall TSampleComp::WriteProp2(TWriter *Writer)  {  Writer->WriteString(FProp2);  }  void __fastcall TSampleComp::ReadComp3(TReader *Reader)  {  if(Reader->ReadBoolean())      FComp3 = (TComp *)Reader->ReadComponent(NULL);  }  void __fastcall TSampleComp::WriteComp3(TWriter *Writer)  {  if(FComp3)    {    Writer->WriteBoolean(true);    Writer->WriteComponent(Comp3);    }  else    Writer->WriteBoolean (false);  } 

The DefineProperties() method contains these two lines of code to register the first two properties:

 Filer->DefineProperty("Prop1", ReadProp1, WriteProp1, (FProp1 != 10));  Filer->DefineProperty("Prop2", ReadProp2, WriteProp2, (FProp2 != "")); 

This tells C++Builder to use the read-and-write methods provided when streaming these properties. The last parameter is a flag to indicate if we have data to store. Prop1 and Prop2 need to be stored only if their values differ from the default value.

The Comp3 property is different and will require some additional explanation. This property is different from other properties because it is a component ( instantiated by the class at runtime) rather than a data type. Listing 4.62 presents the section of code responsible for determining if this property requires streaming.

Listing 4.62 Determining If a Property That Is a Component Requires Streaming
 bool WriteValue;  if(Filer->Ancestor) // check for inherited value    {    TSampleComp *FilerComp = dynamic_cast<TSampleComp *>(Filer->Ancestor);    if(FilerComp->Comp3 == NULL)      WriteValue = (Comp3 != NULL);    else      {      if((Comp3 == NULL)  (FilerComp->Comp3->Name != Comp3->Name))        WriteValue = true;      else        WriteValue = false;      }    }  else    // no inherited value, write property if not null    WriteValue = (Comp3 != NULL);  Filer->DefineProperty("Comp3", ReadComp3, WriteComp3, WriteValue); 

This property represents a component instantiated at runtime. Because the component is not dropped onto a form, the default mechanism of streaming the properties is not performed. This implementation of the DefineProperties() method will take care of that for us.

First we need to determine if the filer's Ancestor property is true to avoid saving a property value in inherited forms. If there is no inherited value, we will stream the property (which is a component) if Comp3 is not NULL .

If the filer's Ancestor property is true , we need to look at the Comp3 property of the ancestor next . If this property is NULL , we stream our property ( TSampleComp->Comp3 ) if it is not NULL . If the filer's Ancestor Comp3 property is not NULL , we perform two final checks. If our property ( TsampleComp->Comp3 ) is NULL or the name of our Comp3 property is different than the ancestor's, we will stream the property (a component).

Finally, we define our property, using DefineProperty() as previously explained.

We have seen the uses of the DefineProperty() method, which deals with data types such as integers, strings, chars, Booleans, and enumerated types. There is another method, DefineBinaryProperty() , that is designed to be used for the streaming of binary information such as graphics and sound files. Refer to the "DefineBinaryProperty" section of "TWriter" in the online help index for additional information on this.


   
Top


C++ Builder Developers Guide
C++Builder 5 Developers Guide
ISBN: 0672319721
EAN: 2147483647
Year: 2002
Pages: 253

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net