﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Evil.Ghost</title><link>http://www.cppblog.com/vivence/</link><description>c++ programmer</description><language>zh-cn</language><lastBuildDate>Tue, 07 Apr 2026 22:21:26 GMT</lastBuildDate><pubDate>Tue, 07 Apr 2026 22:21:26 GMT</pubDate><ttl>60</ttl><item><title>AVL树</title><link>http://www.cppblog.com/vivence/archive/2011/06/17/AVLTree.html</link><dc:creator>EvilGhost</dc:creator><author>EvilGhost</author><pubDate>Fri, 17 Jun 2011 12:01:00 GMT</pubDate><guid>http://www.cppblog.com/vivence/archive/2011/06/17/AVLTree.html</guid><wfw:comment>http://www.cppblog.com/vivence/comments/148880.html</wfw:comment><comments>http://www.cppblog.com/vivence/archive/2011/06/17/AVLTree.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/vivence/comments/commentRss/148880.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vivence/services/trackbacks/148880.html</trackback:ping><description><![CDATA[<p>&nbsp;</p><p>AVLTree.h文件</p>
<p>
</p><div class="cnblogs_Highlighter">
<pre class="brush:cpp;gutter:false;">#ifndef AVL_TREE_H
#define AVL_TREE_H

#include &lt;cassert&gt;
#include &lt;algorithm&gt;

#ifdef _PRINT
#include &lt;vector&gt;
#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;functional&gt;
#endif // _PRINT

namespace ghost{

/// AVL树
template&lt;typename ComparableT&gt;
class AVLTree{
public:
    typedef ComparableT DataType;

private:
    /// 节点，缓存了自身的高度
    struct Node_{
        DataType data;        // 可进行比较的数据
        Node_* pLeftChild;   // 指向左儿子
        Node_* pRightChild;  // 指向右儿子
        int height;           // 作为根节点的树高度，

        Node_()
            : pLeftChild(0)
            , pRightChild(0)
            , height(0) // 约定叶子高度为0，故节点高度初始化为0
        {

        }
        explicit Node_(const DataType&amp; d)
            : data(d)
            , pLeftChild(0)
            , pRightChild(0)
            , height(0) // 约定叶子高度为0，故节点高度初始化为0
        {

        }

        Node_(const Node_&amp;) = delete;
        Node_&amp; operator =(const Node_&amp;) = delete;
    };
    Node_* pRoot_;   // 指向根节点

public:
    /// 默认初始化为空树
    AVLTree()
        : pRoot_(0)
    {
#ifdef _PRINT
        std::cout&lt;&lt;"创建AVL树"&lt;&lt;std::endl;
#endif // _PRINT
    }
    ~AVLTree()
    {
        Clear();
    }

    AVLTree(const AVLTree&amp;) = delete;
    AVLTree&amp; operator =(const AVLTree&amp;) = delete;

public:
    /// 获取树高度，空树返回-1，只有个节点返回0
    int GetHeight() const{return GetHeight_(pRoot_);}

#ifdef _PRINT
    /// 打印者，即需要打印的对象
    class Printer{
    public:
        virtual ~Printer(){}

    public:
        virtual void Print() const{}
        virtual bool IsValid() const{return false;}
    };

    typedef std::shared_ptr&lt;Printer&gt; PSharedPrinter;         // 打印者共享指针
    typedef std::vector&lt;PSharedPrinter&gt; PrinterContainer;   // 打印者共享指针的容器

    /// 节点打印者
    class NodePrinter : public Printer{
        Node_* pNode_;
        size_t width_;
        PrinterContainer&amp; nextPrinters_;

    public:
        NodePrinter(Node_* p, PrinterContainer&amp; printers)
            : pNode_(p)
            , width_(0)
            , nextPrinters_(printers)
        {
            assert(pNode_);
            UpdateWidth();
        }
        virtual ~NodePrinter(){}
        NodePrinter(const NodePrinter&amp;) = delete;
        NodePrinter&amp; operator =(const NodePrinter&amp;) = delete;

    public:
        void UpdateWidth()
        {
            width_ = CalcDataWidth_(pNode_-&gt;data);
        }

        virtual void Print() const
        {
            // 计算左右子树宽度
            size_t leftChildWidth = CalcWidth_(pNode_-&gt;pLeftChild);
            size_t rightChildWidth = CalcWidth_(pNode_-&gt;pRightChild);  // +1是为了将数据隔开

            // 打印左边空白
            for (size_t i = 0; i &lt; leftChildWidth; ++i)
            {
                std::cout&lt;&lt;' ';
            }
            // 打印节点
            std::cout&lt;&lt;"["&lt;&lt;pNode_-&gt;data&lt;&lt;"]";
            // 打印右边空白
            for (size_t i = 0; i &lt; rightChildWidth; ++i)
            {
                std::cout&lt;&lt;' ';
            }

            // 将左儿子放入下一层需要打印的节点集合中
            if (pNode_-&gt;pLeftChild)
            {
                nextPrinters_.push_back(PSharedPrinter(new NodePrinter(pNode_-&gt;pLeftChild, nextPrinters_)));
            }
            // 将自身所占空位放入下一层需要打印的节点集合中
            nextPrinters_.push_back(PSharedPrinter(new BlankPrinter(width_)));
             // 将右儿子放入下一层需要打印的节点集合中
            if (pNode_-&gt;pRightChild)
            {
                nextPrinters_.push_back(PSharedPrinter(new NodePrinter(pNode_-&gt;pRightChild, nextPrinters_)));
            }
            // 将自身所占空位放入下一层需要打印的节点集合中
            nextPrinters_.push_back(PSharedPrinter(new BlankPrinter(width_)));
        }
        virtual bool IsValid() const{return true;}
    };

    /// 空白打印者，主要完成打印父节点所占用的空白
    class BlankPrinter : public Printer{
        size_t count_;
    public:
        explicit BlankPrinter(size_t c) : count_(c){}
        virtual ~BlankPrinter(){}
    public:
        virtual void Print() const
        {
            for (size_t i = 0; i &lt; count_; ++i)
            {
                std::cout&lt;&lt;' ';
            }
        }
    };

    /// 广度优先打印节点，目前只支持打印int型数据
    void Print() const
    {
        std::cerr&lt;&lt;"不支持打印的数据类型："&lt;&lt;typeid(DataType).name()&lt;&lt;"\n";
    }

private:
    /// 计算十进制数位数
    static size_t CalcDataWidth_(int n)
    {
        assert(false);
    }
    /**
    计算树宽度
    因为约定空树宽度为0，叶子宽度为1，所以树宽度等于左右子树宽度和+数据所占的位数
    */
    static size_t CalcWidth_(const Node_* pRoot)
    {
        if (!pRoot)
        {
            return 0;
        }
        return CalcWidth_(pRoot-&gt;pLeftChild) + CalcWidth_(pRoot-&gt;pRightChild) + CalcDataWidth_(pRoot-&gt;data);
    }
#endif // _PRINT

public:
    /// 插入数据
    void Insert(const DataType&amp; data)
    {
#ifdef _PRINT
        std::cout&lt;&lt;"插入数据："&lt;&lt;data&lt;&lt;std::endl;
#endif // _PRINT
        Insert_(data, pRoot_);
    }
    /// 删除数据
    void Erase(const DataType&amp; data)
    {
#ifdef _PRINT
        std::cout&lt;&lt;"删除数据："&lt;&lt;data&lt;&lt;std::endl;
#endif // _PRINT
        Erase_(data, pRoot_);
    }

    /// 清空
    void Clear()
    {
#ifdef _PRINT
        std::cout&lt;&lt;"清空"&lt;&lt;std::endl;
#endif // _PRINT
        // 销毁所有节点
        RecursDestroyNode_(pRoot_);
        pRoot_ = 0;
    }

private:
    /// 创建节点
    static Node_* CreateNode_(const DataType&amp; data)
    {
        return new Node_(data);
    }
    /// 销毁节点
    static void DestroyNode_(Node_* pNode)
    {
        delete pNode;
    }
    /// 递归销毁节点
    static void RecursDestroyNode_(Node_* pNode)
    {
        if (pNode)
        {
            // 先递归销毁子节点
            RecursDestroyNode_(pNode-&gt;pLeftChild);
            RecursDestroyNode_(pNode-&gt;pRightChild);
            // 再销毁自身
            DestroyNode_(pNode);
        }
    }

    /// 获取树高度，约定空树高度为-1
    static int GetHeight_(const Node_* pRoot)
    {
        return pRoot ? pRoot-&gt;height : -1;
    }
    /**
    计算树高度
    因为约定空树高度为-1，叶子高度为0，所以树高度等于左右子树较高者高度+1
    */
    static int CalcHeight_(const Node_* pRoot)
    {
        assert(pRoot);  // 断言树存在
        return std::max(GetHeight_(pRoot-&gt;pLeftChild), GetHeight_(pRoot-&gt;pRightChild)) + 1;
    }

    /**
    与子树进行单旋转
    由于旋转后节点将成为其原儿子的儿子，故节点指针pNode将会指向其原儿子
    pChild1指向被旋转的儿子成员指针，pChild2指向另一个儿子成员指针
    */
    static void SingleRatateWithChild_(Node_*&amp; pNode, Node_* Node_::* pChild1, Node_* Node_::* pChild2)
    {
        assert(pChild1 &amp;&amp; pChild2); // 断言成员变量指针有效

        assert(pNode);   // 断言节点存在

        // 节点的儿子1重定向于儿子1的儿子2
        Node_* pOriginalChild = pNode-&gt;*pChild1;
        pNode-&gt;*pChild1 = pOriginalChild-&gt;*pChild2;
        // 节点的原儿子1的儿子2重定向于节点
        pOriginalChild-&gt;*pChild2 = pNode;

        // 旋转之后需要重新计算高度
        pNode-&gt;height = CalcHeight_(pNode);
        pOriginalChild-&gt;height = CalcHeight_(pOriginalChild);

        // pNode指向其原儿子
        pNode = pOriginalChild;
    }

    /// 与左子树进行单旋转
    static void RotateWithLeftChild_(Node_*&amp; pNode)
    {
        SingleRatateWithChild_(pNode, &amp;Node_::pLeftChild, &amp;Node_::pRightChild);
    }

    /// 与右子树进行单旋转
    static void RotateWithRightChild_(Node_*&amp; pNode)
    {
        SingleRatateWithChild_(pNode, &amp;Node_::pRightChild, &amp;Node_::pLeftChild);
    }

    /**
    与子树进行双旋转
    由于旋转后节点将成为其原儿子的儿子，故节点指针pNode将会指向其原儿子
    pChild1指向被旋转的儿子成员指针，pChild2指向另一个儿子成员指针
    */
    static void DoubleRatateWithChild_(Node_*&amp; pNode, Node_* Node_::* pChild1, Node_* Node_::* pChild2)
    {
        assert(pChild1); // 断言成员变量指针有效

        // 先对儿子进行一次旋转
        SingleRatateWithChild_(pNode-&gt;*pChild1, pChild2, pChild1);
        // 再对自己进行一次旋转
        SingleRatateWithChild_(pNode, pChild1, pChild2);
    }

    /// 与左子树进行双旋转
    static void DoubleRotateWithLeftChild_(Node_*&amp; pNode)
    {
        DoubleRatateWithChild_(pNode, &amp;Node_::pLeftChild, &amp;Node_::pRightChild);
    }

    /// 与右子树进行双旋转
    static void DoubleRotateWithRightChild_(Node_*&amp; pNode)
    {
        DoubleRatateWithChild_(pNode, &amp;Node_::pRightChild, &amp;Node_::pLeftChild);
    }

    /**
    确定左子树是否过高（破坏了AVL平衡条件），是则与其进行旋转
    当在左子树中插入新节点，或者在右子树中删除节点时使用
    */
    static void RatateWithLeftChildIfNeed_(Node_*&amp; pNode)
    {
        // AVL平衡条件为左右子树高度相差不超过1
        // 左子树比右子树高2，需要通过旋转来使之重新达到AVL平衡条件
        if (2 == GetHeight_(pNode-&gt;pLeftChild) - GetHeight_(pNode-&gt;pRightChild))
        {
            if (GetHeight_(pNode-&gt;pLeftChild-&gt;pLeftChild) &gt; GetHeight_(pNode-&gt;pLeftChild-&gt;pRightChild))
            {
                // 左子树的左子树高于左子树的右子树，应当与左子树进行单旋转
                RotateWithLeftChild_(pNode);
            }
            else
            {
                // 左子树的右子树高于左子树的左子树，应当与左子树进行双旋转
                DoubleRotateWithLeftChild_(pNode);
            }
        }
    }

