Recent

Author Topic: [Solved]Naming error for self written component  (Read 1361 times)

dangersgromit

  • Newbie
  • Posts: 4
[Solved]Naming error for self written component
« on: May 18, 2024, 12:21:07 pm »
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

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  [Select][+][-]
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <CONFIG>
  3.   <Package Version="5">
  4.     <PathDelim Value="\"/>
  5.     <Name Value="KMControls"/>
  6.     <Type Value="RunAndDesignTime"/>
  7.     <CompilerOptions>
  8.       <Version Value="11"/>
  9.       <PathDelim Value="\"/>
  10.       <SearchPaths>
  11.         <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)\"/>
  12.       </SearchPaths>
  13.     </CompilerOptions>
  14.     <Files>
  15.       <Item>
  16.         <Filename Value="ukmlistviewfilter.pas"/>
  17.         <HasRegisterProc Value="True"/>
  18.         <UnitName Value="uKMListViewFilter"/>
  19.       </Item>
  20.     </Files>
  21.     <RequiredPkgs>
  22.       <Item>
  23.         <PackageName Value="IDEIntf"/>
  24.       </Item>
  25.     </RequiredPkgs>
  26.     <UsageOptions>
  27.       <UnitPath Value="$(PkgOutDir)"/>
  28.     </UsageOptions>
  29.     <PublishOptions>
  30.       <Version Value="2"/>
  31.       <UseFileFilters Value="True"/>
  32.     </PublishOptions>
  33.   </Package>
  34. </CONFIG>

Code: Pascal  [Select][+][-]
  1. { This file was automatically created by Lazarus. Do not edit!
  2.   This source is only used to compile and install the package.
  3.  }
  4.  
  5. unit KMControls;
  6.  
  7. {$warn 5023 off : no warning about unused units}
  8. interface
  9.  
  10. uses
  11.   uKMListViewFilter, LazarusPackageIntf;
  12.  
  13. implementation
  14.  
  15. procedure Register;
  16. begin
  17.   RegisterUnit('uKMListViewFilter', @uKMListViewFilter.Register);
  18. end;
  19.  
  20. initialization
  21.   RegisterPackage('KMControls', @Register);
  22. end.
  23.  
  24.  

