Forum > General

[Solved]Naming error for self written component

(1/1)

dangersgromit:
Hello.

I use fpc and lazarus extensively for accessing databases presenting the data in tables and list.
I wrote an extended filter for a TListview which is based on the TEditButton (like TListViewFilter)-

After using this for while I had the idea to make is real component. I want to drag it from the components bar and use the property editor to customize it.
So i followed the tutorial https://wiki.freepascal.org/How_To_Write_Lazarus_Component.

The package compiled without errors. I installed the package into the IDE, which worked fine.

But when I want to add the component to the form, I get an odd error message:


--- Quote ---Error naming component

Error setting the name of the component
KMListViewFilterEdit1:TKMListViewFilterEdit to
KMListViewFilter1
--- End quote ---

I'm currently using Lazarus 3.2 (rev lazarus_3_2) FPC 3.2.2 x86_64-win64-win32/win64.

I'm sure the code is not error-free, that's not the point - I changed a few routines when porting it to a component, so it's relatively untested  ;D.
I just want to know how to get rid of the error that prevents me from using the new component at all. I just can't figure out what i did wrong when creating the package.

Greetings Klaus


--- Code: XML  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---<?xml version="1.0" encoding="UTF-8"?><CONFIG>  <Package Version="5">    <PathDelim Value="\"/>    <Name Value="KMControls"/>    <Type Value="RunAndDesignTime"/>    <CompilerOptions>      <Version Value="11"/>      <PathDelim Value="\"/>      <SearchPaths>        <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)\"/>      </SearchPaths>    </CompilerOptions>    <Files>      <Item>        <Filename Value="ukmlistviewfilter.pas"/>        <HasRegisterProc Value="True"/>        <UnitName Value="uKMListViewFilter"/>      </Item>    </Files>    <RequiredPkgs>      <Item>        <PackageName Value="IDEIntf"/>      </Item>    </RequiredPkgs>    <UsageOptions>      <UnitPath Value="$(PkgOutDir)"/>    </UsageOptions>    <PublishOptions>      <Version Value="2"/>      <UseFileFilters Value="True"/>    </PublishOptions>  </Package></CONFIG>

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---{ This file was automatically created by Lazarus. Do not edit!  This source is only used to compile and install the package. } unit KMControls; {$warn 5023 off : no warning about unused units}interface uses  uKMListViewFilter, LazarusPackageIntf; implementation procedure Register;begin  RegisterUnit('uKMListViewFilter', @uKMListViewFilter.Register);end; initialization  RegisterPackage('KMControls', @Register);end.  

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit uKMListViewFilter; {$mode ObjFPC}{$H+} interface uses  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, EditBtn, ComCtrls, FGL, LCLStrConsts; type   TKMListViewFilterCustomMatch = Function (SearchString : String; MatchAll : Boolean = false; CaseSensitive: Boolean = true) : Boolean of Object;   { TKMListViewFilterEntry }   TKMListViewFilterEntry = Class  private    FItems : TStringList;    FStateIndex : Integer;    FData : Pointer;    FCustomMatch : TKMListViewFilterCustomMatch;  public    constructor Create;    destructor Destroy; override;    function Match (SearchString : String; MatchAll : Boolean = false; CaseSensitive: Boolean = true) : Boolean;    property Items : TStringList read FItems;    property StateIndex : Integer read FStateIndex write FStateIndex;    property Data : Pointer read FData write FData;    property CustomMatch : TKMListViewFilterCustomMatch read FCustomMatch write FCustomMatch;  end;   TCustomKMListViewFilterEntries = specialize TFPGList<TKMListViewFilterEntry>;  TKMListViewFilterEntries = Class (TCustomKMListViewFilterEntries);   { TKMCustomListViewFilter }   TKMCustomListViewFilter = class(TCustomEditButton)  private    FListEntries : TKMListViewFilterEntries;    FListView : TListView;    FMatchAll : Boolean;    FFilterOptions : TFilterStringOptions;    FSortData: Boolean;    procedure SetCustomMatch(AValue: TkmListViewFilterCustomMatch);    procedure SetFilteredListView(AValue: TListView);    procedure SetFilterOptions(AValue: TFilterStringOptions);    procedure SetMatchAll(AValue: Boolean);    procedure SetSortData(AValue: Boolean);    function IsTextHintStored : Boolean;  protected    FCustomMatch : TKMListViewFilterCustomMatch;    FAfterFilter : TNotifyEvent;    function GetDefaultGlyphName: string; override;    function GetCount : Integer;    procedure BuddyClick; override;    procedure EditChange (Sender : TObject); overload;  public    constructor Create (aOwner : TComponent); override;    destructor Destroy; override;    procedure Reload;    procedure RemoveFilter;    procedure Filter;  published    property FFilteredListView : TListView read FListView write SetFilteredListView;    property MatchAll : Boolean read FMatchAll write SetMatchAll default false;    property SortData : Boolean read FSortData write SetSortData;    property OriginalCount : Integer read GetCount;    property FilterOptions: TFilterStringOptions read fFilterOptions write SetFilterOptions default [];    property OnAfterFilter : TNotifyEvent read FAfterFilter write FAfterFilter;    property OnCustomMatch : TkmListViewFilterCustomMatch read FCustomMatch write SetCustomMatch;    property TextHint stored IsTextHintStored;  end;   TKMListViewFilterEdit = Class (TKMCustomListViewFilter)  public    property AutoSelected;    property Button;    property Edit;    property OnChange;  published    property NumbersOnly;    property Action;    property AutoSelect;    property AutoSize default True;    property Align;    property Alignment;    property Anchors;    property BiDiMode;    property BorderSpacing;    property BorderStyle default bsNone;    property ButtonCaption;    property ButtonCursor;    property ButtonHint;    property ButtonOnlyWhenFocused;    property ButtonWidth;    property CharCase;    property Color;    property Constraints;    property Cursor;    property DirectInput;    property EchoMode;    property Enabled;    property Flat;    property FocusOnButtonClick;    property Font;    property Glyph;//    property HideSelection;    property Hint;    property Images;    property ImageIndex;    property ImageWidth;    property Layout;    property MaxLength;    property NumGlyphs;    property OnButtonClick;    property OnClick;    property OnDblClick;    property OnDragDrop;    property OnDragOver;    property OnContextPopup;    property OnEditingDone;    property OnEndDrag;    property OnEnter;    property OnExit;    property OnKeyDown;    property OnKeyPress;    property OnKeyUp;    property OnMouseDown;    property OnMouseEnter;    property OnMouseLeave;    property OnMouseMove;    property OnMouseUp;    property OnMouseWheel;    property OnMouseWheelDown;    property OnMouseWheelUp;    property OnStartDrag;    property OnUTF8KeyPress;    property ParentBiDiMode;    property ParentColor;    property ParentFont;    property ParentShowHint;    property PasswordChar;    property PopupMenu;    property ReadOnly;    property ShowHint;    property Spacing;    property TabOrder;    property TabStop;    property Text;    property TextHint;    property Visible;    property OnAfterFilter;    property OnCustomMatch;  end; procedure Register; implementation procedure Register;begin  {$I ukmlistviewfilter_icon.lrs}  RegisterComponents('Misc',[TKMListViewFilterEdit]);end; { TKMListViewFilterEntry } constructor TKMListViewFilterEntry.Create;begin  FItems := TStringList.Create;  FItems.Duplicates:= dupAccept;  FItems.Sorted := false;  FItems.OwnsObjects:=false;  FData := NIL;end; destructor TKMListViewFilterEntry.Destroy;var  i : Integer;begin  For i := 0 to FItems.Count - 1 do    FItems.Objects[i] := NIL;  FreeAndNil (FItems);  FData := NIL;  inherited Destroy;end; function TKMListViewFilterEntry.Match(SearchString: String; MatchAll: Boolean;  CaseSensitive: Boolean): Boolean;var  i, max : Integer;begin  Result := false;   if FCustomMatch <> NIL then    Result := FCustomMatch (SearchString, MatchAll, CaseSensitive)  else begin    max := 0;    if MatchAll then      max := FItems.Count -1;     if not CaseSensitive then SearchString := LowerCase (SearchString);     For i := 0 to max do    begin      if CaseSensitive then        Result := POS (SearchString, FItems[i]) > 0      else        Result := POS (SearchString, LowerCase(FItems[i])) > 0;      if Result then Break;    end;  end;end; { TKMCustomListViewFilter } procedure TKMCustomListViewFilter.SetCustomMatch(  AValue: TkmListViewFilterCustomMatch);begin  if FCustomMatch=AValue then Exit;  FCustomMatch:=AValue;end; procedure TKMCustomListViewFilter.SetFilteredListView(AValue: TListView);begin  if FListView=AValue then Exit;  FListView:=AValue;  Reload;end; procedure TKMCustomListViewFilter.SetFilterOptions(AValue: TFilterStringOptions  );begin  if fFilterOptions=AValue then Exit;  fFilterOptions:=AValue;end; procedure TKMCustomListViewFilter.SetMatchAll(AValue: Boolean);begin  if FMatchAll=AValue then Exit;  FMatchAll:=AValue;end; procedure TKMCustomListViewFilter.SetSortData(AValue: Boolean);begin  if FSortData=AValue then Exit;  FSortData:=AValue;end; function TKMCustomListViewFilter.IsTextHintStored: Boolean;begin  Result := TextHint<>rsFilter;end; function TKMCustomListViewFilter.GetDefaultGlyphName: string;begin  Result:='btnfiltercancel';end; function TKMCustomListViewFilter.GetCount: Integer;begin  Result := -1;  if Assigned (FListEntries) then    Result := FListEntries.Count;end; procedure TKMCustomListViewFilter.BuddyClick;begin  Self.Text := '';  RemoveFilter;  if (fListView.SortType <> stNone) or fListView.AutoSort then    fListView.Sort;  if FocusOnButtonClick then     Edit.SetFocus;  Button.Enabled:= false;  inherited;end; procedure TKMCustomListViewFilter.EditChange(Sender: TObject);begin  Button.Enabled:= Text <> '';  Filter;  if (fListView.SortType <> stNone) or fListView.AutoSort then    fListView.Sort;end; constructor TKMCustomListViewFilter.Create(aOwner: TComponent);begin  inherited Create(aOwner);  FListEntries := TKMListViewFilterEntries.Create;  FMatchAll:= false;  FFilterOptions:= [];  TextHint:= rsFilter;  ShowHint:= true;  ButtonHint:= 'Filter entfernen';  FocusOnButtonClick:= true;  Button.Enabled:= false;  OnChange:= @EditChange;end; destructor TKMCustomListViewFilter.Destroy;begin  FListEntries.Free;  fAfterFilter := NIL;  fListView := NIL;  inherited Destroy;end; procedure TKMCustomListViewFilter.Reload;var  i,j : Integer;  e : TkmListViewFilterEntry;begin  if (fListView = NIL) or not Assigned (fListView) or not Assigned (fListEntries) then    Exit;   FListEntries.Clear;   For i := 0 to fListView.Items.Count -1 do  begin    e := TkmListViewFilterEntry.Create;    e.CustomMatch:=FCustomMatch;    With e do    begin      Items.AddObject (fListView.Items[i].Caption, TObject(PtrInt (fListView.Items[i].ImageIndex)));      StateIndex := fListView.Items[i].StateIndex;      For j := 0 to fListView.Items[i].SubItems.Count -1 do        Items.AddObject (fListView.Items[i].SubItems[j], TObject (PtrInt (fListView.Items[i].SubItemImages[j])));      FData:= fListView.Items[i].Data;    end;    FListEntries.Add(e);  end;  if Assigned (FAfterFilter) then FAfterFilter (Self);end; procedure TKMCustomListViewFilter.RemoveFilter;var  i,j : Integer;  e : TkmListViewFilterEntry;begin  if (fListView = NIL) or not Assigned (fListView) then    Exit;   fListView.BeginUpdate;  fListView.Items.Clear;   For i := 0 to FListEntries.Count -1 do  begin    e := FListEntries[i];    if e <> NIL then      With fListView.Items.Add do      begin        Caption := e.Items[0];        ImageIndex := PtrInt (e.Items.Objects[0]);        StateIndex := e.StateIndex;         SubItems.AddStrings (e.Items);        SubItems.Delete(0);        for j := 0 to SubItems.Count -1 do          SubItemImages[j] := PtrInt (e.Items.Objects[j+1]);        Data := e.Data;      end;  end;  fListView.EndUpdate;  if Assigned (FAfterFilter) then FAfterFilter (Self);end; procedure TKMCustomListViewFilter.Filter;var  i,j : Integer;  e : TkmListViewFilterEntry;begin  if (fListView = NIL) or not Assigned (fListView) then    Exit;   if Self.Text = '' then    RemoveFilter  else begin    fListView.BeginUpdate;    fListView.Items.Clear;     For i := 0 to FListEntries.Count -1 do    begin      e := FListEntries[i];      if e.Match (Self.Text, FMatchall, fsoCaseSensitive in FFilterOptions) then        With fListView.Items.Add do        begin          Caption := e.Items[0];          ImageIndex := PtrInt (e.Items.Objects[0]);          StateIndex := e.StateIndex;           SubItems.AddStrings(e.Items);          SubItems.Delete(0);          For j := 0 to SubItems.Count -1 do            SubItemImages[j] := PtrInt (e.Items.Objects[j+1]);           Data := e.Data;        end;    end;    fListView.EndUpdate;    if Assigned (FAfterFilter) then FAfterFilter (Self);  end;end; end. 

