使用listbox的内嵌控件

Listbox支持内嵌控件是Nana 1.1中引入的新功能。Listbox提供了一个接口类inline_notifier_interface,用于定义一套内嵌控件的行为,并与指定category的某个column相关联。

下面的例子将演示创建内嵌控件的方法。

using namespace nana;

//创建一个textbox和一个button
//textbox显示某个listbox项中的文本值
//button用于删除本项
class inline_widget : public listbox::inline_notifier_interface
{
private:
    //创建内嵌控件。listbox会调用该方法创建内嵌控件
    //这里不必处理控件的位置和大小。
    virtual void create(window wd) override
    {
        //创建文本框
        txt_.create(wd);
        txt_.events().click([this]
        {
            //当点击文本框时,选中本项
            indicator_->selected(pos_);
        });

        txt_.events().mouse_move([this]
        {
            //当鼠标在文本框上移动时,需要高亮本项
            indicator_->hovered(pos_);
        });

        txt_.events().key_char([this](const arg_keyboard& arg)
        {
            if (arg.key == keyboard::enter)
            {
                //在文本框里按下回车,则修改本项的值
                arg.ignore = true;
                indicator_->modify(pos_, txt_.caption());
            }
        });

        //创建按钮
        btn_.create(wd);
        btn_.caption(L"Delete");
        btn_.events().click([this]
        {
            //点击按钮时,删除本项
            auto & lsbox = dynamic_cast<listbox&>(indicator_->host());
            lsbox.erase(lsbox.at(pos_));
        });

        btn_.events().mouse_move([this]
        {
            //当鼠标在按钮上移动时,高亮本项
            indicator_->hovered(pos_);
        });
    }

    //激活该inline_widget对象,与listbox中的某项关联起来。
    //inline_indicator是与listbox相关的对象,可以通过它去操作listbox,
    //在create()函数里创建那些回调处理时,就会使用到该对象
    //pos表示listbox中关联的项。
    virtual void activate(inline_indicator& ind, index_type pos)
    {
        indicator_ = &ind;
        pos_ = pos;
    }

    //调整内嵌控件的尺寸
    //dimension指定控件能调整的最大尺寸
    //坐标为逻辑坐标,因此不必考虑控件在listbox中的绝对位置
    void resize(const size& dimension) override
    {
        auto sz = dimension;
        sz.width -= 50;
        txt_.size(sz);

        rectangle r(sz.width + 5, 0, 45, sz.height);
        btn_.move(r);
    }

    //设置值,即listbox将本项的值设置给内嵌控件
    virtual void set(const value_type& value)
    {
        txt_.caption(value);
    }

    //决定listbox是否绘制该项的值,例如内嵌控件将该项背景
    //完全遮挡,就可以返回false,告知listbox不用再绘制内容
    bool whether_to_draw() const override
    {
        return true;
    }
private:
    inline_indicator * indicator_{ nullptr };
    index_type pos_;
    textbox txt_;
    button btn_;
};

然后使用这个inline_widget

int main()
{
    using namespace nana;

    form fm;
    listbox lsbox(fm, rectangle{ 10, 10, 300, 200 });

    //创建两个column
    lsbox.append_header(L"column 0");
    lsbox.append_header(L"column 1");

    //然后添加内容
    lsbox.at(0).append({ nana::string(L"Hello0"), nana::string(L"World0") });
    lsbox.at(0).append({ nana::string(L"Hello1"), nana::string(L"World1") });
    lsbox.at(0).append({ nana::string(L"Hello2"), nana::string(L"World2") });
    lsbox.at(0).append({ nana::string(L"Hello3"), nana::string(L"World3") });

    //新建一个category
    lsbox.append(L"Category 1");

    //继续添加内容
    lsbox.at(1).append({ nana::string(L"Hello4"), nana::string(L"World4") });
    lsbox.at(1).append({ nana::string(L"Hello5"), nana::string(L"World5") });
    lsbox.at(1).append({ nana::string(L"Hello6"), nana::string(L"World6") });
    lsbox.at(1).append({ nana::string(L"Hello7"), nana::string(L"World7") });

    //然后为两个category设置inline_widget,分别设置第一个column和第二个column
    lsbox.at(0).inline_factory(0, pat::make_factory<inline_widget>());
    lsbox.at(1).inline_factory(1, pat::make_factory<inline_widget>());

    fm.show();
    exec();
}

运行效果
这里写图片描述

需要注意的是listbox会在内部管理这个inline_widget对象,一个inline_widget对象不会与listbox中的某个item一直关联着,listbox最多只创建能满足显示的个数,例如界面上只能显示10个item,那么最多就只有10个inline_widget实例。而当用户拖动滚动条显示其他的item时,这10个inline_widget实例会重新关联到新的item上(即listbox调用activate方法)。因此在inline_widget类中,不要永久保存某项相关的数据。 

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页