Thiago R. Adams website

Home CodeBlog Articles Downloads Links Books About

Websites

Dependency properties

The dependency property, in the way I am using in this text, is a property that depends on someone else to be evaluated.
I came across of this necessity of dependency properties because I am implementing a HTML render including the CSS (Cascading Style Sheets) that is very suitable for this kind of implementation.
For instance:

<p style="font-size: large;"><span style="color:blue;">i'm blue and large font</span> i'm large</p>
Here, the span element is child of p. If you ask the span element for its FontSize, it will have to ask for its parents until someone returns a value, otherwise a default one will be returned.
The implementation is based on a static map that contains a pair of object pointers with a property ID pointing to some value (void*). When the objet does not set a property there is no cost in memory. However, there is some cost in CPU to check in the map if the property exist.
The implementation:




class CascadingStyle
{
private:

    class PropertyKey
    {
        const CascadingStyle* m_pCascadingStyle;
        unsigned int m_id;

    public:
        PropertyKey(const CascadingStyle* p, unsigned int id)
            : m_pCascadingStyle(p),
            m_id(id)
        {
        }

        bool operator < (const PropertyKey& other) const
        {
            if (m_pCascadingStyle == other.m_pCascadingStyle)
                return m_id < other.m_id;
            return m_pCascadingStyle < other.m_pCascadingStyle;
        }
    };
public:
    typedef std::map<PropertyKey, void*> PropertyMap;

    static PropertyMap& GetPropertyMap()
    {
        static PropertyMap map;
        return map;
    }

    static void SetProperty(CascadingStyle* p, unsigned int id, void *pv)
    {        
        std::pair<PropertyMap::iterator, bool> r = 
            GetPropertyMap().insert( std::pair<PropertyKey, void*>(PropertyKey(p, id), pv));
        ASSERT(r.second == true);
    }

    static PropertyMap::const_iterator FindProperty(const CascadingStyle* p,
                                                    unsigned int id,
                                                    bool recursive,
                                                    CascadingStyle** p2)
    {   
        if (p2 != 0)
         *p2 = 0;

        if (p == NULL)
            return GetPropertyMap().end();

        PropertyMap::const_iterator it = GetPropertyMap().find(PropertyKey(p, id));
        if (it == GetPropertyMap().end())
        {
            if (recursive)
                return FindProperty(p->GetCascadingStyle(), id, true, p2);
            else
                return GetPropertyMap().end();
        }
        
        if (p2 != 0)
         *p2 = const_cast<CascadingStyle*>(p);

        ASSERT(it != GetPropertyMap().end());
        ASSERT(it->second != NULL);
        return it;
    }

protected:

    PropertyMap::const_iterator FindProperty(unsigned int id, bool recursive = true, CascadingStyle** p2 = 0) const
    {
        return FindProperty(this, id, recursive, p2);
    }

    template<class T>
    const T& FindProperty(unsigned int id, const T& defaultValue, bool recursive = true, CascadingStyle** p2 = 0) const
    {
        PropertyMap::const_iterator it = FindProperty(this, id, recursive, p2);        
        if (it == GetPropertyMap().end())
            return defaultValue;

        ASSERT(it->second != NULL);
        return *((T*)it->second);
    }


    template<class T>
    void ClearProperty(unsigned int id)
    {
        PropertyMap::iterator it = GetPropertyMap().find(PropertyKey(this, id));
        if (it != GetPropertyMap().end())
        {
            //remove o valor anterior e deleta
            delete (T*)(it->second);
            GetPropertyMap().erase(it);
        }
    }


    template<class T>
    void SetProperty(unsigned int id, const T& v)
    {
        ClearProperty<T>(id);
        SetProperty(this, id, new T(v));
    }

public:
    virtual const CascadingStyle* GetCascadingStyle() const
    {
        return NULL;
    }

    virtual ~CascadingStyle() {}
};

Sample of use of this base class


//IDs
enum CSS
{
    CSS_Background,
 ...
    CSS_Color,
 ...
}

class Style: public CascadingStyle
{
    Style* m_pParent;

    virtual const CascadingStyle* GetCascadingStyle() const
    {
        return m_pParent;
    }

public:

    Style(Style* pParent) : m_pParent(pParent)
    {
    }

    virtual ~Style()
    {
        Style::Clear();
    }

public:


    void Clear()
    {
        ClearProperty<COLORREF>(CSS_Color);
        ClearProperty<COLORREF>(CSS_BackgroundColor);
    }

    void SetColor(COLORREF cr)
    {
        return SetProperty(CSS_Color, cr); 
    }

    COLORREF GetColor() const
    {
        return FindProperty(CSS_Color, COLORREF(RGB(0,0,0))); 
    }

    void SetBackgroundColor(COLORREF cr)
    {
        return SetProperty(CSS_BackgroundColor, cr);
    }

    COLORREF GetBackgroundColor() const
    {
        return FindProperty(CSS_BackgroundColor, COLORREF(RGB(255,255,255)));
    }
...
}

Want to see more? Go to the CodeBlog section.

About the author: I am Thiago Adams. I work as a professional C++ software engineer. I have created this website to share ideas and source code with other people with similar interests.
I would like to hear from you comments, critics, questions and suggestions about this topic or any other part of this website. Email: thiago.adams at gmail dot com