wp:
Took me some time to find the issue...

The problem is that you are misusing a virtual method EditChange as an event OnChange. You want your component to react on a change in the edit control. As application developer you would write a handler for the OnChange event. But as component developer you must not do this since it would block this handler for the user.  Usually there is a virtual method which fires the event, and you can override (not overload!) it if you want the component to react on the edit change internally. I don't fully understand the issue which must be somehow related to the fact that your event handler is named equally to the virtual method. When I removed the line "OnChange := @EditChange" in the constructor and removed the "(sender: TObject)" from the declaration of the "EditChange" method and declared is as "override" rather than "overload" the component worked correctly.

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type  TKMCustomListViewFilter = class(TCustomEditButton)  ...  protected    procedure EditChange; override;   ...  end; constructor TKMCustomListViewFilter.Create(aOwner: TComponent);begin  inherited Create(aOwner);  ...  //OnChange:= @EditChange;              // Don't do this!end; procedure TKMCustomListViewFilter.EditChange; //(Sender: TObject);begin  Button.Enabled:= Text <> '';  Filter;  if (fListView <> nil) and (fListView.SortType <> stNone) or fListView.AutoSort then      // Check for FListView <> nil    fListView.Sort;  inherited;              // This fires the event for the application developerend;
There are some other issues:

