MFC Grid manual

Aggregation of relations between C++ objects

//C++ objects can have various relations, they can have pointers to other objects etc.
//It is similar to stocks quoted on various markets or an author writing several books.
//Moreover, they can implement Dapfor::Common::CDataObject interface and
//be reused. Classes derived from the Dapfor::Common::CDelegate 
//have the same behavior as classic data objects. However, they also
//have fields provided by their aggregate. 
//In other words, this kind of objects is visible externally as it implements 
//mapping of many classes.
//If we have a market instrument and this instrument
//is inserted into the grid, the grid can show the state of the instrument 
//and the market at the same time in adjacent columns.



//File Market.h

// This class represents a market with its own state that can be either connected or disconnected. 
// State is expressed by enumeration CMarket::MarketState. The class has two functions 
// that return the market name and its state. In actual application market state can be changed.
class CMarket : public Dapfor::Common::CDataObject
{
public:
    //It is better to use enumerations instead of long numeric values...
    //The grid can use the same identifiers to display values returned by functions of this class.
    enum
    {
        FidMarketName = 100,
        FidMarketState,
    };

    enum MarketState
    {
        Connected,
        Disconnected,
    };


public:
    CMarket(const std::string& name, MarketState state);
    virtual ~CMarket();

    //Get- methods
    std::string GetMarketName() const;
    long        GetMarketState() const;

private:
    MarketState  m_MarketState;
    std::string  m_Name;

    //Declaration of map that contains the list of functions that can be called by their identifiers.
    DF_DECLARE_FIELD_MAP();
};




//File Market.cpp

// This declaration is useful to display numeric value (enum) as text string in the grid
Dapfor::Common::CLongEnumFormat::Item states[] =
{
    {CMarket::Connected,    "Connected"},
    {CMarket::Disconnected, "Disconnected"},
};

//FieldMap declaration
DF_BEGIN_FIELD_MAP(CMarket)
    DF_STL_STRING_ID(FidMarketName,  "Market",  &CMarket::GetMarketName,  0, 0)
    DF_LONG_ID      (FidMarketState, "State",   &CMarket::GetMarketState, 0, DF_ENUM_FORMAT(states))
DF_END_FIELD_MAP()

CMarket::CMarket(const std::string& name, MarketState state) : m_Name(name),
                                                               m_MarketState(state)
{
}

CMarket::~CMarket()
{
    // Don't forget to send a notification. 
    // Base class destructor does the same, but when it is called a table of virtual functions 
    // for this class is no longer valid. If another thread accesses this object    // during its destruction, we can catch an exception: pure virtual function call. 
    // For programming safety we recommend calling NotifyDelete() method in each destructor.
    NotifyDelete();
}


std::string CMarket::GetMarketName() const
{
    return m_Name;
}

long CMarket::GetMarketState() const
{
    return m_MarketState;
}



//File Instrument.h

// An instrument presented on the market. An object of this class can be inserted into the grid. 
// The market is used as an aggregated object. It means that the grid can get name and state of the market through 
// an instrument object using the field identifiers CMarket::FidMarketName and CMarket::FidMarketState. 
// If the market state is changed, the grid receives a notification via an instrument object and can update and highlight a text,
// automatically sort or filter corresponded lines. 
// Note, that CMarket and CInstrument classes use different numeric identifiers 
// (The former starts from 100 and the latter has id = 200).
class CInstrument : public Dapfor::Common::CDelegate
{
public:
    //It is better to use enumerations instead of long numeric values...
    //The grid can use the same identifiers to display values returned by functions of this class.
    enum
    {
        FidInstrumentName = 200,
    };

public:
    CInstrument(const std::string& name, CMarket* market);
    virtual ~CInstrument();

    //Get- methods
    std::string GetInstrumentName() const;

private:
    std::string  m_Name;

    //Declaration of the map that contains a list of functions that can be called by their identifiers.
    DF_DECLARE_FIELD_MAP();
};



//File Instrument.cpp

DF_BEGIN_FIELD_MAP(CInstrument)
    DF_STL_STRING_ID(FidInstrumentName,  "Instrument",  &CInstrument::GetInstrumentName,  0, 0)
DF_END_FIELD_MAP()

CInstrument::CInstrument(const std::string& name, CMarket* market) 
    : Dapfor::Common::CDelegate(market, false)
    , m_Name(name)
{
}

CInstrument::~CInstrument()
{
    // For programming safety we advise to call NotifyDelete() method in each destructor.
    NotifyDelete();
}


std::string CInstrument::GetInstrumentName() const
{
    return m_Name;
}


//Using sample

    //Grid initialization
    ...

    Dapfor::GUI::CHeader* header = new Dapfor::GUI::CHeader();
    header->Add(new Dapfor::GUI::CColumn(CMarket::FidMarketName,         "Market",     100));
    header->Add(new Dapfor::GUI::CColumn(CMarket::FidMarketState,        "State",      100));
    header->Add(new Dapfor::GUI::CColumn(CInstrument::FidInstrumentName, "Instrument", 100));
    m_Grid.SetHeader(header);
        
    ...

    //Markets & instruments initialization
    ...

    CMarket* market = new CMarket("Bloomberg", CMarket::Connected);
    CInstrument* instrument = new CInstrument("Bond", market);

    //Add an instrument to the grid.
    
    m_Grid.Add(instrument);


    // The grid displays three columns with market name, connection state 
    // and instrument name. 
    //
    // That is all. No more code is required for sorting, highlighting, updating, filtering 
    // and other routines for a modified object. The grid does everything automatically.