    /**
    确定右子树是否过高（破坏了AVL平衡条件），是则与其进行旋转
    当在右子树中插入新节点，或者在左子树中删除节点时使用
    */
    static void RatateWithRightChildIfNeed_(Node_*&amp; pNode)
    {
        // AVL平衡条件为左右子树高度相差不超过1
        // 右子树比左子树高2，需要通过旋转来使之重新达到AVL平衡条件
        if (2 == GetHeight_(pNode-&gt;pRightChild) - GetHeight_(pNode-&gt;pLeftChild))
        {
            if (GetHeight_(pNode-&gt;pRightChild-&gt;pRightChild) &gt; GetHeight_(pNode-&gt;pRightChild-&gt;pLeftChild))
            {
                // 右子树的右子树高于右子树的左子树，应当与右子树进行单旋转
                RotateWithRightChild_(pNode);
            }
            else
            {
                // 右子树的左子树高于右子树的右子树，应当与右子树进行双旋转
                DoubleRotateWithRightChild_(pNode);
            }
        }
    }

    /**
    插入新节点：
        如果当前节点为空则说明找到了插入的位置，创建新节点，返回插入成功
        如果数据小于当前节点数据则到左子树中插入，如果插入成功，可能需要旋转使之重新平衡（左子树过高），重新计算高度
        如果数据大于当前节点数据则道右子树中插入，如果插入成功，可能需要旋转使之重新平衡（右子树过高），重新计算高度
        如果数据等于当前节点数据则什么都不做，返回插入失败
    */
    static bool Insert_(const DataType&amp; data, Node_*&amp; pNode)
    {
        if (!pNode)
        {
            // 找到位置，创建节点
            pNode = CreateNode_(data);
            assert(pNode); // 断言创建节点成功
            return true;
        }
        else if (data &lt; pNode-&gt;data)
        {
            // 将较小的数据插入到左子树
            if (Insert_(data, pNode-&gt;pLeftChild))
            {
                // 成功插入新节点
                // 如果需要，则与左子树进行旋转以维持AVL平衡条件
                RatateWithLeftChildIfNeed_(pNode);

                // 重新计算高度
                pNode-&gt;height = CalcHeight_(pNode);
                return true;
            }
        }
        else if (data &gt; pNode-&gt;data)
        {
            // 将较大的数据插入到右子树
            if (Insert_(data, pNode-&gt;pRightChild))
            {
                // 成功插入新节点
                // 如果需要，则与右子树进行旋转以维持AVL平衡条件
                RatateWithRightChildIfNeed_(pNode);

                // 重新计算高度
                pNode-&gt;height = CalcHeight_(pNode);
                return true;
            }
        }
        else
        {
            // 重复数据（什么也不做，或者进行计数）
        }
        return false;
    }

    /**
    删除节点
    查找被删除的节点：
        如果当前节点为空则说明没有找到被删除的节点，返回删除失败
        如果被删除的数据小于节点数据，则在节点的左子树中查找并删除，如果删除成功，可能需要旋转使之重新平衡（右子树过高），重新计算高度
        如果被删除的数据大于节点数据，则在节点的右子树中查找并删除，如果删除成功，可能需要旋转使之重新平衡（左子树过高），重新计算高度
        如果被删除的数据等于节点数据，则找到被删除的节点，开始删除，返回删除成功

    删除节点过程，将被删除的节点作为标记节点：
        如果标记节点存在左右双子树，利用右子树的最小节点的数据替换此节点数据，然后删除右子树的最小节点：
            如果右子树有左子树，从左子树中找到最小节点，将其右子树提升一级，可能需要旋转使其父节点重新平衡（其父节点的右子树过高），重新计算其父节点高度
            如果右子树没有左子树，此时右子树则即是最小节点，将其右子树提升一级
        可能需要旋转使标记节点重新平衡（标记节点的左子树过高），重新计算标记节点高度

        如果标记节点不存在左右双子树，删除标记节点，提升其子树
    */
    static bool Erase_(const DataType&amp; data, Node_*&amp; pNode)
    {
        if (!pNode)
        {
            // 没有找到节点
            return false;
        }
        else if (data &lt; pNode-&gt;data)
        {
            // 节点较小，在左子树中删除
            if (Erase_(data, pNode-&gt;pLeftChild))
            {
                // 成功删除节点
                // 如果需要，则与右子树进行旋转以维持AVL平衡条件
                RatateWithRightChildIfNeed_(pNode);

                // 重新计算高度
                pNode-&gt;height = CalcHeight_(pNode);
                return true;
            }
        }
        else if (data &gt; pNode-&gt;data)
        {
            // 节点较大，在右子树中删除
            if (Erase_(data, pNode-&gt;pRightChild))
            {
                // 成功删除节点
                // 如果需要，则与左子树进行旋转以维持AVL平衡条件
                RatateWithLeftChildIfNeed_(pNode);

                // 重新计算高度
                pNode-&gt;height = CalcHeight_(pNode);
                return true;
            }
        }
        else
        {
            // 找到了需要被删除的节点
            if (pNode-&gt;pLeftChild &amp;&amp; pNode-&gt;pRightChild)
            {
                // 存在双子树，利用右子树最小节点替换，并删除右子树最小节点
                Node_* pMin = pNode-&gt;pRightChild;
                if (pNode-&gt;pRightChild-&gt;pLeftChild)
                {
                    // 右子树存在左子树，从右子树的左子树中找最小节点
                    Node_* pMinParent = pNode-&gt;pRightChild;
                    while (pMinParent-&gt;pLeftChild-&gt;pLeftChild)
                    {
                        pMinParent = pMinParent-&gt;pLeftChild;
                    }
                    pMin = pMinParent-&gt;pLeftChild;

                    // 提升最小节点的右子树
                    pMinParent-&gt;pLeftChild = pMin-&gt;pRightChild;

                    // 如果需要，最小节点的父节点则与其右子树进行旋转以维持AVL平衡条件
                    RatateWithRightChildIfNeed_(pMinParent);

                    // 重新计算最小节点的父节点的高度
                    pMinParent-&gt;height = CalcHeight_(pMinParent);
                }
                else
                {
                    // 右子树不存在左子树，那么提升右子树的右子树
                    pNode-&gt;pRightChild = pNode-&gt;pRightChild-&gt;pRightChild;
                }
                // 用最小节点替换
                pNode-&gt;data = pMin-&gt;data;

                // 删除最小节点
                DestroyNode_(pMin);

                // 如果需要，则与左子树进行旋转以维持AVL平衡条件
                RatateWithLeftChildIfNeed_(pNode);

                // 重新计算高度
                pNode-&gt;height = CalcHeight_(pNode);
            }
            else
            {
                // 不存在双子树，则直接用儿子替换
                Node_* pTemp = pNode;
                pNode = pNode-&gt;pLeftChild ? pNode-&gt;pLeftChild : pNode-&gt;pRightChild;
                // 销毁节点
                DestroyNode_(pTemp);
            }
            return true;
        }
        return false;
    }

}; // class AVLTree

#ifdef _PRINT
template&lt;&gt;
void AVLTree&lt;int&gt;::Print() const
{
    if (!pRoot_)
    {
        return;
    }

    PrinterContainer nextPrinters; // 下一层需要打印的对象集合
    nextPrinters.push_back(PSharedPrinter(new NodePrinter(pRoot_, nextPrinters)));

    while (nextPrinters.end() != std::find_if(nextPrinters.begin(), nextPrinters.end(), std::mem_fn(&amp;Printer::IsValid)))
    {
        auto printers(std::move(nextPrinters));  // 当前需要打印的对象集合
        // 打印一行
        std::for_each(printers.begin(), printers.end(), std::mem_fn(&amp;Printer::Print));
        // 换行
        std::cout&lt;&lt;std::endl;
    }
}

template&lt;&gt;
size_t AVLTree&lt;int&gt;::CalcDataWidth_(int n)
{
    if (0 == n)
    {
        return 1+2; // +2是为[]符号占位
    }
    size_t ret = 2; // 2是为[]符号占位
    if (0 &gt; n)
    {
        // 复数，添加符号位
        ++ret;
        n = -n;
    }
    while (n)
    {
        ++ret;
        n /= 10;
    }
    return ret;
}

#endif // _PRINT

} // namespace ghost

#endif // AVL_TREE_H
</pre>
</div>
<p>&nbsp;</p>
<p>main.cpp文件</p>
<p>
</p><div class="cnblogs_Highlighter">
<pre class="brush:cpp;gutter:false;">#define _PRINT

#include "AVLTree.h"
#include &lt;iostream&gt;
#include &lt;ctime&gt;

/// 打印AVL树
template&lt;typename T&gt;
void PrintAVLTree(const ghost::AVLTree&lt;T&gt;&amp; tree)
{
#ifdef _PRINT
    std::cout&lt;&lt;"--------------AVLTree--------------"&lt;&lt;std::endl;
    tree.Print();
    std::cout&lt;&lt;"------------------------------------------"&lt;&lt;std::endl;
#else
    std::cerr&lt;&lt;"未开启打印预处理器，不提供AVL树的打印！\n";
#endif // _PRINT
}

static const size_t TEST_DATA_COUNT = 10;          // 测试数据的个数
static const size_t TEST_DATA_LOWER_LIMIT = 0;    // 测试数据的下限
static const size_t TEST_DATA_UPPER_LIMIT = 10;  // 测试数据的上限

/// 随机构造测试数据
int BuildTestData()
{
    return TEST_DATA_LOWER_LIMIT + rand() % (TEST_DATA_UPPER_LIMIT-TEST_DATA_LOWER_LIMIT);
}

