I have been using Delphi 6 for a very long time, and I recently started using version Delphi DX Rio 10.3.
Against all my expectations, it seems to me that loading items on standard controls (ComboBox, Listbox and so on) in DX RIO is significantly slower (at runtime) than in D6 (both in DEBUG and RELEASE mode, after activating all optimizations).
I tried using BeginUpdate / EndUpdate, without changing the speed difference between the environments.
I would like to ask if anyone has any suggestions for improving DX 10.3 Combobox/Listbox performances. The specific problem I'm trying to solve in my real application is to load some dozens of items in 10-20 combos/listboxes controls on my form.
I wrote a very simple test for a standard COMBO. The same program works in both D6 and DX RIO.
On my development machine (Win 10 pro) the program compiled with D6 takes about 1 second, while the program compiled with DX RIO takes about four times as much.
Many thanks in advance for your answers.
{$A+,B-,C+,D+,E-,F-,G+,H+,I+,J-,K-,L+,M-,N+,O+,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y+,Z1}
unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
cb: TComboBox;
txt_count: TLabel;
btn: TButton;
procedure btnClick(Sender: TObject);
end;
var Form1: TForm1;
implementation
{$R *.dfm}
var dt_start_timer : TDatetime;
procedure start_timer;
begin
dt_start_timer := now
end;
function delta_timer_msec : longint;
begin
result := round((now - dt_start_timer) * 24*60*60*1000)
end;
procedure execute(father : TForm;cb : TComboBox;txt : TLabel;bo_disable_update : boolean);
const
MAX = 100;
ITEMS_COUNT = 1000;
var i, j : integer;
begin
start_timer;
if bo_disable_update then cb.Items.BeginUpdate;
for i := 1 to MAX do begin
cb.Items.Clear;
for j := 1 to ITEMS_COUNT do cb.Items.Add('text-' + intToStr(j))
end;
if bo_disable_update then cb.Items.EndUpdate;
txt.Caption := intToStr(delta_timer_msec) + ' msecs'
end;
procedure TForm1.btnClick(Sender: TObject);
begin
execute(self, cb, txt_count, {disable_update}TRUE)
end;
end.
object Form1: TForm1
Left = -6
Top = 117
Width = 449
Height = 350
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object txt_count: TLabel
Left = 198
Top = 32
Width = 44
Height = 13
Caption = 'txt_count'
end
object cb: TComboBox
Left = 42
Top = 28
Width = 145
Height = 21
ItemHeight = 13
TabOrder = 0
Text = 'cb'
end
object btn: TButton
Left = 24
Top = 70
Width = 251
Height = 139
Caption = 'btn'
TabOrder = 1
OnClick = btnClick
end
end
Related
I want to use a custom Hint window class for my entire application.
I use the Application.OnShowHint, analyze the THintInfo, and return my own TMyHintWindow in the HintInfo.HintWindowClass. This works well for all controls but I have a strange problem only with TVirtualStringTree Columns hint.
VT uses it's own hint window and own structure for the HintInfo.HintData. I study the code, and know it uses the VTHintData. so far so good. problem is that when I return my own hint window class (derived from THintWindow) it shows the hint window only for a split second and disappears!
There is no problem with hints returned for tree Nodes. they use the same method/structure (VTHintData).
Here is a very simple MCVE:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, VirtualTrees, StdCtrls;
type
TForm1 = class(TForm)
VirtualStringTree1: TVirtualStringTree;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
public
procedure ApplicationShowHint(var HintStr: string; var CanShow: Boolean;
var HintInfo: THintInfo);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
TMyHintWindow = class(THintWindow)
public
{ nothing special here for now }
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
VirtualStringTree1.Hint := 'VT main hint';
VirtualStringTree1.ShowHint := True;
Memo1.Hint := 'Memo hint';
Memo1.ShowHint := True;
Application.OnShowHint := ApplicationShowHint;
end;
procedure TForm1.ApplicationShowHint(var HintStr: string; var CanShow: Boolean;
var HintInfo: THintInfo);
var
VTHintData: TVTHintData;
begin
{ VT uses it's own hint window class }
if HintInfo.HintWindowClass = TVirtualTreeHintWindow then
begin
{ VT passes columns and nodes hints information in HintInfo.HintData }
if HintInfo.HintData <> nil then
begin
VTHintData := PVTHintData(HintInfo.HintData)^;
if VTHintData.Node <> nil then { node hint }
begin
{ handle this case with DoGetNodeHint/DoGetNodeToolTip ... it works fine }
end
else
begin { column hint }
HintStr := VTHintData.DefaultHint; { got it! }
end;
end;
end;
Memo1.Lines.Add(HintStr); { prove I got the right hint }
HintInfo.HintColor := clAqua;
{ use my own hint window class
the hint from the VT columns is shown for a split second and hides! }
HintInfo.HintWindowClass := TMyHintWindow;
end;
end.
Form:
object Form1: TForm1
Left = 399
Top = 256
Width = 720
Height = 211
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object VirtualStringTree1: TVirtualStringTree
Left = 8
Top = 8
Width = 409
Height = 153
Header.AutoSizeIndex = 0
Header.Font.Charset = DEFAULT_CHARSET
Header.Font.Color = clWindowText
Header.Font.Height = -11
Header.Font.Name = 'MS Sans Serif'
Header.Font.Style = []
Header.Options = [hoColumnResize, hoDrag, hoShowHint, hoShowSortGlyphs, hoVisible]
HintAnimation = hatNone
HintMode = hmTooltip
TabOrder = 0
Columns = <
item
Position = 0
Width = 150
WideText = 'column 0'
WideHint = 'VT column 0 hint'
end
item
Position = 1
Width = 150
WideText = 'column 1'
WideHint = 'VT column 1 hint'
end>
end
object Memo1: TMemo
Left = 424
Top = 8
Width = 273
Height = 153
ScrollBars = ssVertical
TabOrder = 1
end
end
I have debugged this for hours. I see nothing special.
Why is this happening? who/what closes the hint window right away?
Thanks.
I'm using VT version 5.3.0
In the TBaseVirtualTree.CMHintShowPause() it is stated:
A little workaround is needed here to make the application class
using the correct hint window class. Once the application gets
ShowHint set to true (which is the case when we want to show hints in
the tree) then an internal hint window will be created which is not
our own class (because we don't set an application wide hint window
class but only one for the tree). Unfortunately, this default hint
window class will prevent hints for the non-client area to show up
(e.g. for the header) by calling CancelHint whenever certain messages
arrive. By setting the hint show pause to 0 if our hint class was not
used recently we make sure that the hint timer (in Forms.pas) is
not used and our class is created immediately.
I'm not really sure how to handle this. I think I'll drop the whole idea of using my won hint class for the header columns.
I want to check if an IP address is within a range of a minimum and maximum IP address. How can I do that in Delphi?
For example I want to do something like this:
if CheckIp("127.0.0.15","127.0.0.1","127.0.0.255") then ShowMessage('ok');
127.0.0.1 is start value of range, 127.0.0.255 is end value of range and 127.0.0.15 is IP address that will be checked.
For IPv4 addresses, you can simply convert them to their integer forms and then perform standard ordinal comparisons on them.
IPv6 address are too big to convert to integers (unless you use a third party BigInt library), so you would have to convert them to their binary form and compare them byte-by-byte instead.
I'm going to assume that your addresses are IPv4 addresses stored in a 32 bit integer in host byte order. And I'm also assuming that you want a lexicographic ordering so that:
a.b.c.d < p.q.r.s
is compared by first comparing a and p, and if equal then comparing b and q, and so on.
In which case, the natural unsigned integer ordering (using the < or > operators) will produce the ordering that you desire.
If the addresses are in network byte order, then you need to convert to host byte order before comparing.
In your question, you have addresses as strings. So you'd need to convert them to network byte order 32 bit unsigned integers with inet_addr, and then to host byte order with ntohl. And then you could compare.
I asked a slightly similar question here before, for general string routines for IP addresses. Based off of the answer by NGLN, I have implemented a set of comparison functions, and a demo application. The function IPRange detects whether it's v4 or v6 and compares them accordingly.
uMain.pas
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
IPTypes;
type
TfrmCheckIPRange = class(TForm)
txtFrom: TEdit;
txtTo: TEdit;
txtIP: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
txtResult: TEdit;
Label4: TLabel;
procedure DoCheck(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmCheckIPRange: TfrmCheckIPRange;
implementation
{$R *.dfm}
function IntRange(const Val, Min, Max: Integer): Boolean;
begin
Result:= (Val >= Min) and (Val <= Max);
end;
function IPRangeV4(const IP, IPFrom, IPTo: TIPv4): Boolean;
begin
Result:= IntRange(IP.D, IPFrom.D, IPTo.D);
if Result then
Result:= IntRange(IP.C, IPFrom.C, IPTo.C);
if Result then
Result:= IntRange(IP.B, IPFrom.B, IPTo.B);
if Result then
Result:= IntRange(IP.A, IPFrom.A, IPTo.A);
end;
function IPRangeV6(const IP, IPFrom, IPTo: TIPv6): Boolean;
begin
Result:= IntRange(IP.H, IPFrom.H, IPTo.H);
if Result then
Result:= IntRange(IP.G, IPFrom.G, IPTo.G);
if Result then
Result:= IntRange(IP.F, IPFrom.F, IPTo.F);
if Result then
Result:= IntRange(IP.E, IPFrom.E, IPTo.E);
if Result then
Result:= IntRange(IP.D, IPFrom.D, IPTo.D);
if Result then
Result:= IntRange(IP.C, IPFrom.C, IPTo.C);
if Result then
Result:= IntRange(IP.B, IPFrom.B, IPTo.B);
if Result then
Result:= IntRange(IP.A, IPFrom.A, IPTo.A);
end;
function IPRange(const IP, IPFrom, IPTo: String): Boolean;
var
IP4, FR4, TO4: TIPv4;
IP6, FR6, TO6: TIPv6;
function IsV4(const S: String): Boolean;
begin
Result:= Pos('.', S) > 1;
end;
function IsV6(const S: String): Boolean;
begin
Result:= Pos(':', S) > 0;
end;
begin
Result:= False;
if (IsV6(IP)) and (IsV6(IPFrom)) and (IsV6(IPTo)) then begin
IP6:= StrToIPv6(IP);
FR6:= StrToIPv6(IPFrom);
TO6:= StrToIPv6(IPTo);
Result:= IPRangeV6(IP6, FR6, TO6);
end else
if (IsV4(IP)) and (IsV4(IPFrom)) and (IsV4(IPTo)) then begin
IP4:= StrToIPv4(IP);
FR4:= StrToIPv4(IPFrom);
TO4:= StrToIPv4(IPTo);
Result:= IPRangeV4(IP4, FR4, TO4);
end else begin
raise Exception.Create('Invalid IP Address Input');
end;
end;
{ TfrmCheckIPRange }
procedure TfrmCheckIPRange.FormCreate(Sender: TObject);
begin
DoCheck(nil);
end;
procedure TfrmCheckIPRange.DoCheck(Sender: TObject);
begin
try
if IPRange(txtIP.Text, txtFrom.Text, txtTo.Text) then begin
txtResult.Text:= 'IP is in range';
txtResult.Color:= clGreen;
end else begin
txtResult.Text:= 'IP is NOT in range';
txtResult.Color:= clRed;
end;
except
on e: exception do begin
txtResult.Text:= e.Message;
txtResult.Color:= clYellow;
end;
end;
end;
end.
uMain.dfm
object frmCheckIPRange: TfrmCheckIPRange
Left = 350
Top = 113
BorderIcons = [biSystemMenu]
BorderStyle = bsSingle
Caption = 'Check IP Range'
ClientHeight = 124
ClientWidth = 296
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
OnCreate = FormCreate
DesignSize = (
296
124)
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 11
Top = 11
Width = 71
Height = 13
Alignment = taRightJustify
Caption = 'IP To Compare'
end
object Label2: TLabel
Left = 11
Top = 38
Width = 71
Height = 13
Alignment = taRightJustify
Caption = 'IP Range From'
end
object Label3: TLabel
Left = 23
Top = 65
Width = 59
Height = 13
Alignment = taRightJustify
Caption = 'IP Range To'
end
object Label4: TLabel
Left = 52
Top = 92
Width = 30
Height = 13
Alignment = taRightJustify
Caption = 'Result'
end
object txtFrom: TEdit
Left = 88
Top = 35
Width = 196
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 1
Text = '192.168.3.100'
OnChange = DoCheck
ExplicitWidth = 158
end
object txtTo: TEdit
Left = 88
Top = 62
Width = 196
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 2
Text = '192.168.3.200'
OnChange = DoCheck
ExplicitWidth = 158
end
object txtIP: TEdit
Left = 88
Top = 8
Width = 196
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 0
Text = '192.168.3.105'
OnChange = DoCheck
ExplicitWidth = 158
end
object txtResult: TEdit
Left = 88
Top = 89
Width = 196
Height = 21
Anchors = [akLeft, akTop, akRight]
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = [fsBold]
ParentFont = False
ReadOnly = True
TabOrder = 3
OnChange = DoCheck
ExplicitWidth = 158
end
end
I've tested IPv4, but have NOT tested IPv6, although it should work. I'm not familiar enough with IPv6 to even know different test scenarios.
You may also want to add some logic to check if an IP is within the same subnet, because you might not want to include different subnets. That's as easy as making sure the first 3 numbers (v4) are exactly the same. You may wish to raise an exception if there are any differences in subnets, but that's all up to how you need to implement this.
EDIT
I fixed the logic in determining v4 vs v6, because an IPv6 address could also possibly have a . in it, I had to switch the order of checking from v4-v6 to v6-v4.
I've got a very simple program that uses DSPack from within Delphi 2010. I have a form with a TFilterGraph and a TVideoWindow. The video plays and renders nicely. I can't seem to figure out how to make the video loop back to the beginning when it ends.
How do you make a video automatically loop using DSPack?
Code
unit Unit21;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, DSPack, ExtCtrls;
type
TForm21 = class(TForm)
FilterGraph1: TFilterGraph;
OpenDialog1: TOpenDialog;
Button1: TButton;
Button2: TButton;
Panel1: TPanel;
VideoWindow1: TVideoWindow;
Panel2: TPanel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form21: TForm21;
implementation
{$R *.dfm}
procedure TForm21.Button1Click(Sender: TObject);
begin
OpenDialog1.InitialDir := ExtractFilePath(ParamStr(0));
if OpenDialog1.Execute then
begin
if not FilterGraph1.Active then FilterGraph1.Active:= True;
VideoWindow1.FilterGraph:= FilterGraph1;
FilterGraph1.RenderFile(OpenDialog1.Filename);
FilterGraph1.Play;
end;
end;
procedure TForm21.Button2Click(Sender: TObject);
begin
FilterGraph1.Stop;
end;
procedure TForm21.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
FilterGraph1.ClearGraph;
FilterGraph1.Active:= False;
end;
end.
DFM
object Form21: TForm21
Left = 0
Top = 0
Caption = 'Form21'
ClientHeight = 441
ClientWidth = 644
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCloseQuery = FormCloseQuery
PixelsPerInch = 96
TextHeight = 13
object Panel1: TPanel
Left = 0
Top = 0
Width = 644
Height = 384
Align = alClient
Caption = 'Panel1'
TabOrder = 0
object VideoWindow1: TVideoWindow
Left = 1
Top = 1
Width = 642
Height = 382
Mode = vmVMR
FilterGraph = FilterGraph1
VMROptions.Mode = vmrWindowed
Color = clWhite
Align = alClient
end
end
object Panel2: TPanel
Left = 0
Top = 384
Width = 644
Height = 57
Align = alBottom
Caption = 'Panel2'
TabOrder = 1
object Button1: TButton
Left = 24
Top = 16
Width = 75
Height = 25
Caption = 'Play'
TabOrder = 0
OnClick = Button1Click
end
object Button2: TButton
Left = 128
Top = 16
Width = 75
Height = 25
Caption = 'Stop'
TabOrder = 1
OnClick = Button2Click
end
end
object FilterGraph1: TFilterGraph
GraphEdit = True
LinearVolume = True
Left = 424
Top = 144
end
object OpenDialog1: TOpenDialog
Left = 344
Top = 128
end
end
There is no built in support for seamless looping. Yes you certainly can receive completion event, seek playback to the beginning and run the graph again, however this would inevitably have a restart delay and possibly flickering.
To implement seamless looping you either a multigraph solution, to restart upstream graph while presentation graph is on a short pause and does not flicker. Or otherwise add custom filters into the pipeline to restart streaming internally and present it as continuous stream.
It seems that on Win7 changing the TOpenDialog.InitialDir doesn't work, when the new directory is on a different drive, than the current directory.
e.g.: I want to change my InitialDir from 'C:\program files\MyApp' to 'D:\test\MyAppData'
Is that a known issue, or only on my computer?
I already tried the same thing, as mentioned in the following post, but without any success:
Changing the directory of Delphi OpenDialog
EDIT:
I am using DelphiXE on Win7 32 Bit
The path/dir is correct: So, when I copy that path from code and past it into the 'File Name' field of that Dialog itself and I press ENTER, then the Dialog switches to that directory. Only, in my code it is not working.
UPDATE:
I found the problem. If the path contains some path commands like ..\ the TOpenDialog.InitialDir is not able to resolve that. Use TPath.GetFullPath(...) to make it clean.
I have tested on a Delphi XE, it runs fine... I have done this:
Put a new form:
object Form4: TForm4
Left = 0
Top = 0
Caption = 'Form4'
ClientHeight = 204
ClientWidth = 447
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 24
Top = 40
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object Edit1: TEdit
Left = 120
Top = 42
Width = 121
Height = 21
TabOrder = 1
Text = 'D:\'
end
object OpenDialog1: TOpenDialog
InitialDir = 'C:\'
Left = 120
Top = 72
end
end
And its source code:
unit Unit4;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm4 = class(TForm)
OpenDialog1: TOpenDialog;
Button1: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form4: TForm4;
implementation
{$R *.dfm}
procedure TForm4.Button1Click(Sender: TObject);
begin
OpenDialog1.InitialDir := edit1.text;
OpenDialog1.Execute;
end;
end.
Regards
I don't have any problem changing InitialDir, either through object inspector or runtime (Win7 with Delphi 2010). Try doublechecking if the directory you try to change to is correctly typed.
I have two string lists that I'm working with. One that has a list of keywords, and then another that has a list of negative keywords. I want to be able to search through the list and pick out the list items that do not contain the negative keyword and output to a third keyword list. I was using the AnsiPos function but that found the negative keywords if they were part of a word, vs full word.
Any suggestions on a relatively easy way to do this? Speed is not that important, but would be nice.
Example of what I'm looking to do:
Keyword List:
Cat
Catfish
Fish Sticks
Dog Food
Negative Keyword List:
Fish
Returned Values Wanted:
Cat
Catfish
Dog Food
This is what I've got so far.. which does not work. I used information from: Is There An Efficient Whole Word Search Function in Delphi?
function ExistWordInString(aString: PAnsichar; aSearchString: string;
aSearchOptions: TStringSearchOptions): Boolean;
var
Size : Integer;
begin
Size := StrLen(aString);
result := SearchBuf(aString, Size, 0, 0, aSearchString, aSearchOptions) <> nil;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
i, j, index: integer;
s: string;
stl: tstringlist;
begin
stl := TStringList.Create;
stl.Text := listbox1.Items.Text;
for I := 0 to stl.Count - 1 do
begin
for j := 0 to listbox2.Count - 1 do
begin
if not ExistWordInString(PAnsiChar(listbox2.Items.Strings[j]),
listbox1.Items.Strings[i], [soWholeWord, soDown])
then
listbox3.Items.Append(stl.Strings[i]);
end;
end;
end;
If spaces are the only word delimiter you need to worry about, then you can do a whole word match using AnsiPos by adding a space before and after both the keyword and the negative keyword, ie
AnsiPos(' '+SubStr+' ', ' '+Str+' ')
You'd need a loop to check every entry from the negative keyword list.
this sample code works like a charm (using Delphi 7):
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, StrUtils;
type
TForm1 = class(TForm)
Button1: TButton;
ListBox1: TListBox;
ListBox2: TListBox;
ListBox3: TListBox;
procedure Button1Click(Sender: TObject);
private
function ExistWordInString(aString, aSearchString:string;aSearchOptions: TStringSearchOptions): Boolean;
public
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
i,k: integer;
begin
for k:= 0 to ListBox2.Count -1 do
for i:= 0 to ListBox1.Count - 1 do
begin
if not ExistWordInString(ListBox1.Items[i], ListBox2.Items[k],[soWholeWord,soDown]) then
ListBox3.Items.Append(ListBox1.Items[i]);
end;
end;
function TForm1.ExistWordInString(aString, aSearchString: string; aSearchOptions: TStringSearchOptions): Boolean;
var
Size : Integer;
begin
Size:=Length(aString);
Result := SearchBuf(PChar(aString), Size, 0, 0, aSearchString, aSearchOptions)<>nil;
end;
end.
and here's the form:
object Form1: TForm1
Left = 1008
Top = 398
Width = 411
Height = 294
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 320
Top = 8
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object ListBox1: TListBox
Left = 8
Top = 8
Width = 177
Height = 97
ItemHeight = 13
Items.Strings = (
'Cat '
'Catfish'
'Fish Sticks'
'Dog Food')
TabOrder = 1
end
object ListBox2: TListBox
Left = 192
Top = 8
Width = 121
Height = 97
ItemHeight = 13
Items.Strings = (
'Fish')
TabOrder = 2
end
object ListBox3: TListBox
Left = 8
Top = 112
Width = 305
Height = 137
ItemHeight = 13
TabOrder = 3
end
end
hope this helps.
Reinhard :-)
I think I figured it out. Use stringlist.find('fish',index);
I didn't figure it out. .find did not work.
-Brad
You can use the SearchBuf function (see the pastacool's answer) IF you are not interested in other characters except A..Z / Unicode.
If you have an Unicode Delphi (D2009 or D2010) then you must use TCharacter.IsLetterOrDigit(aString: string; aIndex: integer): boolean; from the Character unit. A simple example for you to get the idea:
procedure TForm7.btn1Click(Sender: TObject);
var
bMatches: boolean;
begin
with rgx1 do //custom component - disregard it
begin
RegEx:=edtTextToFind.Text; //text to find
Subject:=mmoResult.Text; //text in which to search
if Match then //aha! found it!
begin
bMatches:=True;
if chkWholeWord.Checked then //be attentive from here!! - I think that's self explaining...
begin
if MatchedExpressionOffset>1 then
bMatches:=not TCharacter.IsLetterOrDigit(Subject, MatchedExpressionOffset-1);
if bMatches and (MatchedExpressionOffset+MatchedExpressionLength<=Length(Subject)) then
bMatches:=not TCharacter.IsLetterOrDigit(Subject, MatchedExpressionOffset+MatchedExpressionLength);
end;
if bMatches then //select it in the memo
begin
mmoResult.SelStart:=MatchedExpressionOffset-1;
mmoResult.SelLength:=MatchedExpressionLength;
mmoResult.SetFocus;
end
else
ShowMessage('Text not found!');
end
else
ShowMessage('Text not found!');
end;
end;
Change your function to read:
function ExistWordInString(aString:PAnsichar;
aSearchString:string;
aSearchOptions: TStringSearchOptions): Boolean;
var
b : boolean;
begin
if soWholeWord in aSearchOptions then
b := Pos(' '+Uppercase(aSearchString)+' ',' '+UpperCase(aString)+' ') > 0;
else
b := Pos(UpperCase(aSearchString),UpperCase(aString)) > 0;
Result := b;
end;
If your using Delphi 2009/2010 then change it from Pos to AnsiPos. My assumption here is that soWholeWord means that the match "Fish" would match "Fish Sticks" but not "catfish".