Code: Pascal  [Select][+][-]
  1. unit uKMListViewFilter;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, EditBtn, ComCtrls, FGL, LCLStrConsts;
  9.  
  10. type
  11.  
  12.   TKMListViewFilterCustomMatch = Function (SearchString : String; MatchAll : Boolean = false; CaseSensitive: Boolean = true) : Boolean of Object;
  13.  
  14.   { TKMListViewFilterEntry }
  15.  
  16.   TKMListViewFilterEntry = Class
  17.   private
  18.     FItems : TStringList;
  19.     FStateIndex : Integer;
  20.     FData : Pointer;
  21.     FCustomMatch : TKMListViewFilterCustomMatch;
  22.   public
  23.     constructor Create;
  24.     destructor Destroy; override;
  25.     function Match (SearchString : String; MatchAll : Boolean = false; CaseSensitive: Boolean = true) : Boolean;
  26.     property Items : TStringList read FItems;
  27.     property StateIndex : Integer read FStateIndex write FStateIndex;
  28.     property Data : Pointer read FData write FData;
  29.     property CustomMatch : TKMListViewFilterCustomMatch read FCustomMatch write FCustomMatch;
  30.   end;
  31.  
  32.   TCustomKMListViewFilterEntries = specialize TFPGList<TKMListViewFilterEntry>;
  33.   TKMListViewFilterEntries = Class (TCustomKMListViewFilterEntries);
  34.  
  35.   { TKMCustomListViewFilter }
  36.  
  37.   TKMCustomListViewFilter = class(TCustomEditButton)
  38.   private
  39.     FListEntries : TKMListViewFilterEntries;
  40.     FListView : TListView;
  41.     FMatchAll : Boolean;
  42.     FFilterOptions : TFilterStringOptions;
  43.     FSortData: Boolean;
  44.     procedure SetCustomMatch(AValue: TkmListViewFilterCustomMatch);
  45.     procedure SetFilteredListView(AValue: TListView);
  46.     procedure SetFilterOptions(AValue: TFilterStringOptions);
  47.     procedure SetMatchAll(AValue: Boolean);
  48.     procedure SetSortData(AValue: Boolean);
  49.     function IsTextHintStored : Boolean;
  50.   protected
  51.     FCustomMatch : TKMListViewFilterCustomMatch;
  52.     FAfterFilter : TNotifyEvent;
  53.     function GetDefaultGlyphName: string; override;
  54.     function GetCount : Integer;
  55.     procedure BuddyClick; override;
  56.     procedure EditChange (Sender : TObject); overload;
  57.   public
  58.     constructor Create (aOwner : TComponent); override;
  59.     destructor Destroy; override;
  60.     procedure Reload;
  61.     procedure RemoveFilter;
  62.     procedure Filter;
  63.   published
  64.     property FFilteredListView : TListView read FListView write SetFilteredListView;
  65.     property MatchAll : Boolean read FMatchAll write SetMatchAll default false;
  66.     property SortData : Boolean read FSortData write SetSortData;
  67.     property OriginalCount : Integer read GetCount;
  68.     property FilterOptions: TFilterStringOptions read fFilterOptions write SetFilterOptions default [];
  69.     property OnAfterFilter : TNotifyEvent read FAfterFilter write FAfterFilter;
  70.     property OnCustomMatch : TkmListViewFilterCustomMatch read FCustomMatch write SetCustomMatch;
  71.     property TextHint stored IsTextHintStored;
  72.   end;
  73.  
  74.   TKMListViewFilterEdit = Class (TKMCustomListViewFilter)
  75.   public
  76.     property AutoSelected;
  77.     property Button;
  78.     property Edit;
  79.     property OnChange;
  80.   published
  81.     property NumbersOnly;
  82.     property Action;
  83.     property AutoSelect;
  84.     property AutoSize default True;
  85.     property Align;
  86.     property Alignment;
  87.     property Anchors;
  88.     property BiDiMode;
  89.     property BorderSpacing;
  90.     property BorderStyle default bsNone;
  91.     property ButtonCaption;
  92.     property ButtonCursor;
  93.     property ButtonHint;
  94.     property ButtonOnlyWhenFocused;
  95.     property ButtonWidth;
  96.     property CharCase;
  97.     property Color;
  98.     property Constraints;
  99.     property Cursor;
  100.     property DirectInput;
  101.     property EchoMode;
  102.     property Enabled;
  103.     property Flat;
  104.     property FocusOnButtonClick;
  105.     property Font;
  106.     property Glyph;
  107. //    property HideSelection;
  108.     property Hint;
  109.     property Images;
  110.     property ImageIndex;
  111.     property ImageWidth;
  112.     property Layout;
  113.     property MaxLength;
  114.     property NumGlyphs;
  115.     property OnButtonClick;
  116.     property OnClick;
  117.     property OnDblClick;
  118.     property OnDragDrop;
  119.     property OnDragOver;
  120.     property OnContextPopup;
  121.     property OnEditingDone;
  122.     property OnEndDrag;
  123.     property OnEnter;
  124.     property OnExit;
  125.     property OnKeyDown;
  126.     property OnKeyPress;
  127.     property OnKeyUp;
  128.     property OnMouseDown;
  129.     property OnMouseEnter;
  130.     property OnMouseLeave;
  131.     property OnMouseMove;
  132.     property OnMouseUp;
  133.     property OnMouseWheel;
  134.     property OnMouseWheelDown;
  135.     property OnMouseWheelUp;
  136.     property OnStartDrag;
  137.     property OnUTF8KeyPress;
  138.     property ParentBiDiMode;
  139.     property ParentColor;
  140.     property ParentFont;
  141.     property ParentShowHint;
  142.     property PasswordChar;
  143.     property PopupMenu;
  144.     property ReadOnly;
  145.     property ShowHint;
  146.     property Spacing;
  147.     property TabOrder;
  148.     property TabStop;
  149.     property Text;
  150.     property TextHint;
  151.     property Visible;
  152.     property OnAfterFilter;
  153.     property OnCustomMatch;
  154.   end;
  155.  
  156. procedure Register;
  157.  
  158. implementation
  159.  
  160. procedure Register;
  161. begin
  162.   {$I ukmlistviewfilter_icon.lrs}
  163.   RegisterComponents('Misc',[TKMListViewFilterEdit]);
  164. end;
  165.  
  166. { TKMListViewFilterEntry }
  167.  
  168. constructor TKMListViewFilterEntry.Create;
  169. begin
  170.   FItems := TStringList.Create;
  171.   FItems.Duplicates:= dupAccept;
  172.   FItems.Sorted := false;
  173.   FItems.OwnsObjects:=false;
  174.   FData := NIL;
  175. end;
  176.  
  177. destructor TKMListViewFilterEntry.Destroy;
  178. var
  179.   i : Integer;
  180. begin
  181.   For i := 0 to FItems.Count - 1 do
  182.     FItems.Objects[i] := NIL;
  183.   FreeAndNil (FItems);
  184.   FData := NIL;
  185.   inherited Destroy;
  186. end;
  187.  
  188. function TKMListViewFilterEntry.Match(SearchString: String; MatchAll: Boolean;
  189.   CaseSensitive: Boolean): Boolean;
  190. var
  191.   i, max : Integer;
  192. begin
  193.   Result := false;
  194.  
  195.   if FCustomMatch <> NIL then
  196.     Result := FCustomMatch (SearchString, MatchAll, CaseSensitive)
  197.   else begin
  198.     max := 0;
  199.     if MatchAll then
  200.       max := FItems.Count -1;
  201.  
  202.     if not CaseSensitive then SearchString := LowerCase (SearchString);
  203.  
  204.     For i := 0 to max do
  205.     begin
  206.       if CaseSensitive then
  207.         Result := POS (SearchString, FItems[i]) > 0
  208.       else
  209.         Result := POS (SearchString, LowerCase(FItems[i])) > 0;
  210.       if Result then Break;
  211.     end;
  212.   end;
  213. end;
  214.  
  215. { TKMCustomListViewFilter }
  216.  
  217. procedure TKMCustomListViewFilter.SetCustomMatch(
  218.   AValue: TkmListViewFilterCustomMatch);
  219. begin
  220.   if FCustomMatch=AValue then Exit;
  221.   FCustomMatch:=AValue;
  222. end;
  223.  
  224. procedure TKMCustomListViewFilter.SetFilteredListView(AValue: TListView);
  225. begin
  226.   if FListView=AValue then Exit;
  227.   FListView:=AValue;
  228.   Reload;
  229. end;
  230.  
  231. procedure TKMCustomListViewFilter.SetFilterOptions(AValue: TFilterStringOptions
  232.   );
  233. begin
  234.   if fFilterOptions=AValue then Exit;
  235.   fFilterOptions:=AValue;
  236. end;
  237.  
  238. procedure TKMCustomListViewFilter.SetMatchAll(AValue: Boolean);
  239. begin
  240.   if FMatchAll=AValue then Exit;
  241.   FMatchAll:=AValue;
  242. end;
  243.  
  244. procedure TKMCustomListViewFilter.SetSortData(AValue: Boolean);
  245. begin
  246.   if FSortData=AValue then Exit;
  247.   FSortData:=AValue;
  248. end;
  249.  
  250. function TKMCustomListViewFilter.IsTextHintStored: Boolean;
  251. begin
  252.   Result := TextHint<>rsFilter;
  253. end;
  254.  
  255. function TKMCustomListViewFilter.GetDefaultGlyphName: string;
  256. begin
  257.   Result:='btnfiltercancel';
  258. end;
  259.  
  260. function TKMCustomListViewFilter.GetCount: Integer;
  261. begin
  262.   Result := -1;
  263.   if Assigned (FListEntries) then
  264.     Result := FListEntries.Count;
  265. end;
  266.  
  267. procedure TKMCustomListViewFilter.BuddyClick;
  268. begin
  269.   Self.Text := '';
  270.   RemoveFilter;
  271.   if (fListView.SortType <> stNone) or fListView.AutoSort then
  272.     fListView.Sort;
  273.   if FocusOnButtonClick then
  274.      Edit.SetFocus;
  275.   Button.Enabled:= false;
  276.   inherited;
  277. end;
  278.  
  279. procedure TKMCustomListViewFilter.EditChange(Sender: TObject);
  280. begin
  281.   Button.Enabled:= Text <> '';
  282.   Filter;
  283.   if (fListView.SortType <> stNone) or fListView.AutoSort then
  284.     fListView.Sort;
  285. end;
  286.  
  287. constructor TKMCustomListViewFilter.Create(aOwner: TComponent);
  288. begin
  289.   inherited Create(aOwner);
  290.   FListEntries := TKMListViewFilterEntries.Create;
  291.   FMatchAll:= false;
  292.   FFilterOptions:= [];
  293.   TextHint:= rsFilter;
  294.   ShowHint:= true;
  295.   ButtonHint:= 'Filter entfernen';
  296.   FocusOnButtonClick:= true;
  297.   Button.Enabled:= false;
  298.   OnChange:= @EditChange;
  299. end;
  300.  
  301. destructor TKMCustomListViewFilter.Destroy;
  302. begin
  303.   FListEntries.Free;
  304.   fAfterFilter := NIL;
  305.   fListView := NIL;
  306.   inherited Destroy;
  307. end;
  308.  
  309. procedure TKMCustomListViewFilter.Reload;
  310. var
  311.   i,j : Integer;
  312.   e : TkmListViewFilterEntry;
  313. begin
  314.   if (fListView = NIL) or not Assigned (fListView) or not Assigned (fListEntries) then
  315.     Exit;
  316.  
  317.   FListEntries.Clear;
  318.  
  319.   For i := 0 to fListView.Items.Count -1 do
  320.   begin
  321.     e := TkmListViewFilterEntry.Create;
  322.     e.CustomMatch:=FCustomMatch;
  323.     With e do
  324.     begin
  325.       Items.AddObject (fListView.Items[i].Caption, TObject(PtrInt (fListView.Items[i].ImageIndex)));
  326.       StateIndex := fListView.Items[i].StateIndex;
  327.       For j := 0 to fListView.Items[i].SubItems.Count -1 do
  328.         Items.AddObject (fListView.Items[i].SubItems[j], TObject (PtrInt (fListView.Items[i].SubItemImages[j])));
  329.       FData:= fListView.Items[i].Data;
  330.     end;
  331.     FListEntries.Add(e);
  332.   end;
  333.   if Assigned (FAfterFilter) then FAfterFilter (Self);
  334. end;
  335.  
  336. procedure TKMCustomListViewFilter.RemoveFilter;
  337. var
  338.   i,j : Integer;
  339.   e : TkmListViewFilterEntry;
  340. begin
  341.   if (fListView = NIL) or not Assigned (fListView) then
  342.     Exit;
  343.  
  344.   fListView.BeginUpdate;
  345.   fListView.Items.Clear;
  346.  
  347.   For i := 0 to FListEntries.Count -1 do
  348.   begin
  349.     e := FListEntries[i];
  350.     if e <> NIL then
  351.       With fListView.Items.Add do
  352.       begin
  353.         Caption := e.Items[0];
  354.         ImageIndex := PtrInt (e.Items.Objects[0]);
  355.         StateIndex := e.StateIndex;
  356.  
  357.         SubItems.AddStrings (e.Items);
  358.         SubItems.Delete(0);
  359.         for j := 0 to SubItems.Count -1 do
  360.           SubItemImages[j] := PtrInt (e.Items.Objects[j+1]);
  361.         Data := e.Data;
  362.       end;
  363.   end;
  364.   fListView.EndUpdate;
  365.   if Assigned (FAfterFilter) then FAfterFilter (Self);
  366. end;
  367.  
  368. procedure TKMCustomListViewFilter.Filter;
  369. var
  370.   i,j : Integer;
  371.   e : TkmListViewFilterEntry;
  372. begin
  373.   if (fListView = NIL) or not Assigned (fListView) then
  374.     Exit;
  375.  
  376.   if Self.Text = '' then
  377.     RemoveFilter
  378.   else begin
  379.     fListView.BeginUpdate;
  380.     fListView.Items.Clear;
  381.  
  382.     For i := 0 to FListEntries.Count -1 do
  383.     begin
  384.       e := FListEntries[i];
  385.       if e.Match (Self.Text, FMatchall, fsoCaseSensitive in FFilterOptions) then
  386.         With fListView.Items.Add do
  387.         begin
  388.           Caption := e.Items[0];
  389.           ImageIndex := PtrInt (e.Items.Objects[0]);
  390.           StateIndex := e.StateIndex;
  391.  
  392.           SubItems.AddStrings(e.Items);
  393.           SubItems.Delete(0);
  394.           For j := 0 to SubItems.Count -1 do
  395.             SubItemImages[j] := PtrInt (e.Items.Objects[j+1]);
  396.  
  397.           Data := e.Data;
  398.         end;
  399.     end;
  400.     fListView.EndUpdate;
  401.     if Assigned (FAfterFilter) then FAfterFilter (Self);
  402.   end;
  403. end;
  404.  
  405. end.
  406.  
