'"Control has no parent" in Create ComboBox

In this code :

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.

When I change ComboBox items, the program breaks with the message :
'Control' has no parent.
How can I fix this error or initialize ComboBox items in another way?



Solution 1:[1]

Remy explained the problem well, but for a more general solution, you could create a descendant of TComboBox, for example:

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;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 vfbb