It's common knowledge that the VCL uses properties to provide much of its functionality. Properties allow the VCL to do what appears at first to be black magic. For example, when you change the value of the Left property for a form, the form magically moves on the screen. This is because the Left property has a write method associated with it. When you assign a value to the Left property, the write method is called, and code is executed behind the scenes. In this case, the code moves the form on the screen. It's a given that properties work in C++Builder--you use them every time you write a VCL application. But did you know that you can use properties in your own C++ classes? Indeed, you can. This article will explain how.
Finally, some properties use read and/or write methods. Read and write methods are methods that are called when the property is read or written to. Let's go back to the example of a form's Left property. The Left property has a write method called SetLeft. This method is called every time the Left property is assigned a value. The SetLeft method includes code to move the form on the screen (among other things). So it's not black magic at work at all, but simply properties in action.
| Note: Direct access and read/write methods can be mixed in the same property. Many properties use direct access for reading the property value, but a write method for writing to the property. |
To summarize, if a property includes a write method, then that method will be called every time the property is written to. If the property includes a read method, then that read method will be called every time the property's value is read.
class TMyPoint {
int X;
int Y;
};
That's a pretty simple class, so let's take it a bit further. Let's say that
your point class only allows a specific range of x and y
coordinates. For the sake of argument, let's say that both coordinates must
fall in the range of 0 to 100. Using straight C++, you'd write the class like
this:
class TMyPoint {
private:
int X;
int Y;
public:
void SetX(int x) {
if (x > 100 || x < 0)
throw ("Range error");
else
X = x;
}
void SetY(int y) {
if (y > 100 || y < 0)
throw ("Range error");
else
Y = y;
}
int GetX() { return X; };
int GetY() { return Y; };
};
As you can see, the class now has private data and uses getter and setter
methods to access the data. Note that the setter methods throw an exception if
the value passed in is less than 0 or greater than 100. This all works
perfectly fine, but the code to make use of this class isn't exactly a thing of
beauty:TMyPoint point; point.SetX(50); point.SetY(100); Label1->Caption = String(point.GetX()) + ", " + String(point.GetY());Now, let's consider this class when written using properties. The new class declaration looks like this:
class TMyPoint {
private:
int FX;
int FY;
void SetX(int x) {
if (x > 100 || x < 0)
throw ("Range error");
else
FX = x;
}
void SetY(int y) {
if (y > 100 || y < 0)
throw ("Range error");
else
FY = y;
}
public:
__property int X = {read=FX,
write=SetX};
__property int Y = {read=FY,
write=SetY};
};
There are several items of note in our new TMyPoint class. First, notice that
the underlying data members are called FX and FY. The use of the letter
F preceding the data members is traditional, but you can use any naming
convention you like. The important thing to note here, though, is that the
underlying data members must use a name other than X and Y because those are
our property names.
Next, notice the SetX and SetY property write methods. These methods will be called each time the X and Y properties are assigned a value. The declaration of a write method must conform to a predetermined signature. Specifically, the write method must take a single parameter of the property's type and must return void.
Finally, notice the X and Y property declarations. Here's the X property's declaration again:
__property int X = {read=FX, write=SetX};
This property is declared of type int because the property's data type must
exactly match the underlying data member's type. Notice that we're using direct
access to read the property (read=FX), and a write method to write to the
property (write=SetX). (Note that if you omit the write statement, the property
becomes a read-only property.)
The code to implement our newly written class now looks like this:
TMyPoint point; point.X = 50; point.Y = 100; Label1->Caption = String(point.X) + ", " + String(point.Y);This code is much easier to read and is much cleaner than the previous example using a straight C++ class.