* You attach an external ListView to the "FilteredListView" property of the control. When you delete this ListView in the form designer you very probably will crash the IDE because your control is not notified of the deletion and will still access the no longer existing control. The general remedy against this is to override the virtual "Notification" procedure where you can set the deleted control to nil. Look at TCustomFilterComboBox to see how this is done.
* In TKMListViewFilterEntry the field FItems and the related property Items are declared as TStringList. This is not very practical since many list-like controls have their own strings-list types which do not descent from TStringList, but only from TStrings and you cannot assign your Items to them. It is better to declare FItems and Items as TStrings, but still create FItems in the constructor as TStringList:
--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type  TKMListViewFilterEntry = Class  private    FItems : TStrings;   // rather than TStringList;    ...  public    constructor Create;    destructor Destroy; override;    property Items : TStrings read FItems;   // rather than TStringList    ...  end; constructor TKMListViewFilterEntry.Create;begin  FItems := TStringList.Create;  // But keep this!  ...end;
* In TKMCustomListViewFilter.BuddyClick and .EditChange you should check for FListView <> nil before accessing it.

dangersgromit:
Thanks wp, now it works like a charm in my testing environment.

For the new component - my first one - I just ported (copied  ::)) some working code directly. The original code made use of the OnChange and OnButtonClick event and. You are most likely right, that assigning the OnChange event internally instead of overriding the method lead to then naming error. I even took the original TCustomControlFilterEdit as a base for my component's implementation.  I should have seen it right there in the code by myself .

And thank you for looking at my other code as well, I would have stumbled across overriding "Notification" sooner or later :D but probably with a lot of frustration on the way.

Navigation

[0] Message Index

Go to full version