Objective Views : Chapter 10 Symbols : Creating a Symbol Class
Creating a Symbol Class
Deriving new classes from the CODSymbolComponent class is an easy way to customize the behavior of the symbols on your canvas. Adding a symbol class enables you to:
Add new properties and methods that are specific to a certain class of symbols.
Base the appearance on certain inputs or data.
Respond to events that occur on the symbol.
Provide rules for connecting symbols.
Programmatically add ports or labels.
Derived symbol classes must support MFC serialization to ensure that they function properly. To achieve this, you need to use the VIEWS_DECLARE_SERIAL macro in your declaration and the IMPLEMENT_SERIAL macro in your implementation. If you add new persistent data members to your class, override the Serialize() method and add code to save and restore those data members. If you use custom properties to add new data to your symbols, you do not need to add code to the Serialize() function for those properties.
When creating a derived symbol class, consider adding a Create() method to your class. If a symbol is always loaded from the same file or application resource, then your Create() method can encapsulate that particular detail. The symbol can also be created programmatically by creating and adding components to the symbol. The Create() method is also a good place to insert that functionality and to add custom properties to the symbol.
There are many virtual functions in CODSymbolComponent that can be overridden by derived classes. Consult the Objective Views Class Reference for a complete list of virtual functions in CODSymbolComponent.
If your symbol class has custom properties or member variables that extend the serialization format, you need to take special care if you are loading symbols created by the Symbol Designer. The Symbol Designer is not aware of your extended serialization format, so when you load a symbol from a file or application resource you may hit the end of file before you are finished with your serialization. As long as you initialize your member variables, this shouldn’t be a problem. You may also need to put a try/catch block in your serialization code to catch the CArchiveException, which will be thrown when you try to read past the end of file.
If you are using custom properties instead of member variables, the problem is slightly different. You need to ensure that you add the custom properties to the symbol when serializing them in—so that the Symbol Designer will not remove them. The best approach is to isolate the loading of the symbol in the Create() method and add custom properties to the symbol after serialization is complete. Note that this is not a problem when your application performs serialization, because it is linking to your symbol class and has your custom serialization routine. It only occurs when your application loads a symbol that was created by Symbol Designer.
The code in Example 14 is a symbol class that changes its line drawing color depending on whether there are connections to the symbol. Two custom properties are added to the symbol to store the colors for each connection state. The custom properties are added in the Create() method after the symbol has been loaded from the application resources. When a connection is added, the OnConnect() method is called and PROP_CONNECTED_COLOR value is applied. When a connection is removed, the OnDisconnect() method is called and the PROP_DISCONNECTED_COLOR is applied. Notice that the color must be applied to each child, because each child may have its own set of line properties that override the parent symbol line properties.
Example 14 – Symbol class creation example
#define PROP_CONNECTED_COLOR OD_CUSTOM_PROPERTY_BASE + 10
#define PROP_DISCONNECTED_COLOR OD_CUSTOM_PROPERTY_BASE + 11
 
class CConnectionSensitiveSymbol : public CODSymbolComponent
{
VIEWS_DECLARE_SERIAL(CConnectionSensitiveSymbol);
public:
BOOL Create(UINT nID)
{
BOOL bSuccess = CODSymbolComponent::Create(nID);
 
// Add two custom color properties to indicate the
// connection state of the symbol.
CODDWordProperty
propConnectedClr(PROP_CONNECTED_COLOR);
propConnectedClr.SetValue(RGB(255,0,0));
AddProperty(propConnectedClr);
 
CODDWordProperty
propDisconnectedClr(PROP_DISCONNECTED_COLOR);
propDisconnectedClr.SetValue(RGB(0,0,0));
AddProperty(propDisconnectedClr);
 
ApplyLineColor(PROP_DISCONNECTED_COLOR);
 
return bSuccess;
}
 
virtual void OnConnect(CODConnection* pConnection)
{
CODSymbolComponent::OnConnect(pConnection);
ApplyLineColor(PROP_CONNECTED_COLOR);
}
 
virtual void OnDisconnect(CODConnection* pConnection)
{
CODSymbolComponent::OnDisconnect(pConnection);
 
CODConnectionSet setConnections;
GetAllConnections(setConnections);
if (setConnections.GetSize() == 1)
{
ApplyLineColor(PROP_DISCONNECTED_COLOR);
}
}
 
void ApplyLineColor(const int nPropId)
{
// Apply the given color property to the line
// properties of each child component.
COLORREF crLineClr;
if (GetValue(nPropId, crLineClr))
{
for (int i = 0; i < GetChildCount(); i++)
{
CODComponent* pChild = GetChild(i);
pChild->SetValue(OD_PROP_LINE+OD_LINE_COLOR,
crLineClr);
}
}
}
};
 
IMPLEMENT_SERIAL(CConnectionSensitiveSymbol, CODSymbolComponent, 0)