int main()
{
    srand((int)time(0));

    ghost::AVLTree&lt;int&gt; tree;

    // 随机插入测试数据
    for (size_t i = 0; i &lt; TEST_DATA_COUNT; ++i)
    {
        tree.Insert(BuildTestData());
        PrintAVLTree(tree);
    }

    // 随机删除测试数据
    for (size_t i = 0; i &lt; TEST_DATA_COUNT; ++i)
    {
        tree.Erase(BuildTestData());
        PrintAVLTree(tree);
    }

//    tree.Insert(5);
//    PrintAVLTree(tree);
//
//    tree.Insert(2);
//    PrintAVLTree(tree);
//
//    tree.Insert(8);
//    PrintAVLTree(tree);
//
//    tree.Insert(1);
//    PrintAVLTree(tree);
//
//    tree.Insert(4);
//    PrintAVLTree(tree);
//
//    tree.Insert(7);
//    PrintAVLTree(tree);
//
//    tree.Insert(3);
//    PrintAVLTree(tree);
//
//    tree.Insert(6); // 此时应触发一次单旋转
//    PrintAVLTree(tree);
    return 0;
}
</pre>
</div>
<p>&nbsp;</p><img src="http://www.cnblogs.com/EvilGhost/aggbug/2083622.html?type=0" width="1" height="1" alt="" /><p>作者: <a href="http://www.cnblogs.com/EvilGhost/" target="_blank">Evil.Ghost</a> 发表于 2011-06-17 14:53 <a href="http://www.cnblogs.com/EvilGhost/archive/2011/06/17/AVLTree.html" target="_blank">原文链接</a></p><p>评论: 0　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/06/17/AVLTree.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/06/17/AVLTree.html#commentform" target="_blank">发表评论</a></p><hr /><p>最新新闻：<br />&#183; <a href="http://news.cnblogs.com/n/105675/" target="_blank">郭台铭赞河南工人素质佳 iPhone生产落户郑州</a><span style="color:gray">(2011-06-17 17:53)</span><br />&#183; <a href="http://news.cnblogs.com/n/105674/" target="_blank">RIM首席运营官病休离职 秋季重返工作岗位</a><span style="color:gray">(2011-06-17 17:50)</span><br />&#183; <a href="http://news.cnblogs.com/n/105673/" target="_blank">土豆网下半年赴纳斯达克上市 融资1.5亿美元</a><span style="color:gray">(2011-06-17 17:48)</span><br />&#183; <a href="http://news.cnblogs.com/n/105672/" target="_blank">传McAfee总裁将跳槽至初创公司出任CEO</a><span style="color:gray">(2011-06-17 17:47)</span><br />&#183; <a href="http://news.cnblogs.com/n/105671/" target="_blank">搜人功能正式上线 搜搜社区化战略再升级</a><span style="color:gray">(2011-06-17 17:46)</span><br /></p><p>编辑推荐：<a href="http://kb.cnblogs.com/page/105605/" target="_blank">像人脑一样思考 揭秘Kinect工作原理</a><br /></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">我的园子</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p><br />文章来源:<a href="http://www.cnblogs.com/EvilGhost/archive/2011/06/17/AVLTree.html">http://www.cnblogs.com/EvilGhost/archive/2011/06/17/AVLTree.html</a><img src ="http://www.cppblog.com/vivence/aggbug/148880.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vivence/" target="_blank">EvilGhost</a> 2011-06-17 20:01 <a href="http://www.cppblog.com/vivence/archive/2011/06/17/AVLTree.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一步一步实现自己的模拟控件（9）——消息处理</title><link>http://www.cppblog.com/vivence/archive/2011/04/24/Abstract_Widget_9.html</link><dc:creator>EvilGhost</dc:creator><author>EvilGhost</author><pubDate>Sun, 24 Apr 2011 02:48:00 GMT</pubDate><guid>http://www.cppblog.com/vivence/archive/2011/04/24/Abstract_Widget_9.html</guid><wfw:comment>http://www.cppblog.com/vivence/comments/144894.html</wfw:comment><comments>http://www.cppblog.com/vivence/archive/2011/04/24/Abstract_Widget_9.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/vivence/comments/commentRss/144894.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vivence/services/trackbacks/144894.html</trackback:ping><description><![CDATA[
<p>&#160;</p><p>这次我们将要给Widget增加一些状态，并使其能够接受出消息处理扩展，测试工程中实现了一个按钮的消息处理扩展。</p>
<p><strong>Widget状态：</strong></p>
<p>之前的控件只是绘制了一个边框，并且总是会在窗口中显示。实际上我们往往会希望能够让某个控件显示或者隐藏、可用或者不可用等等，那么控件应该具有能够标识这些状态的属性，于是我们给Widget增加了状态概念。</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #008000;">//</span><span style="color: #008000;"> 状态相关</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;"> AddStates(size_t states);<br></span><span style="color: #0000ff;">void</span><span style="color: #000000;"> SubStates(size_t states);<br></span><span style="color: #0000ff;">bool</span><span style="color: #000000;"> CheckState(widget::StateBitField s);</span></div></pre>
</div>
<p>&#160;</p>
<p>上面是状态相关的几个接口，包括了增加状态组，减少状态组，检测状态。这里有个状态组的概念，是因为我将状态用位域来实现，那么他们就可以通过or运算来得到多个状态的集合，我就这个集合称为状态组。</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">enum</span><span style="color: #000000;"> StateBitField{<br>    STATE_VISIBLE         </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">1</span><span style="color: #000000;"> </span><span style="color: #000000;">&lt;&lt;</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">, </span><span style="color: #008000;">//</span><span style="color: #008000;"> 控件可见？决定控件是否被绘制</span><span style="color: #008000;"><br></span><span style="color: #000000;">    STATE_ENABLE          </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">1</span><span style="color: #000000;"> </span><span style="color: #000000;">&lt;&lt;</span><span style="color: #000000;"> </span><span style="color: #800080;">1</span><span style="color: #000000;">, </span><span style="color: #008000;">//</span><span style="color: #008000;"> 控件可用？决定控件是否响应鼠标键盘消息</span><span style="color: #008000;"><br></span><span style="color: #000000;">    STATE_TRANSPARENT     </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">1</span><span style="color: #000000;"> </span><span style="color: #000000;">&lt;&lt;</span><span style="color: #000000;"> </span><span style="color: #800080;">2</span><span style="color: #000000;">, </span><span style="color: #008000;">//</span><span style="color: #008000;"> 控件透明？决定控件是否响应鼠标键盘消息以及是否显示tooltip</span><span style="color: #008000;"><br></span><span style="color: #000000;">};</span></div></pre>
</div>
<p>&#160;</p>
<p>目前我给Widget定义了3个状态：可见、可用、透明。默认创建的Widget是不可见、不可用、不透明的，需要在创建成功后手动设置，例如：</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #008000;">//</span><span style="color: #008000;"> 建立根控件</span><span style="color: #008000;"><br></span><span style="color: #000000;">auto pRootWidget </span><span style="color: #000000;">=</span><span style="color: #000000;"> ghost::Widget::Create();<br>pRootWidget</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">AddStates(</span></div><div><span style="color: #000000;">　　ghost::widget::STATE_VISIBLE</span><span style="color: #000000;">|</span><span style="color: #000000;">ghost::widget::STATE_ENABLE</span><span style="color: #000000;">|</span><span style="color: #000000;">ghost::widget::STATE_TRANSPARENT);</span></div></pre>
</div>
<p>&#160;</p>
<p><strong>消息处理扩展：</strong></p>
<p>通常对于控件来说，没有消息处理就等于没有生命意义，那么为Widget添加消息处理扩展就意味着使Widget活起来，这次我们就来完成这个任务，期待Widget活起来的那激动人心的一刻。和以往添加扩展支持一样，为扩展编写一个抽象基类，在Widget的关联对象管理中添加这个扩展的关联处理，然后在Widget的SendMessage处理逻辑里增加对消息处理扩展的支持。那么我们的SendMessage实现就变成了这样：</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">int</span><span style="color: #000000;"> Widget::SendMessage(widget::Message message, </span><span style="color: #0000ff;">void</span><span style="color: #000000;">*</span><span style="color: #000000;"> param1</span><span style="color: #008000;">/*</span><span style="color: #008000;"> = 0</span><span style="color: #008000;">*/</span><span style="color: #000000;">, </span><span style="color: #0000ff;">void</span><span style="color: #000000;">*</span><span style="color: #000000;"> param2</span><span style="color: #008000;">/*</span><span style="color: #008000;"> = 0</span><span style="color: #008000;">*/</span><span style="color: #000000;">)<br>{<br>    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (</span><span style="color: #000000;">!</span><span style="color: #000000;">CheckState(widget::STATE_ENABLE))<br>    {<br>        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 不响应鼠标键盘消息</span><span style="color: #008000;"><br></span><span style="color: #000000;">        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> ((widget::MSG_MOUSE_FIRST </span><span style="color: #000000;">&lt;=</span><span style="color: #000000;"> message <br>                </span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;"> widget::MSG_MOUSE_LAST </span><span style="color: #000000;">&gt;=</span><span style="color: #000000;"> message)<br>            </span><span style="color: #000000;">||</span><span style="color: #000000;">(widget::MSG_KEY_FIRST </span><span style="color: #000000;">&lt;=</span><span style="color: #000000;"> message <br>                </span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;"> widget::MSG_KEY_LAST </span><span style="color: #000000;">&gt;=</span><span style="color: #000000;"> message))<br>        {<br>            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> widget::MSG_RESULT_NONE;<br>        }<br>    }<br>    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (CheckState(widget::STATE_TRANSPARENT))<br>    {<br>        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 不响应鼠标键盘消息，不显示tooltip</span><span style="color: #008000;"><br></span><span style="color: #000000;">        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> ((widget::MSG_MOUSE_FIRST </span><span style="color: #000000;">&lt;=</span><span style="color: #000000;"> message <br>            </span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;"> widget::MSG_MOUSE_LAST </span><span style="color: #000000;">&gt;=</span><span style="color: #000000;"> message)<br>            </span><span style="color: #000000;">||</span><span style="color: #000000;">(widget::MSG_KEY_FIRST </span><span style="color: #000000;">&lt;=</span><span style="color: #000000;"> message <br>            </span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;"> widget::MSG_KEY_LAST </span><span style="color: #000000;">&gt;=</span><span style="color: #000000;"> message))<br>        {<br>            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> widget::MSG_RESULT_NONE;<br>        }<br>    }<br><br>    auto pRelatedObject </span><span style="color: #000000;">=</span><span style="color: #000000;"> GetRelatedObject();<br>    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (pRelatedObject)<br>    {<br>        auto pMessageHandle </span><span style="color: #000000;">=</span><span style="color: #000000;"> pRelatedObject</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">GetMessageHandle();<br>        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (pMessageHandle)<br>        {<br>            </span><span style="color: #0000ff;">bool</span><span style="color: #000000;"> handled </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>            </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> res </span><span style="color: #000000;">=</span><span style="color: #000000;">  pMessageHandle</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">OnMessage(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">, message, param1, param2, handled);<br>            </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (handled)<br>            {<br>                </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> res;<br>            }<br>        }<br>    }<br><br>    </span><span style="color: #0000ff;">switch</span><span style="color: #000000;"> (message)<br>    {<br>#ifdef _DEBUG<br>    </span><span style="color: #0000ff;">case</span><span style="color: #000000;"> widget::MSG_DRAW:<br>        {<br>            HBRUSH hBrush </span><span style="color: #000000;">=</span><span style="color: #000000;"> ::CreateSolidBrush(pImpl_</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">testFrameColor_);<br>            ::FrameRect((HDC)param1, </span><span style="color: #000000;">&amp;</span><span style="color: #000000;">pImpl_</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">absoluteRect_, hBrush);<br>            ::DeleteObject(hBrush);<br>        }<br>        </span><span style="color: #0000ff;">break</span><span style="color: #000000;">;<br></span><span style="color: #0000ff;">#endif</span><span style="color: #000000;"> </span><span style="color: #008000;">//</span><span style="color: #008000;"> _DEBUG</span><span style="color: #000000;"><br>    </span><span style="color: #0000ff;">case</span><span style="color: #000000;"> widget::MSG_HIT_TEST:<br>        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (::PtInRect(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">pImpl_</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">absoluteRect_, </span><span style="color: #000000;">*</span><span style="color: #000000;">(</span><span style="color: #0000ff;">const</span><span style="color: #000000;"> POINT</span><span style="color: #000000;">*</span><span style="color: #000000;">)param1))<br>        {<br>            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #000000;">~</span><span style="color: #000000;">widget::MSG_RESULT_NONE;<br>        }<br>        </span><span style="color: #0000ff;">break</span><span style="color: #000000;">;<br>    }<br>    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> widget::MSG_RESULT_NONE;<br>}</span></div></pre>
</div>
<p>&#160;</p>
<p>我多次提到了tooltip，但是我们这次并没有为Widget增加其支持，它将在后面被加入到Widget中来。可以看到这里处理有对消息处理扩展的支持，还有状态对于消息的影响。这里出现了一个MSG_HIT_TEST，这是一个新增的消息。这次为Widget添加了很多的消息，包括了鼠标、键盘等，要将鼠标消息准确的发送给正确的控件，那么点击测试是必不可少的，这个MSG_HIT_TEST消息则是用于控件处理点击测试的。</p>
<p><strong>点击测试：</strong></p>
<p>当容器产生了鼠标事件的时候，我们能够得到鼠标热点在容器中的坐标。前面我也多次提到，模拟控件是容器中的某个区域，那么当鼠标热点位于某个控件所处的区域的时候，那么这个鼠标事件我们就应当交由这个控件进行处理（这是通常情况，也有可能某个控件作为透明控件，不接受任何点击测试）。于是我们便通过点击测试（HitTest）这个访问接口来找到应该处理鼠标事件的控件。在找到控件之后，我们还将坐标映射到了控件的相对坐标系，这样控件就可以以自身的相对位置来处理鼠标事件了。</p>

<p>当然，这次的内容非常多，包括坐标映射、区域映射，捕获鼠标的控件、活动控件、焦点控件等概念都未提到，但在代码中还是能够看到这些概念的。如果一一介绍，那文章就会非常冗长，也会使Widget实现进展缓慢，因此我通常都会省略一些内容，这些内容也就只能通过代码阅读来得到了。</p>

