0

在这段代码中:

unit MSEC;

interface

uses
  Winapi.Windows, Vcl.Dialogs, Vcl.ExtCtrls, System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls;

type
  TMSEC = class(TWinControl)
  private
    FOpr                  :TComboBox;
  public
    constructor Create(AOwner: TComponent); override;
  end;

implementation

const
    DEF_OPERATIONS :array[0..3] of Char = ('+', '-', '*', '/');

constructor TMSEC.Create(AOwner: TComponent);
var i         :Integer;
begin
  inherited;
  FOpr:= TComboBox.Create(Self);
  with FOpr do begin
    Parent:= Self;
    Align:= alLeft;
    Width:= DEF_OPERATIONS_WIDTH;
    Style:= csDropDownList;
    //error in next lines :
    Items.Clear;
    for i := Low(DEF_OPERATIONS) to High(DEF_OPERATIONS) do Items.Add(DEF_OPERATIONS[i]);
    ItemIndex:= 0;  
  end;
end;

end.

当我更改 ComboBox 项目时,程序中断并显示消息:
'Control' has no parent。
如何修复此错误或以其他方式初始化 ComboBox 项?

4

2 回答 2

8

TComboBox需要分配的 HWND 才能将字符串存储在其Items属性中。为了TComboBox获得 HWND,它Parent首先需要一个 HWND,然后它Parent需要一个 HWND,依此类推。问题是您的TMSEC对象Parent在其构造函数运行时尚未分配,因此无法TComboBox获得 HWND,因此出现错误。

试试这个:

type
  TMSEC = class(TWinControl)
  private
    FOpr: TComboBox;
  protected
    procedure CreateWnd; override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

constructor TMSEC.Create(AOwner: TComponent);
begin
  inherited;
  FOpr := TComboBox.Create(Self);
  with FOpr do begin
    Parent := Self;
    Align := alLeft;
    Width := DEF_OPERATIONS_WIDTH;
    Style := csDropDownList;
    Tag := 1;
  end;
end;

procedure TMSEC.CreateWnd;
var
  i :Integer;
begin
  inherited;
  if FOpr.Tag = 1 then
  begin
    FOpr.Tag := 0;
    for i := Low(DEF_OPERATIONS) to High(DEF_OPERATIONS) do
      FOpr.Items.Add(DEF_OPERATIONS[i]);
    FOpr.ItemIndex := 0;
  end;
end;
于 2013-09-24T21:31:54.537 回答
0

Remy 很好地解释了这个问题,但对于更通用的解决方案,您可以创建 TComboBox 的后代,例如:

type
  TComboBoxSafe = class(TComboBox)
  strict private
    FSafeItemIndex: Integer;
    FSafeItems: TArray<string>;
    function GetSafeItemIndex: Integer;
    function GetSafeItems: TArray<string>;
    procedure SetSafeItemIndex(const AValue: Integer);
    procedure SetSafeItems(const AValue: TArray<string>);
  strict protected
    procedure CreateWnd; override;
    procedure DestroyWnd; override;
  public
    constructor Create(AOwner: TComponent); override;
    property SafeItemIndex: Integer read GetSafeItemIndex write SetSafeItemIndex;
    property SafeItems: TArray<string> read GetSafeItems write SetSafeItems;
  end;

{ TComboBoxSafe }

constructor TComboBoxSafe.Create(AOwner: TComponent);
begin
  inherited;
  FSafeItemIndex := -1;
end;

procedure TComboBoxSafe.CreateWnd;
var
  LOnChange: TNotifyEvent;
begin
  inherited;
  LOnChange := OnChange;
  OnChange := nil;
  try
    Items.Text := string.Join(sLineBreak, FSafeItems);
    ItemIndex := FSafeItemIndex;
  finally
    OnChange := LOnChange;
  end;
end;

procedure TComboBoxSafe.DestroyWnd;
begin
  FSafeItemIndex := ItemIndex;
  FSafeItems := Items.ToStringArray;
  inherited;
end;

function TComboBoxSafe.GetSafeItemIndex: Integer;
begin
  if WindowHandle <> 0 then
    Result := ItemIndex
  else
    Result := FSafeItemIndex;
end;

function TComboBoxSafe.GetSafeItems: TArray<string>;
begin
  if WindowHandle <> 0 then
    Result := Items.ToStringArray
  else
    Result := FSafeItems;
end;

procedure TComboBoxSafe.SetSafeItemIndex(const AValue: Integer);
begin
  if WindowHandle <> 0 then
    ItemIndex := AValue
  else
    FSafeItemIndex := AValue;
end;

procedure TComboBoxSafe.SetSafeItems(const AValue: TArray<string>);
begin
  if WindowHandle <> 0 then
    Items.Text := string.Join(sLineBreak, AValue)
  else
    FSafeItems := AValue;
end;
于 2022-01-25T19:29:57.223 回答