MFC Grid manual

What is a data object?

The grid can display objects of any class. Class objects correspond to grid lines, and their functions correspond to columns. Values returned by these functions are displayed through formats in grid cells. This gives enormous advantages as compared to grids that do binding to object containers or present data as arrays of strings. How does it work? Let's take a simple class:

class CMyClass
{
public:
    CMyClass(double price, decimal quantity);
    ~CMyClass();

    double  GetPrice() const;
    void    SetPrice(double price);

    long    GetQuantity() const;
    void    SetQuantity(long);

private:
    double  m_Price;
    long    m_Quantity;
};

This class has some public methods and private fields. Now, let's explain how values returned by GetPrice() function can be shown in the grid. When you create a new object, memory is allocated only to the values declared in this object (if there are no virtual functions). There is also memory allocation for methods, but the compiler turns it into a code segment. Each method can have its pointer. The declaration of the method pointer looks like

typedef double (CMyClass::*pfnGetDouble)() const;
A non-static method can be called only together with the object. Without the object this doesn't make sense. The method is called and data are retrieved as follows:
double price = (object->*pfnGetDouble)();
This is a fundamental principle of the Common library.

Now, when one can call methods, let's add the notion of formats. The values returned by functions can't be presented directly in the grid. The values can have integer, float or other type. The grid can display only strings. Indeed, the integer value 123456 can be presented in different way as '123456' or '0x1E240' etc. So the form of presentation depends on the format that can convert the value into string. The Common library offers a number of formats to convert value into string and vice-versa. Each format may be bi-directional. When a user types a string in the cell, the typed string can be parsed in the format and transformed into the value. Then this value can be passed to SetPrice() function of the object.

The last step is to explain how a function can be associated with grid columns and what the Field of data object is. Data object is your own object of an arbitrary class. This class may have various Get and Set functions that manipulate internal data of the object. Get and Set functions can be grouped in the field. So the field is a pair of Get and Set functions with a format to transform the value into a string and vice-versa. The field has its own type (long, double etc). To identify the field numeric identifiers are used. Why? To avoid binding by strings that can lead to further errors when an identifier is changed in one place and is left unchanged in another one. Another reason is that numeric identifiers may be expressed by enumerations, which is good practice. So you can detect the errors at compilation stage.

Set function of the field permits to transform string or data obtained from edit-in-place controls (edit box, dropdown list etc.) to a value and pass it to a business object by the described mechanism.

Now, an important issue is multithreading protection. Indeed, MFC controls can work only in a single thread. They are not thread-safe. Any call from any non-GUI thread can lead to crash of the application. So, once you implemented notifications, the grid can get them from any thread. There are many aspects of this problem:

Well, let's explain how it works. All Grid functions are thread-safe. They use two main algorithms: with or without blocking the calling thread. We don't mean that calling these functions in any thread is the best practice but it’s the only way the grid can work properly in case of such call. All functions that add, remove or get data block the calling thread for period of operation. If you listen for some notifications from the grid, be aware that there is an implicit lock that holds the calling thread. Internally the grid doesn't call methods of business data that can lead to deadlock. So, be careful with the notification handlers.

Data are updated without locking the calling thread. There is no risk of the deadlock, but it is up to you to protect your business data. We implemented two algorithms of data updating:

Now let's explain what Dapfor::Common::CDataObject is: This is a base class for classes, the objects of which can be inserted in the grid. It implements the above binding mechanism and permits to call methods like GetPrice or SetPrice from the grid by their identifiers.

Supported types:

definition  Type  Description 
Common::StlString std::string or std::wstring STL string
Common::MfcString CStrind MFC string
Common::Char char 8-bit signed value
Common::UChar unsigned char 8-bit unsigned value
Common::Short short 16-bit signed value
Common::UShort unsigned short 16-bit unsigned value
Common::Long long 32-bit signed value
Common::ULong unsigned long 32-bit unsigned value
Common::Int64 __int64 64-bit signed value
Common::Bool bool Boolean type
Common::Float float 32 bit value with a floating point
Common::Double double 64 bit value with a floating point
Common::ObjectPtr Dapfor::Common::CDataObject* Pointer to a data object
Common::Value Dapfor::Common::CValue Combination of the types described above

Read How to install the MFC grid and compile the first application? article to get more information.