<p><strong>按钮：</strong></p>
<p>为了测试我们这次实现的内容，我们编写了一个按钮的消息处理扩展。简单起见，我们使其不产生命令、不绘制文本，仅仅只是展示对鼠标消息的处理和状态的变化而已。</p>
<p>﻿﻿﻿<img src="http://pic002.cnblogs.com/images/2011/95718/2011042410321772.png"></p>
<p>我们将原先测试代码中的中间那个控件关联了按钮的消息处理，那么它就称为了一个按钮控件了。我们可以将鼠标移到它上面点击看看会发生什么。</p>
<p><span style="font-size: 18pt;"><strong><a href="http://files.cnblogs.com/EvilGhost/%E5%85%B1%E4%BA%AB(9).zip">下载测试工程代码</a> </strong></span>因为我一直都在使用VC10来编写Widget，也用到了一些新的特性，所以子啊这次的测试工程我生成了一份release下的程序，没有VC10的人至少能够看到其运行效果。</p><img src="http://www.cnblogs.com/EvilGhost/aggbug/2026106.html?type=0" width="1" height="1" alt=""><p>作者: <a href="http://www.cnblogs.com/EvilGhost/" target="_blank">Evil.Ghost</a> 发表于 2011-04-24 10:48 <a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/24/Abstract_Widget_9.html" target="_blank">原文链接</a></p><p>评论: 0　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/24/Abstract_Widget_9.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/24/Abstract_Widget_9.html#commentform" target="_blank">发表评论</a></p><hr><p>最新新闻：<br>&#183; <a href="http://news.cnblogs.com/n/98801/" target="_blank">人人赴美上市：无奈大杂烩 挟资本对阵Facebook</a><span style="color:gray">(2011-04-24 11:07)</span><br>&#183; <a href="http://news.cnblogs.com/n/98800/" target="_blank">位置隐私事件进展：Google 回应，华尔街日报质疑</a><span style="color:gray">(2011-04-24 10:19)</span><br>&#183; <a href="http://news.cnblogs.com/n/98799/" target="_blank">消息称苹果正测试T-Mobile版iPhone</a><span style="color:gray">(2011-04-24 10:04)</span><br>&#183; <a href="http://news.cnblogs.com/n/98798/" target="_blank">报告称2020年全球云计算规模达2410亿美元</a><span style="color:gray">(2011-04-24 10:03)</span><br>&#183; <a href="http://news.cnblogs.com/n/98797/" target="_blank">沃尔玛在美国加州测试在线商品配送服务</a><span style="color:gray">(2011-04-24 09:40)</span><br></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/98754/" target="_blank">南方都市报：网络正在改写我们的思维？</a><br></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">我的园子</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p><img src ="http://www.cppblog.com/vivence/aggbug/144894.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vivence/" target="_blank">EvilGhost</a> 2011-04-24 10:48 <a href="http://www.cppblog.com/vivence/archive/2011/04/24/Abstract_Widget_9.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一步一步实现自己的模拟控件（8）——重构</title><link>http://www.cppblog.com/vivence/archive/2011/04/19/Abstract_Widget_8.html</link><dc:creator>EvilGhost</dc:creator><author>EvilGhost</author><pubDate>Tue, 19 Apr 2011 12:06:00 GMT</pubDate><guid>http://www.cppblog.com/vivence/archive/2011/04/19/Abstract_Widget_8.html</guid><wfw:comment>http://www.cppblog.com/vivence/comments/144690.html</wfw:comment><comments>http://www.cppblog.com/vivence/archive/2011/04/19/Abstract_Widget_8.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/vivence/comments/commentRss/144690.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vivence/services/trackbacks/144690.html</trackback:ping><description><![CDATA[
<p>&#160;</p><p>模拟控件已经出了雏形，是应该重构一下了。</p>
<p><strong>新概念：</strong></p>
<p>其实说是新概念也不尽然，只是在这几步的实现中可以发现，这个模拟控件框架中有两个大的概念：容器和控件。我们一直一来都说的是容器窗口，但是其实这是个可以上升的概念。容器容纳一个控件体系，也就是关联一个根控件，那么它就有点类似于我们的根控件了。但是容器由必须实现和具体窗口和控件的交互，所以我们不能简单的将根控件作为容器。并且对于某些特殊的实现，我们还可能需要对容器的某些功能加以扩展，所以我们这次重构为容器抽象了一个基类，并实现了一个窗口容器。</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">class</span><span style="color: #000000;"> Container : </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> Monopolistic{<br>    </span><span style="color: #0000ff;">using</span><span style="color: #000000;"> Monopolistic::pWidget_;<br>    </span><span style="color: #0000ff;">using</span><span style="color: #000000;"> Monopolistic::GetWidget;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>    Container();<br>    </span><span style="color: #0000ff;">virtual</span><span style="color: #000000;"> </span><span style="color: #000000;">~</span><span style="color: #000000;">Container();<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>    Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> GetRootWidget() </span><span style="color: #0000ff;">const</span><span style="color: #000000;">{</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> GetWidget();}<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>    </span><span style="color: #0000ff;">virtual</span><span style="color: #000000;"> </span><span style="color: #0000ff;">bool</span><span style="color: #000000;"> IsValid() </span><span style="color: #0000ff;">const</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>    </span><span style="color: #0000ff;">virtual</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> Invalidate(</span><span style="color: #0000ff;">const</span><span style="color: #000000;"> RECT</span><span style="color: #000000;">&amp;</span><span style="color: #000000;"> rect){} </span><span style="color: #008000;">//</span><span style="color: #008000;"> 无效化容器矩形区域</span><span style="color: #008000;"><br></span><span style="color: #000000;">    </span><span style="color: #0000ff;">virtual</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> Invalidate(HRGN hRgn){}        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 无效化容器不规则区域</span><span style="color: #008000;"><br></span><span style="color: #000000;">    </span><span style="color: #0000ff;">virtual</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> Invalidate(){}                 </span><span style="color: #008000;">//</span><span style="color: #008000;"> 无效化整个容器区域</span><span style="color: #008000;"><br></span><span style="color: #000000;">};</span></div></pre>
</div>
<p>&#160;</p>
<p><strong>解耦：</strong></p>
<p>到目前为止，我们的容器驱动和控件之间具有十分紧密的耦合关系（生命周期耦合），这次重构打算将此解耦，使得我们的容器创建和控件体系创建互不相关，仅仅通过一次操作来使得他们关联或者解除关联。</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #008000;">//</span><span style="color: #008000;"> 建立窗口容器</span><span style="color: #008000;"><br></span><span style="color: #000000;">ghost::widget::WindowContainer container;<br>container.AttachWindow(hWnd);<br><br></span><span style="color: #008000;">//</span><span style="color: #008000;"> 建立根控件</span><span style="color: #008000;"><br></span><span style="color: #000000;">auto pRootWidget </span><span style="color: #000000;">=</span><span style="color: #000000;"> ghost::Widget::Create();<br><br></span><span style="color: #008000;">//</span><span style="color: #008000;"> 关联容器</span><span style="color: #008000;"><br></span><span style="color: #000000;">pRootWidget</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">CreateRelationship(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">container);</span></div></pre>
</div>
<p>&#160;</p>
<p><strong>减少类：</strong></p>
<p>这次还有一个大的变化就是将原先的一些概念融合成了新的概念：驱动、消息过滤、消息映射都融合到了容器当中，因为这三个概念和具体容器关系紧密，所以将他们进行融合以减少我们的类数目。</p>
<p><strong>收获：</strong></p>
<p>这次重构建立了容器这个概念我们是有一定收获的：原先我们无法访问控件所关联的容器，现在可以了。原先控件的容器实现不易扩展，现在可以通过继承来扩展（例如layered窗口），甚至可以为容器实现特有的绘制机制（例如使用opengl，以后我们抽象了绘制概念便可以看到，目前还只能使用GDI）。</p>
<p>这次没有什么新的内容添加，但重构还是有必要的，周期性重构利于我们对整个框架的管理，在重构过程中较容易发现和修改BUG。</p>
<p><span style="font-size: 18pt;"><strong><a href="http://files.cnblogs.com/EvilGhost/%E5%85%B1%E4%BA%AB(8).zip">下载测试工程源码</a></strong></span></p><img src="http://www.cnblogs.com/EvilGhost/aggbug/2021452.html?type=1" width="1" height="1" alt=""><p>作者: <a href="http://www.cnblogs.com/EvilGhost/" target="_blank">Evil.Ghost</a> 发表于 2011-04-19 20:06 <a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/19/Abstract_Widget_8.html" target="_blank">原文链接</a></p><p>评论: 0　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/19/Abstract_Widget_8.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/19/Abstract_Widget_8.html#commentform" target="_blank">发表评论</a></p><hr><p>最新新闻：<br>&#183; <a href="http://news.cnblogs.com/n/98451/" target="_blank">满座网炮轰分众哄抬广告价：江南春承诺将调查</a><span style="color:gray">(2011-04-20 18:08)</span><br>&#183; <a href="http://news.cnblogs.com/n/98450/" target="_blank">周鸿祎：技术颠覆+商业颠覆让360颠覆了行业</a><span style="color:gray">(2011-04-20 18:03)</span><br>&#183; <a href="http://news.cnblogs.com/n/98447/" target="_blank">XJP：网购冷静期恐成一纸空文</a><span style="color:gray">(2011-04-20 18:00)</span><br>&#183; <a href="http://news.cnblogs.com/n/98430/" target="_blank">任正非：一法 两制 三宣教</a><span style="color:gray">(2011-04-20 17:24)</span><br>&#183; <a href="http://news.cnblogs.com/n/98429/" target="_blank">微软：Chrome和Opera存在HTML5执行漏洞</a><span style="color:gray">(2011-04-20 17:20)</span><br></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/98374/" target="_blank">再谈&#8220;我是怎么招聘程序员的&#8221;</a><br></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">我的园子</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p><img src ="http://www.cppblog.com/vivence/aggbug/144690.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vivence/" target="_blank">EvilGhost</a> 2011-04-19 20:06 <a href="http://www.cppblog.com/vivence/archive/2011/04/19/Abstract_Widget_8.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一步一步实现自己的模拟控件（7）——可扩展布局子控件</title><link>http://www.cppblog.com/vivence/archive/2011/04/12/Abstract_Widget_7.html</link><dc:creator>EvilGhost</dc:creator><author>EvilGhost</author><pubDate>Tue, 12 Apr 2011 13:17:00 GMT</pubDate><guid>http://www.cppblog.com/vivence/archive/2011/04/12/Abstract_Widget_7.html</guid><wfw:comment>http://www.cppblog.com/vivence/comments/144069.html</wfw:comment><comments>http://www.cppblog.com/vivence/archive/2011/04/12/Abstract_Widget_7.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/vivence/comments/commentRss/144069.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vivence/services/trackbacks/144069.html</trackback:ping><description><![CDATA[

<p>&nbsp;</p><p><strong>可扩展：</strong></p>
<p>要使得我们的控件具备一定的可扩展性，那么必定会产生控件之外的对象作为扩展，并且这个对象对于控件来说是可插入可移除的。用于扩展的对象和控件之间应该具备一定的关系，例如：1-1，1-n，n-n等。我们将这样的对象关系抽象了出来，称之为对象关系。</p>
<p><strong>对象关系：</strong></p>
<p>一个对象可能允许单个对象对其进行关联，也可能允许多个对象对其进行关联，甚至可能即允许多个对象进行关联，但却对某些类型的对象限制为只能单个的对其进行关联。我们将这些对象抽象为：单对象关系， 多对象关系，独占式对象关系（这是对多对象关系的一种扩展）。</p>
<p><img src="http://pic002.cnblogs.com/images/2011/95718/2011041220560533.png"></p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">class</span><span style="color: #000000;"> ObjectRelationship{<br></span><span style="color: #0000ff;">protected</span><span style="color: #000000;">:<br>    ObjectRelationship(){}<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>    </span><span style="color: #0000ff;">virtual</span><span style="color: #000000;"> </span><span style="color: #000000;">~</span><span style="color: #000000;">ObjectRelationship(){}<br><br></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>    ObjectRelationship(</span><span style="color: #0000ff;">const</span><span style="color: #000000;"> ObjectRelationship</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">);<br>    ObjectRelationship</span><span style="color: #000000;">&amp;</span><span style="color: #000000;"> </span><span style="color: #0000ff;">operator</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;">(</span><span style="color: #0000ff;">const</span><span style="color: #000000;"> ObjectRelationship</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">);<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>    </span><span style="color: #0000ff;">bool</span><span style="color: #000000;"> CreateRelationship(ObjectRelationship</span><span style="color: #000000;">*</span><span style="color: #000000;"> pObject)<br>    {<br>        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (</span><span style="color: #000000;">!</span><span style="color: #000000;">DoCreateRelationship_(pObject))<br>        {<br>            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>        }<br>        RelationshipCreated_(pObject);<br>        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br>    }<br>    </span><span style="color: #0000ff;">bool</span><span style="color: #000000;"> DestroyRelationship(ObjectRelationship</span><span style="color: #000000;">*</span><span style="color: #000000;"> pObject)<br>    {<br>        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (</span><span style="color: #000000;">!</span><span style="color: #000000;">DoDestroyRelationship_(pObject))<br>        {<br>            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>        }<br>        RelationshipDestroyed_(pObject);<br>        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br>    }<br><br></span><span style="color: #0000ff;">protected</span><span style="color: #000000;">:<br>    </span><span style="color: #0000ff;">virtual</span><span style="color: #000000;"> </span><span style="color: #0000ff;">bool</span><span style="color: #000000;"> DoCreateRelationship_(ObjectRelationship</span><span style="color: #000000;">*</span><span style="color: #000000;"> </span><span style="color: #008000;">/*</span><span style="color: #008000;">pObject</span><span style="color: #008000;">*/</span><span style="color: #000000;">) </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">;<br>    </span><span style="color: #0000ff;">virtual</span><span style="color: #000000;"> </span><span style="color: #0000ff;">bool</span><span style="color: #000000;"> DoDestroyRelationship_(ObjectRelationship</span><span style="color: #000000;">*</span><span style="color: #000000;"> </span><span style="color: #008000;">/*</span><span style="color: #008000;">pObject</span><span style="color: #008000;">*/</span><span style="color: #000000;">) </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">;<br><br></span><span style="color: #0000ff;">protected</span><span style="color: #000000;">:<br>    </span><span style="color: #0000ff;">virtual</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> RelationshipCreated_(ObjectRelationship</span><span style="color: #000000;">*</span><span style="color: #000000;"> </span><span style="color: #008000;">/*</span><span style="color: #008000;">pObject</span><span style="color: #008000;">*/</span><span style="color: #000000;">){}<br>    </span><span style="color: #0000ff;">virtual</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> RelationshipDestroyed_(ObjectRelationship</span><span style="color: #000000;">*</span><span style="color: #000000;"> </span><span style="color: #008000;">/*</span><span style="color: #008000;">pObject</span><span style="color: #008000;">*/</span><span style="color: #000000;">){}<br>};</span></div></pre>
</div>
<p>&nbsp;</p>
<p>这是对象关系基类，接口只有两个：建立关系，销毁关系。</p>
<p>单对象关系， 多对象关系都派生于这个基类，而独占式对象关系是实现的两个帮助函数来辅助多对象关系。我们的Widget派生于多对象关系，它便具备了和多个对象建立关系的能力（我们将有不同的扩展关联到Widget）。为了便于管理和扩展，我们将所有和Widget关联的扩展放到一个对象当中进行管理，Widget和扩展之间的关系建立和销毁都委托这个对象来进行。</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">class</span><span style="color: #000000;"> LayoutChildren;<br><br>typedef std::</span><span style="color: #0000ff;">set</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">LayoutChildren</span><span style="color: #000000;">*&gt;</span><span style="color: #000000;"> LayoutChildrenSet;<br><br></span><span style="color: #0000ff;">class</span><span style="color: #000000;"> RelatedObject{<br>    Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> pWidget_; </span><span style="color: #008000;">//</span><span style="color: #008000;"> 控件<br><br>    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 控件所关联的对象</span><span style="color: #008000;"><br></span><span style="color: #000000;">    LayoutChildrenSet layoutChildrens_; </span><span style="color: #008000;">//</span><span style="color: #008000;"> 可以有多个布局管理管理不同的子控件布局</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>    friend </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Widget;<br>    </span><span style="color: #0000ff;">explicit</span><span style="color: #000000;"> RelatedObject(Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> </span><span style="color: #0000ff;">const</span><span style="color: #000000;"> pWidget);<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">: </span><span style="color: #008000;">//</span><span style="color: #008000;"> 获取关联对象的接口</span><span style="color: #008000;"><br></span><span style="color: #000000;">    </span><span style="color: #0000ff;">const</span><span style="color: #000000;"> LayoutChildrenSet</span><span style="color: #000000;">&amp;</span><span style="color: #000000;"> GetLayoutChildrens() </span><span style="color: #0000ff;">const</span><span style="color: #000000;">{</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> layoutChildrens_;}<br><br></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>    </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> RelationshipCreated_(ObjectRelationship</span><span style="color: #000000;">*</span><span style="color: #000000;"> pObject);<br>    </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> RelationshipDestroyed_(ObjectRelationship</span><span style="color: #000000;">*</span><span style="color: #000000;"> pObject);<br>};</span></div></pre>
</div>
<p>&nbsp;</p>