« Last Edit: May 19, 2024, 06:42:21 am by dangersgromit »

wp

  • Hero Member
  • *****
  • Posts: 12575
Re: Naming error for self written component
« Reply #1 on: May 19, 2024, 01:08:00 am »
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  [Select][+][-]
  1. type
  2.   TKMCustomListViewFilter = class(TCustomEditButton)
  3.   ...
  4.   protected
  5.     procedure EditChange; override;
  6.    ...
  7.   end;
  8.  
  9. constructor TKMCustomListViewFilter.Create(aOwner: TComponent);
  10. begin
  11.   inherited Create(aOwner);
  12.   ...
  13.   //OnChange:= @EditChange;              // Don't do this!
  14. end;
  15.  
  16. procedure TKMCustomListViewFilter.EditChange; //(Sender: TObject);
  17. begin
  18.   Button.Enabled:= Text <> '';
  19.   Filter;
  20.   if (fListView <> nil) and (fListView.SortType <> stNone) or fListView.AutoSort then      // Check for FListView <> nil
  21.     fListView.Sort;
  22.   inherited;              // This fires the event for the application developer
  23. end;

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  [Select][+][-]
  1. type
  2.   TKMListViewFilterEntry = Class
  3.   private
  4.     FItems : TStrings;   // rather than TStringList;
  5.     ...
  6.   public
  7.     constructor Create;
  8.     destructor Destroy; override;
  9.     property Items : TStrings read FItems;   // rather than TStringList
  10.     ...
  11.   end;
  12.  
  13. constructor TKMListViewFilterEntry.Create;
  14. begin
  15.   FItems := TStringList.Create;  // But keep this!
  16.   ...
  17. end;
  • In TKMCustomListViewFilter.BuddyClick and .EditChange you should check for FListView <> nil before accessing it.

dangersgromit

  • Newbie
  • Posts: 4
[Solved]Naming error for self written component
« Reply #2 on: May 19, 2024, 06:40:25 am »
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.

 

TinyPortal © 2005-2018