【问题标题】:C++ Builder derived TComboBox to have Items by defaultC++ Builder 派生的 TComboBox 默认具有 Items
【发布时间】:2015-09-22 07:35:19
【问题描述】:

这里是 VCL 组件新手,如果这是一个愚蠢的问题,请原谅我......

我正在尝试制作一个包含默认项目的 TComboBox 组件,然后将其拖放到表单上,即一个 TMonthComboBox,当拖放到表单上时,其项目列表中将包含月份列表。

我发现在构造过程中尝试访问 Items 属性会导致“Control '' has no parent window”错误,如果我尝试将此类组合框拖放到表单上。 p>

这里是(部分)构造函数:

__fastcall TMonthCombo::TMonthCombo(TComponent *Owner):TComboBox(Owner)
{
    this->Style = csDropDownList; // this is okay
    this->Items->Add("January"); // This is causing problem
}

我认为问题是由于 Items 属性在构造的这个时候还不可用。

是否有办法确保组件已准备好在组件源代码本身内将值接受到其 Items 属性中(即在设计时不在属性编辑器中添加列表项)?

在有人告诉我“只需在运行时将项目添加到您的应用程序代码中”之前,我必须解释一下,这个 ComboBox 将在很多地方非常频繁地使用,而月份选择只是我用来解释的一个简单示例问题是,我想放在 ComboBox 中的实际值要多样化得多,而且大多数时候是动态的。它还必须以多种方式响应用户的选择。

我已经尝试过运行时的方式,但它变得非常乏味。这就是我将它制作成一个组件的原因,这样它就可以自行处理,而无需我重复输入多个版本的代码来填充 ComboBox。

感谢您的帮助。

编辑:在尝试了 manlio 的解决方案后,ComboBox 在运行时看起来很奇怪:

ComboBox 在运行时具有双图像。我做错了什么?

__fastcall TYearCombo::TYearCombo(TComponent* Owner) : TComboBox(Owner), init_items(true)
{

}
//---------------------------------------------------------------------------
void __fastcall TYearCombo::CreateWnd()
{
    unsigned short yr, mn, dy;

    this->Width = 90;
    this->Style = csDropDownList;
    this->DropDownCount = 11;
    TDate d = Today();
    d.DecodeDate(&yr, &mn, &dy);
    year = yr;

    if (init_items)
    {
        init_items = false;
        TComboBox::CreateWnd();
        Items->BeginUpdate();
        for(int i=year-5; i<=year+5; i++)
        {
            Items->Add(IntToStr(i));
        }
        this->ItemIndex = 5;
        Items->EndUpdate();
    }
}
//---------------------------------------------------------------------------

【问题讨论】:

标签: c++ c++builder vcl tcombobox


【解决方案1】:

你可以试试这个:

  1. 覆盖CreateWnd虚方法并添加init_items私有数据成员:

    class TMonthCombo : public TComboBox
    {
    // ...
    
    protected:
      virtual void __fastcall CreateWnd();
    
    private:
      bool init_items;
    
    // ...
    };
    
  2. 设置init_items 标志:

    TMonthCombo::TMonthCombo(TComponent *Owner) : TComboBox(Owner),
                                                  init_items(true)
    {
      // ...
    }
    
  3. CreateWnd 内可以添加新项目:

    void __fastcall TMonthCombo::CreateWnd()
    {
      TComboBox::CreateWnd();
    
      if (init_items)
      {
        init_items = false;
        Items->BeginUpdate();
        Items->Add("January");
        // ...
        Items->EndUpdate();
      }
    }
    

补充说明:

  • "Control has no parent" in Create ComboBoxTComboBox 需要分配的 HWND 以便将字符串存储在其 Items 属性中)。
  • 只需将构造函数的Owner 参数转换为TWinControl 并将结果分配给组件的Parent 属性不是解决方案

    TMonthCombo::TMonthCombo(TComponent *Owner) : TComBoBox(Owner)
    {
      Parent = static_cast<TWinControl *>(Owner);
      // ...
    }
    

    赋值解决了“控件没有父窗口错误”,但产生了另一个问题:表单始终是组件的父级(您不能将表单添加到不同的容器中)。

【讨论】:

  • 感谢您对我的代码的回答和更正。您建议的方式有效,但产生了一个奇怪的现象 - ComboBox 在运行时具有双重图像。我编辑了我的原始帖子以包含屏幕截图和代码。
  • TMonthCombo::CreateWnd() 必须调用TComboBox::CreateWnd(),而不管init_items 的值如何。如当前所示,如果在TMonthCombo 或(祖)父控件上调用RecreateWnd()TMonthCombo 将失去其HWND 并且永远不会再创建新的HWND
  • 另外,NEVER NEVER NEVER 在构造函数中分配Parent 属性!调用者有责任在构建完成后设置适当的ParentParent 可能与 Owner 不同(尤其是对于从 DFM 流式传输的组件),Owner 甚至可能不是 TWinControl 的后代。如果您想检测何时分配了Parent,请改写虚拟SetParent() 方法。
  • @RemyLebeau 感谢您对CreateWnd() 的注释,我已经修复了呼叫点。关于Parent 属性,显示的代码用于解释为什么分配不是解决方案。
猜你喜欢
  • 2018-08-11
  • 2019-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-24
  • 1970-01-01
  • 2011-01-10
  • 2018-04-11
相关资源
最近更新 更多