<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">void</span><span style="color: #000000;"> Widget::RelationshipCreated_(ObjectRelationship</span><span style="color: #000000;">*</span><span style="color: #000000;"> pObject)<br>{<br>    GetRelatedObject()</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">RelationshipCreated_(pObject);<br>}<br></span><span style="color: #0000ff;">void</span><span style="color: #000000;"> Widget::RelationshipDestroyed_(ObjectRelationship</span><span style="color: #000000;">*</span><span style="color: #000000;"> pObject)<br>{<br>    GetRelatedObject()</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">RelationshipDestroyed_(pObject);<br>}</span></div></pre>
</div>
<p>&nbsp;</p>
<p><strong>布局子控件：</strong></p>
<p>我们为Widgt添加了一个布局子控件的接口，当控件自身区域变化的时候会自动的调用这个接口，当然用户也可以随时调用此接口对子控件进行布局。此接口负责将操作传递给扩展，我们考虑到子控件的布局策略可能会各有不同，因此我们能够关联多个布局子控件扩展到Widget，这使得我们能够以不同的布局策略来区别对待不同的子控件。</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">void</span><span style="color: #000000;"> Widget::LayoutChildren()<br>{<br>    auto pRelatedObject </span><span style="color: #000000;">=</span><span style="color: #000000;"> GetRelatedObject();<br>    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (pRelatedObject)<br>    {<br>        </span><span style="color: #0000ff;">const</span><span style="color: #000000;"> widget::LayoutChildrenSet</span><span style="color: #000000;">&amp;</span><span style="color: #000000;"> layoutChildrens </span><span style="color: #000000;">=</span><span style="color: #000000;"> pRelatedObject</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">GetLayoutChildrens();<br>        std::for_each(<br>            layoutChildrens.begin(), layoutChildrens.end(), <br>            std::bind(std::mem_fn(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">widget::LayoutChildren::Layout), std::placeholders::_1, </span><span style="color: #0000ff;">this</span><span style="color: #000000;">));<br>    }<br>}</span></div></pre>
</div>
<p>&nbsp;</p>
<p>我们创建了一个边缘式布局自控件扩展进行测试，测试效果在测试工程中能够看到。</p>
<p><strong><span style="font-size: 18pt;"><a href="http://files.cnblogs.com/EvilGhost/%E5%85%B1%E4%BA%AB(7).zip">下载测试工程源码</a></span></strong></p><img src="http://www.cnblogs.com/EvilGhost/aggbug/2014101.html?type=1" width="1" height="1" alt=""><p>作者: <a href="http://www.cnblogs.com/EvilGhost/" target="_blank">Evil.Ghost</a> 发表于 2011-04-12 21:17 <a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/12/Abstract_Widget_7.html" target="_blank">原文链接</a></p><p>评论: 0　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/12/Abstract_Widget_7.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/12/Abstract_Widget_7.html#commentform" target="_blank">发表评论</a></p><hr><p>最新新闻：<br>&#183; <a href="http://news.cnblogs.com/n/97208/" target="_blank">消息称iPad 3仍不采用Retina屏幕</a><span style="color:gray">(2011-04-12 20:29)</span><br>&#183; <a href="http://news.cnblogs.com/n/97207/" target="_blank">nginx 1.0.0发布</a><span style="color:gray">(2011-04-12 20:27)</span><br>&#183; <a href="http://news.cnblogs.com/n/97206/" target="_blank">2011Mozilla开发者大会亮点抢先看</a><span style="color:gray">(2011-04-12 20:24)</span><br>&#183; <a href="http://news.cnblogs.com/n/97204/" target="_blank">亚马逊成谷歌第一大广告主 每年2亿美元（图）</a><span style="color:gray">(2011-04-12 20:16)</span><br>&#183; <a href="http://news.cnblogs.com/n/97202/" target="_blank">九城OpenFeint中国首秀：与联通推手游</a><span style="color:gray">(2011-04-12 20:13)</span><br></p><p>编辑推荐：<a href="http://www.cnblogs.com/baihmpgy/archive/2011/04/11/2013113.html" target="_blank">体验Managed Extensibility Framework精妙的设计</a><br></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">我的园子</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p><img src ="http://www.cppblog.com/vivence/aggbug/144069.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vivence/" target="_blank">EvilGhost</a> 2011-04-12 21:17 <a href="http://www.cppblog.com/vivence/archive/2011/04/12/Abstract_Widget_7.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一步一步实现自己的模拟控件（6）——控件树及控件区域</title><link>http://www.cppblog.com/vivence/archive/2011/04/09/Abstract_Widget_6.html</link><dc:creator>EvilGhost</dc:creator><author>EvilGhost</author><pubDate>Sat, 09 Apr 2011 11:05:00 GMT</pubDate><guid>http://www.cppblog.com/vivence/archive/2011/04/09/Abstract_Widget_6.html</guid><wfw:comment>http://www.cppblog.com/vivence/comments/143840.html</wfw:comment><comments>http://www.cppblog.com/vivence/archive/2011/04/09/Abstract_Widget_6.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/vivence/comments/commentRss/143840.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vivence/services/trackbacks/143840.html</trackback:ping><description><![CDATA[
<p>&#160;</p><p><strong>控件树</strong></p>
<p><a target="_parent" href="http://www.cnblogs.com/EvilGhost/archive/2011/04/03/Abstract_Widget_1.html">一步一步实现自己的模拟控件（1）</a>中的图上我们可以看到，我们的控件体系其实就是一个控件树。每一个窗口关联一个根控件，所有控件都在这个根控件之下，父控件包容并管理子控件，那么我们的Widget就应该是一个树结点。一个树结点至少有对Parent和Chilren的设置和访问接口：</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">void</span><span style="color: #000000;"> SetParent(Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> </span><span style="color: #0000ff;">const</span><span style="color: #000000;"> pNewParent);<br>Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> GetParent() </span><span style="color: #0000ff;">const</span><span style="color: #000000;">;<br><br></span><span style="color: #0000ff;">bool</span><span style="color: #000000;"> InsertChild(Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> </span><span style="color: #0000ff;">const</span><span style="color: #000000;"> pChild);<br></span><span style="color: #0000ff;">bool</span><span style="color: #000000;"> RemoveChild(Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> </span><span style="color: #0000ff;">const</span><span style="color: #000000;"> pChild);</span></div></pre>
</div>
<p>&#160;</p>
<p>在父控件销毁的时候它要负责销毁其下所有的子控件（类似窗口销毁也会销毁其子窗口）：</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #000000;">Widget::</span><span style="color: #000000;">~</span><span style="color: #000000;">Widget()<br>{<br>    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 销毁所有子控件</span><span style="color: #008000;"><br></span><span style="color: #000000;">    WidgetSet temp(std::move(pImpl_</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">children_));<br>    std::for_each(temp.begin(), temp.end(), std::mem_fn(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">Widget::Destroy));<br><br>    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (IsRoot()) <br>    {<br>        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 作为根控件，同时销毁驱动</span><span style="color: #008000;"><br></span><span style="color: #000000;">        delete pImpl_</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">pDriver_;<br>    }<br>    </span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br>    {<br>        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 不是根控件，则脱离父控件</span><span style="color: #008000;"><br></span><span style="color: #000000;">        SetParent(</span><span style="color: #800080;">0</span><span style="color: #000000;">);<br>    }<br><br>    delete pImpl_;<br>}</span></div></pre>
</div>
<p>&#160;</p>
<p>实现中我们使用std::set来保存子控件，这样便于防止子控件重复设置，也便于移除子控件，缺点就是不能对子控件进行排序。如果以后我们提供控件的z-order概念，那么我们就会使用能够进行排序的容器来容纳子控件。</p>
<p><strong>控件区域：</strong></p>
<p>windows下，我们使用RECT结构来保存控件自身相对于窗口客户区的区域，那么窗口客户区尺寸改变时也就是我们控件进行布局的时机，那我们就要在消息过滤中处理WM_SIZE消息了。</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">case</span><span style="color: #000000;"> WM_SIZE: </span><span style="color: #008000;">//</span><span style="color: #008000;"> 让根控件适应真个客户区</span><span style="color: #008000;"><br></span><span style="color: #000000;">    {<br>        RECT clientRect;<br>        ::GetClientRect(param.hWnd, </span><span style="color: #000000;">&amp;</span><span style="color: #000000;">clientRect);<br>        pRootWidget</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">SetAbsoluteRect(clientRect, </span><span style="color: #0000ff;">false</span><span style="color: #000000;">);<br>    }<br>    </span><span style="color: #0000ff;">break</span><span style="color: #000000;">;</span></div></pre>
</div>
<p>&#160;</p>
<p>我们将控件的布局交由父控件管理，也就是说我们只需要更新根控件区域便可。根控件负责对其子控件进行布局，如此递归。</p>
<p><strong>控件更新：</strong></p>
<p>当控件区域改变了，那么相应的其显示也应相应的进行更新，所以我们的SetAbsoluteRect接口有一个update参数用于控制是否让窗口产生无效区域激活绘制。</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #008000;">//</span><span style="color: #008000;"> 此处的update作用是控制是否立即更新显示。<br></span><span style="color: #008000;">//</span><span style="color: #008000;"> 因为模拟控件只是窗口客户区的一个区域，当区域改变时应该产生原区域和新区域or运算后区域的脏矩形<br></span><span style="color: #008000;">//</span><span style="color: #008000;"> 以使得窗口去重绘这部分区域。<br></span><span style="color: #008000;">//</span><span style="color: #008000;"> 可能有些批量性质的操作会在操作多个控件后进行整体更新，所以在对单个控件设置新区域的时候可能不会想要更新。<br></span><span style="color: #008000;">//</span><span style="color: #008000;"> 所以才加上这个是否立即更新的开关。</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;"> SetAbsoluteRect(</span><span style="color: #0000ff;">const</span><span style="color: #000000;"> RECT</span><span style="color: #000000;">&amp;</span><span style="color: #000000;"> rect, </span><span style="color: #0000ff;">bool</span><span style="color: #000000;"> update </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">true</span><span style="color: #000000;">);</span></div></pre>
</div>
<p>&#160;</p>
<p>既然提到了绘制，那么我们也应该让我们的控件展示在窗口上了。</p>
<p><strong>控件绘制：</strong></p>
<p>通常我们的窗口程序都是在WM_PAINT消息中进行绘制，我们的控件系统当然也需要处理此消息。</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">case</span><span style="color: #000000;"> WM_PAINT:<br>    {<br>        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 使用内存DC来缓冲绘制<br>        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 目前没有计算脏矩形区域</span><span style="color: #008000;"><br></span><span style="color: #000000;">        wnd_msg_assistant::OnPaint opAssistant(param.hWnd);<br>        pRootWidget</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">Draw(opAssistant.GetMemDC());<br>    }<br>    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> S_OK;</span></div></pre>
</div>
<p>&#160;</p>
<p>这里引入了一个辅助对象帮助我们产生内存DC，优化我们的绘制效率。我们直接return了这个消息，也就是说我们将这个消息过滤掉了。前面WM_SIZE和WM_DESTROY我们都没有过滤，只是在这个时机对控件进行了通知或者操作。之所以要过滤WM_PAINT消息是因为外部的绘制和控件的绘制难以协调，那么我们干脆就接管了窗口客户区的绘制了。</p>
<p>当然，控件也需要负责绘制其子控件，那么Draw接口中便会调用子控件的Draw，如此递归使得每个控件都能够得以绘制。</p>
<p><strong>首次直观的看到我们的控件：</strong></p>
<p>我们在调试版本中，为每个控件生成了一个随机的颜色，根据控件的区域绘制了其边框，这样我们就第一次直观的在窗口中看到了我们的控件。</p>
<p>﻿<img src="http://pic002.cnblogs.com/images/2011/95718/2011040918550120.png"></p>
<p>迫不及待，具有了区域的控件，我们已经急切的想要对其布局进行控制，绘制进行定制了。布局控制和绘制定制当然属于扩展部分，那么下面就将要引入我们的扩展体系了，尽请期待。</p>
<p><span style="font-size: 18pt;"><strong><a href="http://files.cnblogs.com/EvilGhost/%E5%85%B1%E4%BA%AB(6).zip">下载测试工程源码</a></strong></span></p><img src="http://www.cnblogs.com/EvilGhost/aggbug/2010698.html?type=1" width="1" height="1" alt=""><p>作者: <a href="http://www.cnblogs.com/EvilGhost/" target="_blank">Evil.Ghost</a> 发表于 2011-04-09 19:05 <a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/09/Abstract_Widget_6.html" target="_blank">原文链接</a></p><p>评论: 0　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/09/Abstract_Widget_6.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/09/Abstract_Widget_6.html#commentform" target="_blank">发表评论</a></p><hr><p>最新新闻：<br>&#183; <a href="http://news.cnblogs.com/n/96900/" target="_blank">苹果iPad 2通过3C认证 最晚5月国内上市</a><span style="color:gray">(2011-04-10 09:18)</span><br>&#183; <a href="http://news.cnblogs.com/n/96899/" target="_blank">盲目依赖iPhone等工具导航 英国驴友迷路多</a><span style="color:gray">(2011-04-10 09:14)</span><br>&#183; <a href="http://news.cnblogs.com/n/96898/" target="_blank">趣谈：想担任CEO的话，最好是去苹果工作，其次是微软，再才是Google</a><span style="color:gray">(2011-04-10 08:26)</span><br>&#183; <a href="http://news.cnblogs.com/n/96897/" target="_blank">腾讯将建立新数据中心，规模为苹果的两倍</a><span style="color:gray">(2011-04-10 08:25)</span><br>&#183; <a href="http://news.cnblogs.com/n/96896/" target="_blank">轻量化的微型博客Tumblr</a><span style="color:gray">(2011-04-10 08:03)</span><br></p><p>编辑推荐：<a href="http://www.cnblogs.com/xiaotie/archive/2011/04/09/2010198.html" target="_blank">非战之罪，从永中Office谈起</a><br></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">我的园子</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p><img src ="http://www.cppblog.com/vivence/aggbug/143840.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vivence/" target="_blank">EvilGhost</a> 2011-04-09 19:05 <a href="http://www.cppblog.com/vivence/archive/2011/04/09/Abstract_Widget_6.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一步一步实现自己的模拟控件（5）——隐藏类</title><link>http://www.cppblog.com/vivence/archive/2011/04/07/Abstract_Widget_5.html</link><dc:creator>EvilGhost</dc:creator><author>EvilGhost</author><pubDate>Thu, 07 Apr 2011 12:55:00 GMT</pubDate><guid>http://www.cppblog.com/vivence/archive/2011/04/07/Abstract_Widget_5.html</guid><wfw:comment>http://www.cppblog.com/vivence/comments/143841.html</wfw:comment><comments>http://www.cppblog.com/vivence/archive/2011/04/07/Abstract_Widget_5.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/vivence/comments/commentRss/143841.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vivence/services/trackbacks/143841.html</trackback:ping><description><![CDATA[
<p>&#160;</p><p><strong>隐藏驱动类：</strong></p>
<p>为了让用户有更简单的使用接口，我们需要把不必要的东西进行一定的隐藏。前面我就提到WidgetDriver对于用户来说是不关心的东西，那么我们就将其进行隐藏。</p>
<p><strong>　　方案1：</strong></p>
<p>　　将WidgetDriver放到Widget.cpp中，这样的隐藏方式是最严密的，对于用户来说完全看不到WidgetDriver。但是随着我们的实现膨胀，这会让我们的Widget.cpp变得非常臃肿。</p>
<p><strong>　　方案2：</strong></p>
<p>　　不改变文件结构，将WidgetDriver私有化，通过友元声明使得只有Widget类对象能够访问WidgetDriver。这样用户虽然能够看到WidgetDriver这个类定义，但是却无法使用，这样对于代码的结构组织也更有利。</p>
<p>我采用了方案2</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">class</span><span style="color: #000000;"> Driver_{<br>    friend </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Widget;<br><br>    DriverImpl_</span><span style="color: #000000;">*</span><span style="color: #000000;"> pImpl_;<br><br></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>    </span><span style="color: #0000ff;">explicit</span><span style="color: #000000;"> Driver_(HWND hWnd);<br>    </span><span style="color: #000000;">~</span><span style="color: #000000;">Driver_();<br>    Driver_(</span><span style="color: #0000ff;">const</span><span style="color: #000000;"> Driver_</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">);<br>    Driver_</span><span style="color: #000000;">&amp;</span><span style="color: #000000;"> </span><span style="color: #0000ff;">operator</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;">(</span><span style="color: #0000ff;">const</span><span style="color: #000000;"> Driver_</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">);<br><br></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>    </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> SetRootWidget(Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> pRootWidget);<br>    HWND GetContainerWindow() </span><span style="color: #0000ff;">const</span><span style="color: #000000;">;<br>    Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> GetRootWidget() </span><span style="color: #0000ff;">const</span><span style="color: #000000;">;<br>};</span></div></pre>
</div>
<p>&#160;</p>
<p>因为我们隐藏了WidgetDriver，那么它的职能就能够进行简化。前面提到的根控件和WidgetDriver之间的关系我们就能够改为当方面控制了。于是我将WidgetDriver放到了根控件中进行管理，我们只需要操作根控件便可。</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #000000;">Widget::Widget(HWND hWnd) <br>    : pImpl_(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> WidgetImpl)<br>{<br>    pImpl_</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">pDriver </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> widget::Driver_(hWnd);<br>    pImpl_</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">pDriver</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">SetRootWidget(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br>}<br><br>Widget::</span><span style="color: #000000;">~</span><span style="color: #000000;">Widget()<br>{<br>    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (IsRoot())<br>    {<br>        delete pImpl_</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">pDriver;<br>    }<br>    delete pImpl_;<br>}</span></div></pre>
</div>
<p>&#160;</p>
<p>前面我们都没有提到应该何时结束我们的系统，但是我们的系统生命周期和窗口是息息相关的，在窗口销毁的时候那么和这个窗口相关联的控件体系就应该销毁。于是我们对消息过滤器做了一点点改动：</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #000000;">LRESULT MessageFilter::Filter(</span><span style="color: #0000ff;">const</span><span style="color: #000000;"> Param</span><span style="color: #000000;">&amp;</span><span style="color: #000000;"> param, Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> pRootWidget)<br>{<br>    assert(param.originalProc);<br>    assert(pRootWidget);<br>#ifdef _DEBUG<br>    std::stringstream ss;<br>    ss</span><span style="color: #000000;">&lt;&lt;</span><span style="color: #800000;">"</span><span style="color: #800000;">窗口消息: </span><span style="color: #800000;">"</span><span style="color: #000000;">&lt;&lt;</span><span style="color: #000000;">std::showbase</span><span style="color: #000000;">&lt;&lt;</span><span style="color: #000000;">std::hex</span><span style="color: #000000;">&lt;&lt;</span><span style="color: #000000;">param.message</span><span style="color: #000000;">&lt;&lt;</span><span style="color: #800000;">"</span><span style="color: #800000;"> 进入Widget消息过滤！\r\n</span><span style="color: #800000;">"</span><span style="color: #000000;">;<br>    ::OutputDebugStringA(ss.str().c_str());<br></span><span style="color: #0000ff;">#endif</span><span style="color: #000000;"> </span><span style="color: #008000;">//</span><span style="color: #008000;"> _DEBUG</span><span style="color: #000000;"><br><br>    LRESULT ret </span><span style="color: #000000;">=</span><span style="color: #000000;"> ::CallWindowProc(<br>        param.originalProc, <br>        param.hWnd, <br>        param.message, <br>        param.wParam, <br>        param.lParam);<br><br>    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (WM_DESTROY </span><span style="color: #000000;">==</span><span style="color: #000000;"> param.message)<br>    {<br>        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 销毁根控件</span><span style="color: #008000;"><br></span><span style="color: #000000;">        pRootWidget</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">Destroy();<br>    }<br><br>    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> ret;<br>}</span></div></pre>
</div>
我们在接收到WM_DESTROY窗口消息的时候销毁根控件，根控件析构的时候又会销毁控件驱动，控件驱动析构的时候会解除和窗口的关联。<p>&#160;</p>
<p>下一步我们便要开始对我们的控件进行设计了，我们自始至终都将控件抽象为窗口客户区的一个区域，所以说我们并不打算使用继承来扩展控件。后面会看到我们使用插入式的扩展，这样的方式具备动态替换的能力甚至还能将一个扩展共享给多个控件使用。</p>
<p><strong><span style="font-size: 18pt;"><a href="http://files.cnblogs.com/EvilGhost/%E5%85%B1%E4%BA%AB(5).zip">下载测试工程源码</a></span></strong></p><img src="http://www.cnblogs.com/EvilGhost/aggbug/2008678.html?type=1" width="1" height="1" alt=""><p>作者: <a href="http://www.cnblogs.com/EvilGhost/" target="_blank">Evil.Ghost</a> 发表于 2011-04-07 20:55 <a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/07/Abstract_Widget_5.html" target="_blank">原文链接</a></p><p>评论: 0　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/07/Abstract_Widget_5.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/07/Abstract_Widget_5.html#commentform" target="_blank">发表评论</a></p><hr><p>最新新闻：<br>&#183; <a href="http://news.cnblogs.com/n/96900/" target="_blank">苹果iPad 2通过3C认证 最晚5月国内上市</a><span style="color:gray">(2011-04-10 09:18)</span><br>&#183; <a href="http://news.cnblogs.com/n/96899/" target="_blank">盲目依赖iPhone等工具导航 英国驴友迷路多</a><span style="color:gray">(2011-04-10 09:14)</span><br>&#183; <a href="http://news.cnblogs.com/n/96898/" target="_blank">趣谈：想担任CEO的话，最好是去苹果工作，其次是微软，再才是Google</a><span style="color:gray">(2011-04-10 08:26)</span><br>&#183; <a href="http://news.cnblogs.com/n/96897/" target="_blank">腾讯将建立新数据中心，规模为苹果的两倍</a><span style="color:gray">(2011-04-10 08:25)</span><br>&#183; <a href="http://news.cnblogs.com/n/96896/" target="_blank">轻量化的微型博客Tumblr</a><span style="color:gray">(2011-04-10 08:03)</span><br></p><p>编辑推荐：<a href="http://www.cnblogs.com/xiaotie/archive/2011/04/09/2010198.html" target="_blank">非战之罪，从永中Office谈起</a><br></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">我的园子</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p><img src ="http://www.cppblog.com/vivence/aggbug/143841.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vivence/" target="_blank">EvilGhost</a> 2011-04-07 20:55 <a href="http://www.cppblog.com/vivence/archive/2011/04/07/Abstract_Widget_5.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一步一步实现自己的模拟控件（4）——根控件</title><link>http://www.cppblog.com/vivence/archive/2011/04/06/Abstract_Widget_4.html</link><dc:creator>EvilGhost</dc:creator><author>EvilGhost</author><pubDate>Wed, 06 Apr 2011 13:21:00 GMT</pubDate><guid>http://www.cppblog.com/vivence/archive/2011/04/06/Abstract_Widget_4.html</guid><wfw:comment>http://www.cppblog.com/vivence/comments/143842.html</wfw:comment><comments>http://www.cppblog.com/vivence/archive/2011/04/06/Abstract_Widget_4.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/vivence/comments/commentRss/143842.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vivence/services/trackbacks/143842.html</trackback:ping><description><![CDATA[
<p>&#160;</p><div>
<p><strong>窗口、控件驱动、根控件之间的关系</strong></p>
<p>前面我们已经说了，一个窗口只能关联一个控件驱动，一个控件驱动也同样对应一个根控件。为什么呢？因为我们的驱动需要作用于一个控件体系，一个控件体系具有一个根控件，这个根控件管理了整个窗口的客户区。这样我们才能在这个根控件下创建任意的控件，并活动在窗口客户区。</p>
<p><strong>生命周期控制</strong></p>
<p>基于上面的关系，控件驱动和根控件的生命息息相关，那么我们让其相互制约。既然是他们自身相互制约，那么用户就不应该管理其生命周期，我们特意引入一个简单的对象池来管理，并用访问控制来避免外部直接构造。</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>        </span><span style="color: #0000ff;">explicit</span><span style="color: #000000;"> Widget(widget::Driver</span><span style="color: #000000;">*</span><span style="color: #000000;"> pDriver);<br>        </span><span style="color: #000000;">~</span><span style="color: #000000;">Widget();<br>        Widget(</span><span style="color: #0000ff;">const</span><span style="color: #000000;"> Widget</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">);<br>        Widget</span><span style="color: #000000;">&amp;</span><span style="color: #000000;"> </span><span style="color: #0000ff;">operator</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;">(</span><span style="color: #0000ff;">const</span><span style="color: #000000;"> Widget</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">);<br><br>        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 让对象池能够创建Widget对象</span><span style="color: #008000;"><br></span><span style="color: #000000;">        friend </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> ObjectPool</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Widget</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">;<br></span><span style="color: #0000ff;">#pragma</span><span style="color: #000000;"> warning(push)</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">#pragma</span><span style="color: #000000;"> warning(disable:4396)</span><span style="color: #000000;"><br>        friend </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> std::_Destroy(Widget _FARQ </span><span style="color: #000000;">*</span><span style="color: #000000;">);<br></span><span style="color: #0000ff;">#pragma</span><span style="color: #000000;"> warning(pop)</span><span style="color: #000000;"><br><br>    </span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>        </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> Create(HWND hWnd); </span><span style="color: #008000;">//</span><span style="color: #008000;"> 创建根控件</span><span style="color: #008000;"><br></span><span style="color: #000000;">        </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> Destroy();</span></div></pre>
</div>
<p>&#160;</p>
<p>我们提供了一个静态接口Create用于创建根控件，可以注意到的一点是参数是窗口句柄。其实用户对于什么驱动、什么过滤的都不关心，用户只关心控件体系，所以说我们可以通过这个接口透明的创建根组件，实现中会自动的去驱动此窗口。</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #000000;">Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> Widget::Create(HWND hWnd)<br>{<br>    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> GetWidgetPool_().Construct(widget::Driver::Create(hWnd));<br>}</span></div></pre>
</div>
<p>&#160;</p>
<p>驱动构造时会创建根控件，析构时销毁根控件</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #000000;">Driver::Driver(HWND hWnd)<br>    : pImpl_(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> DriverImpl(hWnd))<br>{<br>    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 创建根控件</span><span style="color: #008000;"><br></span><span style="color: #000000;">    pImpl_</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">SetRootWidget(Widget::Create_(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">));<br>}</span></div></pre>
</div>
<div class="cnblogs_code">
<pre><div><span style="color: #000000;">~</span><span style="color: #000000;">DriverImpl()<br>{<br>    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 销毁根控件</span><span style="color: #008000;"><br></span><span style="color: #000000;">    Widget</span><span style="color: #000000;">*</span><span style="color: #000000;"> pOldRootWidget </span><span style="color: #000000;">=</span><span style="color: #000000;"> GetRootWidget();<br>    pRootWidget_ </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0</span><span style="color: #000000;">;<br>    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (pOldRootWidget)<br>    {<br>        pOldRootWidget</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">Destroy();<br>    }<br>}</span></div></pre>
</div>
<p>&#160;</p>
<p>同样，根控件析构时也销毁控件驱动</p>
<p>
</p><div class="cnblogs_code">
<pre><div><span style="color: #000000;">Widget::</span><span style="color: #000000;">~</span><span style="color: #000000;">Widget()<br>{<br>    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (IsRoot())<br>    {<br>        pImpl_</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">GetDriver()</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">Destroy();<br>    }<br>    delete pImpl_;<br>}</span></div></pre>
</div>
<p>&#160;</p>
<p>这样，用户其实有两个入口可以进入到我们的控件系统，一个是通过控件驱动，一个是通过控件本身。我们提倡用户不去关心控件驱动。那么甚至我们可以隐藏Driver这个类，目前我没有这样做。</p>
<p><strong><span style="font-size: 18pt;"><a href="http://files.cnblogs.com/EvilGhost/%E5%85%B1%E4%BA%AB(4).zip">下载测试工程源码</a></span></strong></p>
<div></div>
</div><img src="http://www.cnblogs.com/EvilGhost/aggbug/2007217.html?type=1" width="1" height="1" alt=""><p>作者: <a href="http://www.cnblogs.com/EvilGhost/" target="_blank">Evil.Ghost</a> 发表于 2011-04-06 21:21 <a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/06/Abstract_Widget_4.html" target="_blank">原文链接</a></p><p>评论: 0　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/06/Abstract_Widget_4.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/06/Abstract_Widget_4.html#commentform" target="_blank">发表评论</a></p><hr><p>最新新闻：<br>&#183; <a href="http://news.cnblogs.com/n/96900/" target="_blank">苹果iPad 2通过3C认证 最晚5月国内上市</a><span style="color:gray">(2011-04-10 09:18)</span><br>&#183; <a href="http://news.cnblogs.com/n/96899/" target="_blank">盲目依赖iPhone等工具导航 英国驴友迷路多</a><span style="color:gray">(2011-04-10 09:14)</span><br>&#183; <a href="http://news.cnblogs.com/n/96898/" target="_blank">趣谈：想担任CEO的话，最好是去苹果工作，其次是微软，再才是Google</a><span style="color:gray">(2011-04-10 08:26)</span><br>&#183; <a href="http://news.cnblogs.com/n/96897/" target="_blank">腾讯将建立新数据中心，规模为苹果的两倍</a><span style="color:gray">(2011-04-10 08:25)</span><br>&#183; <a href="http://news.cnblogs.com/n/96896/" target="_blank">轻量化的微型博客Tumblr</a><span style="color:gray">(2011-04-10 08:03)</span><br></p><p>编辑推荐：<a href="http://www.cnblogs.com/xiaotie/archive/2011/04/09/2010198.html" target="_blank">非战之罪，从永中Office谈起</a><br></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">我的园子</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p><img src ="http://www.cppblog.com/vivence/aggbug/143842.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vivence/" target="_blank">EvilGhost</a> 2011-04-06 21:21 <a href="http://www.cppblog.com/vivence/archive/2011/04/06/Abstract_Widget_4.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一步一步实现自己的模拟控件（3）——Widget驱动</title><link>http://www.cppblog.com/vivence/archive/2011/04/03/Abstract_Widget_3.html</link><dc:creator>EvilGhost</dc:creator><author>EvilGhost</author><pubDate>Sun, 03 Apr 2011 05:56:00 GMT</pubDate><guid>http://www.cppblog.com/vivence/archive/2011/04/03/Abstract_Widget_3.html</guid><wfw:comment>http://www.cppblog.com/vivence/comments/143843.html</wfw:comment><comments>http://www.cppblog.com/vivence/archive/2011/04/03/Abstract_Widget_3.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/vivence/comments/commentRss/143843.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vivence/services/trackbacks/143843.html</trackback:ping><description><![CDATA[
<p>&#160;</p><p>前面我们利用现有的微软ATL实现的thunk已经为我们截获窗口消息做好了准备，此刻我们应该编写我们的Widget驱动的初步实现了。</p>
<p>利用thunk对窗口消息过程进行子类化，那么窗口消息就会先流入到我们的Widget驱动对象，Widget驱动对象负责将消息传递给消息过滤器。现在我们的消息过滤器还未实现，于是我们打印了进入消息过滤器的消息ID值以观察消息的流动情况。</p>
<p>以下是我们的Widget驱动类，我们将其放入了一个名为widget的名字空间中，以后我们widget相关的名字都会放入到这个名字空间中。</p>
<div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">class</span><span style="color: #000000;"> DriverImpl;<br><br></span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Driver{<br>    DriverImpl</span><span style="color: #000000;">*</span><span style="color: #000000;"> pImpl_;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>    </span><span style="color: #0000ff;">explicit</span><span style="color: #000000;"> Driver(HWND hWnd);<br>    </span><span style="color: #000000;">~</span><span style="color: #000000;">Driver();<br>            <br></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>    Driver(</span><span style="color: #0000ff;">const</span><span style="color: #000000;"> Driver</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">);<br>    Driver</span><span style="color: #000000;">&amp;</span><span style="color: #000000;"> </span><span style="color: #0000ff;">operator</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;">(</span><span style="color: #0000ff;">const</span><span style="color: #000000;"> Driver</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">);<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>    inline HWND GetContainerWindow() </span><span style="color: #0000ff;">const</span><span style="color: #000000;">;<br>};</span></div></pre>
</div>
<p>因为Driver的实现我们并不关心，所以我们将其实现进行了一个隐藏，这样也便于我们修改其实现方式。Driver类对象要求用于构造它的窗口句柄必须为有效的窗口句柄，并且每个窗口句柄只能被驱动一次，所以我们在调试版本中做了断言来约束我们的编码，在发布版本中不会做任何判断。</p>
<div class="cnblogs_code">
<pre><div><span style="color: #000000;">#ifdef _DEBUG<br>    assert(::IsWindow(hContainerWnd_));<br>    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 不能多次驱动同一窗口</span><span style="color: #008000;"><br></span><span style="color: #000000;">    assert(GetContainerWindows_().insert(hContainerWnd_).second);<br></span><span style="color: #0000ff;">#endif</span><span style="color: #000000;"> </span><span style="color: #008000;">//</span><span style="color: #008000;"> _DEBUG</span></div></pre>
</div>
此处有一个<span style="color: #ff0000;">GetContainerWindows_()</span><span>是一个只在调试版本中才有的实现，其返回一个std::set&lt;HWND&gt;&amp;静态对象引用，用于保存已经被驱动的窗口句柄，我们断言窗口句柄未曾保存到这个set之中。</span>
<p>现在我们实现的Driver接口非常简单，只有一个构造接口和查询其驱动的窗口句柄的接口，显然没有任何可以控制驱动或者解除驱动的机会，此处我们先放一放，因为这在以后会涉及到这个驱动所关联的Widget体系的一些问题。</p>
<p>通过thunk截获的窗口消息将会进入到Driver实现中，Driver的功能仅仅是作为Widget的驱动（也就是消息驱动），它不负责任何消息的处理，所以这个窗口过程在截获到窗口消息后立即交由消息过滤处理。</p>
<div class="cnblogs_code">
<pre><div><span style="color: #000000;">LRESULT WndProc_(UINT message, WPARAM wParam, LPARAM lParam)<br>{<br>    </span><span style="color: #008000;">//</span><span style="color: #008000;"> 进行消息过滤</span><span style="color: #008000;"><br></span><span style="color: #000000;">    MessageFilter::Param param;<br>    param.hWnd </span><span style="color: #000000;">=</span><span style="color: #000000;"> hContainerWnd_;<br>    param.originalProc </span><span style="color: #000000;">=</span><span style="color: #000000;"> originalProc_;<br>    param.message </span><span style="color: #000000;">=</span><span style="color: #000000;"> message;<br>    param.wParam </span><span style="color: #000000;">=</span><span style="color: #000000;"> wParam;<br>    param.lParam </span><span style="color: #000000;">=</span><span style="color: #000000;"> lParam;<br>    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> MessageFilter::Filter(param);<br>}</span></div></pre>
</div>
<p>这里消息过滤器的实现不在这一段讨论之中，所以我们简单的以一个类静态接口来作为过滤入口。</p>
<p>好了，我们到这里已经开启了Widget内核的运作系统的实现，从测试工程中感受得到一定的体验了。</p>
<p><span style="font-size: 18pt;"><strong><a href="http://files.cnblogs.com/EvilGhost/%E5%85%B1%E4%BA%AB(3).zip"><strong>下载</strong>测试工程源码</a></strong></span></p><img src="http://www.cnblogs.com/EvilGhost/aggbug/2004381.html?type=1" width="1" height="1" alt=""><p>作者: <a href="http://www.cnblogs.com/EvilGhost/" target="_blank">Evil.Ghost</a> 发表于 2011-04-03 13:56 <a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/03/Abstract_Widget_3.html" target="_blank">原文链接</a></p><p>评论: 0　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/03/Abstract_Widget_3.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/03/Abstract_Widget_3.html#commentform" target="_blank">发表评论</a></p><hr><p>最新新闻：<br>&#183; <a href="http://news.cnblogs.com/n/96900/" target="_blank">苹果iPad 2通过3C认证 最晚5月国内上市</a><span style="color:gray">(2011-04-10 09:18)</span><br>&#183; <a href="http://news.cnblogs.com/n/96899/" target="_blank">盲目依赖iPhone等工具导航 英国驴友迷路多</a><span style="color:gray">(2011-04-10 09:14)</span><br>&#183; <a href="http://news.cnblogs.com/n/96898/" target="_blank">趣谈：想担任CEO的话，最好是去苹果工作，其次是微软，再才是Google</a><span style="color:gray">(2011-04-10 08:26)</span><br>&#183; <a href="http://news.cnblogs.com/n/96897/" target="_blank">腾讯将建立新数据中心，规模为苹果的两倍</a><span style="color:gray">(2011-04-10 08:25)</span><br>&#183; <a href="http://news.cnblogs.com/n/96896/" target="_blank">轻量化的微型博客Tumblr</a><span style="color:gray">(2011-04-10 08:03)</span><br></p><p>编辑推荐：<a href="http://www.cnblogs.com/xiaotie/archive/2011/04/09/2010198.html" target="_blank">非战之罪，从永中Office谈起</a><br></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">我的园子</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p><img src ="http://www.cppblog.com/vivence/aggbug/143843.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vivence/" target="_blank">EvilGhost</a> 2011-04-03 13:56 <a href="http://www.cppblog.com/vivence/archive/2011/04/03/Abstract_Widget_3.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一步一步实现自己的模拟控件（2）——窗口过程thunk</title><link>http://www.cppblog.com/vivence/archive/2011/04/03/Abstract_Widget_2.html</link><dc:creator>EvilGhost</dc:creator><author>EvilGhost</author><pubDate>Sun, 03 Apr 2011 05:55:00 GMT</pubDate><guid>http://www.cppblog.com/vivence/archive/2011/04/03/Abstract_Widget_2.html</guid><wfw:comment>http://www.cppblog.com/vivence/comments/143844.html</wfw:comment><comments>http://www.cppblog.com/vivence/archive/2011/04/03/Abstract_Widget_2.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/vivence/comments/commentRss/143844.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vivence/services/trackbacks/143844.html</trackback:ping><description><![CDATA[
<p>&#160;</p><p><strong>实现Window Proc Thunk：</strong></p>
<p>就好像水泵一样，我们的系统也需要一个泵——消息泵，也就是前面图上的Message Driver。这个Driver的可以依靠一个Window Proc Thunk来截获窗口消息，那么我们得先实现一个Window Proc Thunk。</p>
<p>这里我选择的方法是拿现成的代码来用，微软ATL框架中提供有thunk的实现，我将其提取出来稍加修改就能为我们所用了。</p>
<p>以下是其实现的一点代码片段：</p>
<div class="cnblogs_code">
<pre><div><span style="color: #0000ff;">void</span><span style="color: #000000;">*</span><span style="color: #000000;"> __stdcall AllocStdCallThunk(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">);<br>    </span><span style="color: #0000ff;">void</span><span style="color: #000000;">  __stdcall FreeStdCallThunk(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">*</span><span style="color: #000000;">);<br><br></span><span style="color: #0000ff;">#pragma</span><span style="color: #000000;"> pack(push, 1)</span><span style="color: #000000;"><br>    </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> StdCallThunk_{<br>        DWORD mov_;         </span><span style="color: #008000;">//</span><span style="color: #008000;"> mov dword ptr [esp+0x4], this_ (esp+0x4就是第一个参数)</span><span style="color: #008000;"><br></span><span style="color: #000000;">        DWORD this_;        </span><span style="color: #008000;">//<br></span><span style="color: #000000;">        BYTE jmp_;          </span><span style="color: #008000;">//</span><span style="color: #008000;"> jmp proc</span><span style="color: #008000;"><br></span><span style="color: #000000;">        DWORD relproc_;     </span><span style="color: #008000;">//</span><span style="color: #008000;"> relative jmp</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>    </span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>        </span><span style="color: #0000ff;">bool</span><span style="color: #000000;"> Init(DWORD_PTR proc, </span><span style="color: #0000ff;">void</span><span style="color: #000000;">*</span><span style="color: #000000;"> pThis)<br>        {<br>            mov_ </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0x042444C7</span><span style="color: #000000;">;  </span><span style="color: #008000;">//</span><span style="color: #008000;">C7 44 24 0C</span><span style="color: #008000;"><br></span><span style="color: #000000;">            this_ </span><span style="color: #000000;">=</span><span style="color: #000000;"> PtrToUlong(pThis);<br>            jmp_ </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">0xe9</span><span style="color: #000000;">;<br>            relproc_ </span><span style="color: #000000;">=</span><span style="color: #000000;"> DWORD((INT_PTR)proc </span><span style="color: #000000;">-</span><span style="color: #000000;"> ((INT_PTR)</span><span style="color: #0000ff;">this</span><span style="color: #000000;">+</span><span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(StdCallThunk_)));<br>            </span><span style="color: #008000;">//</span><span style="color: #008000;"> 用自身更新指令缓存</span><span style="color: #008000;"><br></span><span style="color: #000000;">            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> ::FlushInstructionCache(::GetCurrentProcess(), </span><span style="color: #0000ff;">this</span><span style="color: #000000;">, </span><span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(StdCallThunk_)) </span><span style="color: #000000;">?</span><span style="color: #000000;"> </span><span style="color: #0000ff;">true</span><span style="color: #000000;"> : </span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>        }<br>        </span><span style="color: #008000;">//</span><span style="color: #008000;"> 某些thunk将动态的为代码分配内存</span><span style="color: #008000;"><br></span><span style="color: #000000;">        </span><span style="color: #0000ff;">void</span><span style="color: #000000;">*</span><span style="color: #000000;"> GetCodeAddress()<br>        {<br>            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">this</span><span style="color: #000000;">;<br>        }<br>        </span><span style="color: #0000ff;">void</span><span style="color: #000000;">*</span><span style="color: #000000;"> </span><span style="color: #0000ff;">operator</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;">(size_t)<br>        {<br>            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> AllocStdCallThunk();<br>        }<br>        </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> </span><span style="color: #0000ff;">operator</span><span style="color: #000000;"> delete(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">*</span><span style="color: #000000;"> pThunk)<br>        {<br>            FreeStdCallThunk(pThunk);<br>        }<br>    };<br></span><span style="color: #0000ff;">#pragma</span><span style="color: #000000;"> pack(pop)</span></div></pre>
</div>
<p><span style="font-size: 18pt;"><strong><a href="http://files.cnblogs.com/EvilGhost/%E5%85%B1%E4%BA%AB(2).zip">下载测试工程源码</a></strong></span></p><img src="http://www.cnblogs.com/EvilGhost/aggbug/2001485.html?type=1" width="1" height="1" alt=""><p>作者: <a href="http://www.cnblogs.com/EvilGhost/" target="_blank">Evil.Ghost</a> 发表于 2011-04-03 13:55 <a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/03/Abstract_Widget_2.html" target="_blank">原文链接</a></p><p>评论: 0　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/03/Abstract_Widget_2.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/03/Abstract_Widget_2.html#commentform" target="_blank">发表评论</a></p><hr><p>最新新闻：<br>&#183; <a href="http://news.cnblogs.com/n/96900/" target="_blank">苹果iPad 2通过3C认证 最晚5月国内上市</a><span style="color:gray">(2011-04-10 09:18)</span><br>&#183; <a href="http://news.cnblogs.com/n/96899/" target="_blank">盲目依赖iPhone等工具导航 英国驴友迷路多</a><span style="color:gray">(2011-04-10 09:14)</span><br>&#183; <a href="http://news.cnblogs.com/n/96898/" target="_blank">趣谈：想担任CEO的话，最好是去苹果工作，其次是微软，再才是Google</a><span style="color:gray">(2011-04-10 08:26)</span><br>&#183; <a href="http://news.cnblogs.com/n/96897/" target="_blank">腾讯将建立新数据中心，规模为苹果的两倍</a><span style="color:gray">(2011-04-10 08:25)</span><br>&#183; <a href="http://news.cnblogs.com/n/96896/" target="_blank">轻量化的微型博客Tumblr</a><span style="color:gray">(2011-04-10 08:03)</span><br></p><p>编辑推荐：<a href="http://www.cnblogs.com/xiaotie/archive/2011/04/09/2010198.html" target="_blank">非战之罪，从永中Office谈起</a><br></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">我的园子</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p><img src ="http://www.cppblog.com/vivence/aggbug/143844.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vivence/" target="_blank">EvilGhost</a> 2011-04-03 13:55 <a href="http://www.cppblog.com/vivence/archive/2011/04/03/Abstract_Widget_2.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一步一步实现自己的模拟控件（1）——消息驱动设计</title><link>http://www.cppblog.com/vivence/archive/2011/04/03/Abstract_Widget_1.html</link><dc:creator>EvilGhost</dc:creator><author>EvilGhost</author><pubDate>Sun, 03 Apr 2011 05:53:00 GMT</pubDate><guid>http://www.cppblog.com/vivence/archive/2011/04/03/Abstract_Widget_1.html</guid><wfw:comment>http://www.cppblog.com/vivence/comments/143845.html</wfw:comment><comments>http://www.cppblog.com/vivence/archive/2011/04/03/Abstract_Widget_1.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/vivence/comments/commentRss/143845.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vivence/services/trackbacks/143845.html</trackback:ping><description><![CDATA[
<p>&#160;</p><p><strong>目标：</strong></p>
<p>实现一套windows下简单且可扩展的抽象模拟控件内核。</p>
<p><strong>抽象模拟控件：</strong></p>
<p>我们将windows系统的窗口客户区作为模拟控件的容器，每个模拟控件就是其中的一个抽象矩形区域，并且能够模拟控件进行递归嵌套。控件支持基本的鼠标和键盘消息，能够在窗口大小改变时进行布局。</p>
<p><strong>分析：</strong></p>
<p>因为我们要实现的是抽象的模拟控件，不是真正的窗口控件，那么就需要将窗口消息适时派发到相应控件。下面以一个图来规划窗口消息的流向。</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/EvilGhost/201103/201103302107362624.png"><img height="471" width="660" src="http://images.cnblogs.com/cnblogs_com/EvilGhost/201103/201103302107388838.png" alt="Abstract Widget" border="0" title="Abstract Widget" style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;"></a></p>
<p>在这个图上我们可以看到除了我们的控件体系以外还需要实现几个核心模块来驱动控件。他们分别是：消息驱动、消息过滤、消息转换。消息驱动通过某种方法截获窗口消息（图上使用thunk），然后将窗口消息交给过滤器进行过滤（因为模拟控件不是所有窗口消息都感兴趣），消息过滤出控件感兴趣的消息交给消息转换器，其他消息交还给窗口的原有窗口过程进行处理（<strong>此处要注意的是消息过滤后控件感兴趣的消息不会再回到原窗口过程</strong>）。消息转换器将窗口消息转换为控件消息，之所以要进行转换是因为控件体系可能支持的消息系统会有别于窗口消息，也可能需要进行坐标映射等。</p>
<p>图上还能看到，我们的控件是能够递归嵌套的。每个窗口对应一个根控件，所有的控件都是在这个根组件之中的。为了要能够使用窗口的整个客户区，那么根组件所处区域就是窗口的整个客户区。</p>
<p>现在我们要开始进行设计了，设计过程中将引入代码，代码将随着设计的演化进行演化，最终形成我们这个简单且可扩展的抽象模拟控件核心。</p><img src="http://www.cnblogs.com/EvilGhost/aggbug/2000310.html?type=1" width="1" height="1" alt=""><p>作者: <a href="http://www.cnblogs.com/EvilGhost/" target="_blank">Evil.Ghost</a> 发表于 2011-04-03 13:53 <a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/03/Abstract_Widget_1.html" target="_blank">原文链接</a></p><p>评论: 0　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/03/Abstract_Widget_1.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/EvilGhost/archive/2011/04/03/Abstract_Widget_1.html#commentform" target="_blank">发表评论</a></p><hr><p>最新新闻：<br>&#183; <a href="http://news.cnblogs.com/n/96900/" target="_blank">苹果iPad 2通过3C认证 最晚5月国内上市</a><span style="color:gray">(2011-04-10 09:18)</span><br>&#183; <a href="http://news.cnblogs.com/n/96899/" target="_blank">盲目依赖iPhone等工具导航 英国驴友迷路多</a><span style="color:gray">(2011-04-10 09:14)</span><br>&#183; <a href="http://news.cnblogs.com/n/96898/" target="_blank">趣谈：想担任CEO的话，最好是去苹果工作，其次是微软，再才是Google</a><span style="color:gray">(2011-04-10 08:26)</span><br>&#183; <a href="http://news.cnblogs.com/n/96897/" target="_blank">腾讯将建立新数据中心，规模为苹果的两倍</a><span style="color:gray">(2011-04-10 08:25)</span><br>&#183; <a href="http://news.cnblogs.com/n/96896/" target="_blank">轻量化的微型博客Tumblr</a><span style="color:gray">(2011-04-10 08:03)</span><br></p><p>编辑推荐：<a href="http://www.cnblogs.com/xiaotie/archive/2011/04/09/2010198.html" target="_blank">非战之罪，从永中Office谈起</a><br></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">我的园子</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p><img src ="http://www.cppblog.com/vivence/aggbug/143845.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vivence/" target="_blank">EvilGhost</a> 2011-04-03 13:53 <a href="http://www.cppblog.com/vivence/archive/2011/04/03/Abstract_Widget_1.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>