I need a little help with a function. What I need to do is determine, if a string contains a number or not. If yes, I need only a number from the string, if not I need a whole word from it.
For example:
If my string is 'xyz 60', I need '60' from it, but if the string is 'xyz', I need the whole string:
function TForm1.FindNumberInString(InString: string): TNumberInString;
var
i, j: Integer;
ST: String;
begin
Result.TNumber := '';
Result.TIsNumber := False;
for i := 1 to Length(InString) do
begin
if (InString[i] in ['0'..'9']) then
begin
Result.TNumber := Result.TNumber + InString[i];
Result.TIsNumber := True;
end else
Result.TNumber := InString;
end;
end;
// TNumberInString is a packed record
TNumberInString = Packed Record
TNumber: string;
TIsNumber: boolean;
end;
Anyone can help what will be the problem? The Result is always 'xyz60', not '60'. I tried "if not (InString[i] in...), but it did not work, too.
Try something more like this instead:
function TForm1.FindNumberInString(InString: string): TNumberInString;
var
i, j: Integer;
begin
Result.TNumber := InString;
Result.TIsNumber := False;
for i := 1 to Length(InString) do
begin
if InString[i] in ['0'..'9'] then
begin
Result.TNumber := InString[i];
Result.TIsNumber := True;
For j := i+1 to Length(InString) do
begin
if not (InString[i] in ['0'..'9']) then
Break;
Result.TNumber := Result.TNumber + InString[j];
end;
Exit;
end;
end;
end;
I would use a Regular Expressions, and extend your record with a constructor
uses
RegularExpressions;
{$R *.dfm}
type
TNumberInString = Packed Record
TNumber: string;
TIsNumber: Boolean;
constructor Create(const Value: String);
end;
{ TNumberInString }
constructor TNumberInString.Create(const Value: String);
var
Match: TMatch;
begin
Match := TRegEx.Create('\d+').Match(Value);
TIsNumber := Match.Success;
if TIsNumber then
TNumber := Match.Value
else
TNumber := Value;
end;
Then only thing left is to call it:
procedure TForm30.FormCreate(Sender: TObject);
begin
Memo1.Lines.Clear;
Memo1.Lines.Add(TNumberInString.Create('XYZ').TNumber);
Memo1.Lines.Add(TNumberInString.Create('XYZ60').TNumber);
Memo1.Lines.Add(TNumberInString.Create('XYZ 60').TNumber);
end;
And show the result:
Every now and then I use the watch window to display strings which contain sql statements.
Now I select Copy Value from the context menu and get
'SELECT NAME FROM SAMPLE_TABLE WHERE FIRST_NAME = ''George'''#$D#$A
Of course, this statement has to be reformatted if I want to execute it in a sql tool displaying the results. This is a little bit annoying.
Is there a trick / workaround for that?
I thought it would be amusing to try and work out a way to do this by adding something inside the IDE, mainly because when you posted your q, I didn't have a clue how to. It turns out that you can do it quite easily using a custom OTA package containing a unit like the one below.
Btw, I'm particularly obliged to Rob Kennedy for pointing out in another SO question that the IDE has a Screen object just like any other. That provides an easy way into the problem, bypassing the maze of OTA interfaces I've usually had to work with to code an IDE add-in.
It works by
Finding the Watch Window,
Finding the Copy Watch value item in its context menu & adding a new menu item after it
Using the OnClick handler of the new item to pick up the value from the Watch Window's focused item, re-formatting it as required, then pasting it to the Clipboard.
So far as using OTA services is concerned, it doesn't do anything fancy, but with the IDE I think the KISS principle applies.
Code:
unit IdeMenuProcessing;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ToolsAPI, Menus, ClipBrd, ComCtrls;
type
TOtaMenuForm = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
OurMenuItem : TMenuItem;
WatchWindow : TForm;
WWListView : TListView;
procedure GetWatchValue(Sender : TObject);
end;
var
OtaMenuForm: TOtaMenuForm;
procedure Register;
implementation
{$R *.dfm}
procedure ShowMenus;
begin
OtaMenuForm := TOtaMenuForm.Create(Nil);
OtaMenuForm.Show;
end;
procedure Register;
begin
ShowMenus;
end;
procedure TOtaMenuForm.FormCreate(Sender: TObject);
var
i : Integer;
S : String;
PM : TPopUpMenu;
Item : TMenuItem;
begin
// First create a menu item to insert in the Watch Window's context menu
OurMenuItem := TMenuItem.Create(Self);
OurMenuItem.OnClick := GetWatchValue;
OurMenuItem.Caption := 'Get processed watch value';
WatchWindow := Nil;
WWListView := Nil;
// Next, iterate the IDE's forms to find the Watch Window
for i := 0 to Screen.FormCount - 1 do begin
S := Screen.Forms[i].Name;
if CompareText(S, 'WatchWindow') = 0 then begin // < Localize if necessary
WatchWindow := Screen.Forms[i];
Break;
end;
end;
Assert(WatchWindow <> Nil);
if WatchWindow <> Nil then begin
// Next, scan the Watch Window's context menu to find the existing "Copy watch value" entry
// and insert our menu iem after it
PM := WatchWindow.PopUpMenu;
for i:= 0 to PM.Items.Count - 1 do begin
Item := PM.Items[i];
if CompareText('Copy Watch &Value', Item.Caption) = 0 then begin // < Localize if necessary
PM.Items.Insert(i + 1, OurMenuItem);
Break;
end;
end;
// Now, find the TListView in the Watch Window
for i := 0 to WatchWindow.ComponentCount - 1 do begin
if WatchWindow.Components[i] is TListView then begin
WWListView := WatchWindow.Components[i] as TListView;
Break;
end;
end;
Assert(WWListView <> Nil);
end;
end;
procedure TOtaMenuForm.GetWatchValue(Sender: TObject);
var
WatchValue : String;
begin
// This is called when the Watch Window menu item we added is clicked
if WWListView.ItemFocused = Nil then begin
Memo1.Lines.Add('no Watch selected');
exit;
end;
WatchValue := WWListView.ItemFocused.SubItems[0];
WatchValue := StringReplace(WatchValue, #$D#$A, ' ', [rfreplaceAll]);
if WatchValue[1] = '''' then
Delete(WatchValue, 1, 1);
if WatchValue[Length(WatchValue)] = '''' then
WatchValue := Copy(WatchValue, 1, Length(WatchValue) - 1);
// [etc]
ClipBoard.AsText := WatchValue;
Memo1.Lines.Add('>' + WatchValue + '<');
end;
initialization
finalization
if Assigned(OTAMenuForm) then begin
OTAMenuForm.Close;
FreeAndNil(OTAMenuForm);
end;
end.
Btw, I wrote this in D7 because I use that as a sort of lowest common denominator for SO answers because its quite obvious that a large number of people here still use it. Later versions have additional string functions, such as the AniDequotedStr mentioned in a comment, which might be helpful in reformatting the watch value.
Update: According to the OP, the above doesn't work with XE3 because the watch window is implemented using a TVirtualStringTree rather than a TListView. The reason I used the ListView was that I found that picking up the Watch value from the Clipboard (after simulating a click on the context menu's Copy Watch Value) to process it wasn't very reliable. That seems to have improved in XE4 (I don't have XE3 to test), so here's a version that seems to work in XE4:
Update #2: The OP mentioned that the previous version of the code below failed the WatchWindow <> Nil assertion when Delphi is first started. I imagine the reason is that the code is called before the Watch Window has been created in the IDE. I've re-arranged the code an added an OTANotifier that's used to get the notification that a project desktop has been loaded, ad uses that to called the new SetUp routine.
unit IdeMenuProcessing;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ToolsAPI, Menus, ClipBrd, ComCtrls;
type
TIdeNotifier = class(TNotifierObject, IOTANotifier, IOTAIDENotifier)
protected
procedure AfterCompile(Succeeded: Boolean);
procedure BeforeCompile(const Project: IOTAProject; var Cancel: Boolean);
procedure FileNotification(NotifyCode: TOTAFileNotification;
const FileName: string; var Cancel: Boolean);
end;
TOtaMenuForm = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
IsSetUp : Boolean;
ExistingMenuItem,
OurMenuItem : TMenuItem;
WatchWindow : TForm;
Services: IOTAServices;
Notifier : TIdeNotifier;
NotifierIndex: Integer;
procedure GetWatchValue(Sender : TObject);
procedure SetUp;
end;
var
OtaMenuForm: TOtaMenuForm;
procedure Register;
implementation
{$R *.dfm}
procedure ShowMenus;
begin
OtaMenuForm := TOtaMenuForm.Create(Nil);
OtaMenuForm.Services := BorlandIDEServices as IOTAServices;
OtaMenuForm.NotifierIndex := OtaMenuForm.Services.AddNotifier(TIdeNotifier.Create);
OtaMenuForm.Show;
end;
procedure Register;
begin
ShowMenus;
end;
procedure TOtaMenuForm.SetUp;
var
i : Integer;
S : String;
PM : TPopUpMenu;
Item : TMenuItem;
begin
if IsSetUp then exit;
// First create a menu item to insert in the Watch Window's context menu
OurMenuItem := TMenuItem.Create(Self);
OurMenuItem.OnClick := GetWatchValue;
OurMenuItem.Caption := 'Get processed watch value';
WatchWindow := Nil;
// Next, iterate the IDE's forms to find the Watch Window
for i := 0 to Screen.FormCount - 1 do begin
S := Screen.Forms[i].Name;
if CompareText(S, 'WatchWindow') = 0 then begin
WatchWindow := Screen.Forms[i];
Break;
end;
end;
Assert(WatchWindow <> Nil);
if WatchWindow <> Nil then begin
// Next, scan the Watch Window's context menu to find the existing "Copy watch value" entry
// and insert our menu item after it
PM := WatchWindow.PopUpMenu;
for i:= 0 to PM.Items.Count - 1 do begin
Item := PM.Items[i];
if CompareText('Copy Watch &Value', Item.Caption) = 0 then begin
ExistingMenuItem := Item;
PM.Items.Insert(i + 1, OurMenuItem);
if ExistingMenuItem.Action <> Nil then
Memo1.Lines.Add('Has action')
else
Memo1.Lines.Add('No action');
Break;
end;
end;
end;
Caption := 'Setup complete';
IsSetUp := True;
end;
procedure TOtaMenuForm.FormCreate(Sender: TObject);
begin
IsSetUp := False;
end;
procedure TOtaMenuForm.GetWatchValue(Sender: TObject);
var
S,
WatchValue : String;
TL : TStringList;
i : Integer;
begin
// This is called when the Watch Window menu item we added is clicked
ExistingMenuItem.Click;
WatchValue := ClipBoard.AsText;
WatchValue := StringReplace(WatchValue, '#$D#$A', #$D#$A, [rfreplaceAll]);
if WatchValue <> '' then begin
TL := TStringList.Create;
try
TL.Text := WatchValue;
WatchValue := '';
for i := 0 to TL.Count - 1 do begin
S := TL[i];
if S[1] = '''' then
Delete(S, 1, 1);
if S[Length(S)] = '''' then
S := Copy(S, 1, Length(S) - 1);
if WatchValue <> '' then
WatchValue := WatchValue + ' ';
WatchValue := WatchValue + S;
end;
finally
TL.Free;
end;
// [etc]
end;
ClipBoard.AsText := WatchValue;
Memo1.Lines.Add('>' + WatchValue + '<');
end;
{ TIdeNotifier }
procedure TIdeNotifier.AfterCompile(Succeeded: Boolean);
begin
end;
procedure TIdeNotifier.BeforeCompile(const Project: IOTAProject;
var Cancel: Boolean);
begin
end;
procedure TIdeNotifier.FileNotification(NotifyCode: TOTAFileNotification;
const FileName: string; var Cancel: Boolean);
begin
if NotifyCode = ofnProjectDesktopLoad then
OTAMenuForm.SetUp
end;
initialization
finalization
if Assigned(OTAMenuForm) then begin
OTAMenuForm.Services.RemoveNotifier(OTAMenuForm.NotifierIndex);
OTAMenuForm.Close;
FreeAndNil(OTAMenuForm);
end;
end.
I'm posting this as a separate answer because it uses a different implementation
based on the ToolsAPI's debugger visualizers. There are examples in the Visualizers
sub-folder of the Delphi source code. The one which looked most promising as a
starting point is the example in the StringListVisualizer.Pas file. However, I found
that impenetrable on the first few readings and it turned out that it didn't actually
do what I was hoping for.
The code below, which of course needs to be compiled into an IDE package which
requires the rtl and designide units, is based upon the much simpler DateTime
sample visualizer, but adapted to the Text property of TStrings objects. This adaptation still required quite a lot of work, and that's the main reason I'm posting this additional answer, to save others some head-scratching.
Normally, the Text property of a TStrings variable is displayed in the Watch Window as one or more text lines surrounded by single quotes and separated by the string #$D#$A. The code removes the single quotes and replaces the #$D#$A by a space. This isdone inside the GetReplacementValue function near the top of the code. The rest of the code is just the baggage that you need to include to implement a visualizer, and there's quite a lot of it, even in this rather minimalist implementation.
Once the package is installed, as well as being displayed in the Watch Window,
the Text property can be pasted to the Clipboard using the Copy Watch Value
entry on the Watch Window's context menu.
Code (written for and tested in XE4):
{*******************************************************}
{ }
{ RadStudio Debugger Visualizer Sample }
{ Copyright(c) 2009-2013 Embarcadero Technologies, Inc. }
{ }
{*******************************************************}
{Adapted by Martyn Ayers, Bristol, UK Oct 2015}
unit SimpleTStringsVisualizeru;
interface
procedure Register;
implementation
uses
Classes, Forms, SysUtils, ToolsAPI;
resourcestring
sVisualizerName = 'TStrings Simple Visualizer for Delphi';
sVisualizerDescription = 'Simplifies TStrings Text property format';
const
CRLFReplacement = ' ';
type
TDebuggerSimpleTStringsVisualizer = class(TInterfacedObject,
IOTADebuggerVisualizer, IOTADebuggerVisualizerValueReplacer,
IOTAThreadNotifier, IOTAThreadNotifier160)
private
FNotifierIndex: Integer;
FCompleted: Boolean;
FDeferredResult: string;
public
{ IOTADebuggerVisualizer }
function GetSupportedTypeCount: Integer;
procedure GetSupportedType(Index: Integer; var TypeName: string;
var AllDescendants: Boolean);
function GetVisualizerIdentifier: string;
function GetVisualizerName: string;
function GetVisualizerDescription: string;
{ IOTADebuggerVisualizerValueReplacer }
function GetReplacementValue(const Expression, TypeName, EvalResult: string): string;
{ IOTAThreadNotifier }
procedure EvaluteComplete(const ExprStr: string; const ResultStr: string;
CanModify: Boolean; ResultAddress: Cardinal; ResultSize: Cardinal;
ReturnCode: Integer);
procedure ModifyComplete(const ExprStr: string; const ResultStr: string;
ReturnCode: Integer);
procedure ThreadNotify(Reason: TOTANotifyReason);
procedure AfterSave;
procedure BeforeSave;
procedure Destroyed;
procedure Modified;
{ IOTAThreadNotifier160 }
procedure EvaluateComplete(const ExprStr: string; const ResultStr: string;
CanModify: Boolean; ResultAddress: TOTAAddress; ResultSize: LongWord;
ReturnCode: Integer);
end;
TTypeLang = (tlDelphi, tlCpp);
// The following function is the one which actually changes the TStrings
// representation in the Watch Window
//
// Normally, the Text property of TStrings variable is displayed in the Watch Window
// and Evaluate window as one or more text lines surrounded by single quotes
// and separated by the string #$D#$A
//
// This implementation removes the single quotes and replaces the #$D#$A
// by a space
//
// Note the addition of '.Text' to the expression which gets evaluated; this is to
// produce the desired result when using the 'Copy Watch Value' item in the
// Watch Window context menu.
function TDebuggerSimpleTStringsVisualizer.GetReplacementValue(
const Expression, TypeName, EvalResult: string): string;
var
Lang: TTypeLang;
i: Integer;
CurProcess: IOTAProcess;
CurThread: IOTAThread;
ResultStr: array[0..4095] of Char; // was 255
CanModify: Boolean;
ResultAddr, ResultSize, ResultVal: LongWord;
EvalRes: TOTAEvaluateResult;
DebugSvcs: IOTADebuggerServices;
function FormatResult(const Input: string; out ResStr: string): Boolean;
var
TL : TStringList;
i : Integer;
S : String;
const
CRLFDisplayed = '#$D#$A';
begin
Result := True;
ResStr := '';
TL := TStringList.Create;
try
S := Input;
S := StringReplace(S, CRLFDisplayed, #13#10, [rfReplaceAll]);
TL.Text := S;
for i := 0 to TL.Count - 1 do begin
S := TL[i];
if S <> '' then begin
if S[1] = '''' then // Remove single quote at start of line
Delete(S, 1, 1);
if S[Length(S)] = '''' then // Remove single quote at end of line
S := Copy(S, 1, Length(S) - 1);
end;
if ResStr <> '' then
ResStr := ResStr + CRLFReplacement;
ResStr := ResStr + S;
end;
finally
TL.Free;
end;
end;
begin
Lang := tlDelphi;
if Lang = tlDelphi then
begin
if Supports(BorlandIDEServices, IOTADebuggerServices, DebugSvcs) then
CurProcess := DebugSvcs.CurrentProcess;
if CurProcess <> nil then
begin
CurThread := CurProcess.CurrentThread;
if CurThread <> nil then
begin
EvalRes := CurThread.Evaluate(Expression + '.Text', #ResultStr, Length(ResultStr),
CanModify, eseAll, '', ResultAddr, ResultSize, ResultVal, '', 0);
if EvalRes = erOK then
begin
Result := ResultStr;
end else if EvalRes = erDeferred then
begin
FCompleted := False;
FDeferredResult := '';
FNotifierIndex := CurThread.AddNotifier(Self);
while not FCompleted do
DebugSvcs.ProcessDebugEvents;
CurThread.RemoveNotifier(FNotifierIndex);
FNotifierIndex := -1;
if (FDeferredResult = '') then
Result := EvalResult
else
FormatResult(FDeferredResult, Result);
end;
end;
end;
end
else
;
end;
procedure TDebuggerSimpleTStringsVisualizer.AfterSave;
begin
// don't care about this notification
end;
procedure TDebuggerSimpleTStringsVisualizer.BeforeSave;
begin
// don't care about this notification
end;
procedure TDebuggerSimpleTStringsVisualizer.Destroyed;
begin
// don't care about this notification
end;
procedure TDebuggerSimpleTStringsVisualizer.Modified;
begin
// don't care about this notification
end;
procedure TDebuggerSimpleTStringsVisualizer.ModifyComplete(const ExprStr,
ResultStr: string; ReturnCode: Integer);
begin
// don't care about this notification
end;
procedure TDebuggerSimpleTStringsVisualizer.EvaluteComplete(const ExprStr,
ResultStr: string; CanModify: Boolean; ResultAddress, ResultSize: Cardinal;
ReturnCode: Integer);
begin
EvaluateComplete(ExprStr, ResultStr, CanModify, TOTAAddress(ResultAddress),
LongWord(ResultSize), ReturnCode);
end;
procedure TDebuggerSimpleTStringsVisualizer.EvaluateComplete(const ExprStr,
ResultStr: string; CanModify: Boolean; ResultAddress: TOTAAddress; ResultSize: LongWord;
ReturnCode: Integer);
begin
FCompleted := True;
if ReturnCode = 0 then
FDeferredResult := ResultStr;
end;
function TDebuggerSimpleTStringsVisualizer.GetSupportedTypeCount: Integer;
begin
Result := 1;
end;
procedure TDebuggerSimpleTStringsVisualizer.GetSupportedType(Index: Integer; var TypeName: string;
var AllDescendants: Boolean);
begin
AllDescendants := True;
TypeName := 'TStrings';
end;
function TDebuggerSimpleTStringsVisualizer.GetVisualizerDescription: string;
begin
Result := sVisualizerDescription;
end;
function TDebuggerSimpleTStringsVisualizer.GetVisualizerIdentifier: string;
begin
Result := ClassName;
end;
function TDebuggerSimpleTStringsVisualizer.GetVisualizerName: string;
begin
Result := sVisualizerName;
end;
procedure TDebuggerSimpleTStringsVisualizer.ThreadNotify(Reason: TOTANotifyReason);
begin
// don't care about this notification
end;
var
TStringsVis: IOTADebuggerVisualizer;
procedure Register;
begin
TStringsVis := TDebuggerSimpleTStringsVisualizer.Create;
(BorlandIDEServices as IOTADebuggerServices).RegisterDebugVisualizer(TStringsVis);
end;
procedure RemoveVisualizer;
var
DebuggerServices: IOTADebuggerServices;
begin
if Supports(BorlandIDEServices, IOTADebuggerServices, DebuggerServices) then
begin
DebuggerServices.UnregisterDebugVisualizer(TStringsVis);
TStringsVis := nil;
end;
end;
initialization
finalization
RemoveVisualizer;
end.
I have a problem to sync the GUI of server. I'm using Delphi 2007 and Indy 10.1.5.
This is my case:
Server send to all connected client a hearbit (this is the message send from server --> "REQ|HeartBit")
Client response to server with "I'm alive" (this is the message send from client --> "ANS|USERNAME|I'm alive"
In onExecute procedure of the TIdTCPServer I want to see the answer of the client in a TlistView of server, so I have done like in this Link
When I start my application with two process client connected (that are runs in my PC) and send a hearbit message to clients, I see in the listview of server this situation:
REQ|HeartBit (send to Client1)
REQ|HeartBit (send to Client2)
ANS|Client2|I'm Alive
ANS|Client2|I'm Alive
two response message from Client2 (!?!?)
Where is my mistake?
Sorry for my poor English.
Thanks
The code of server side is this:
type
TLog = class(TIdSync)
private
FMsg : string;
protected
procedure DoSynchronize; override;
public
constructor Create(const AMsg: String);
//class procedure AddMsg(const AMsg: String);
end;
// procedure that add items in listview of server
procedure WriteListLog(aTimeStamp : TDateTime;strMessaggio: String);
implementation
procedure TLog.DoSynchronize;
begin
WriteListLog(Now,FMsg);
end
procedure TForm1.tsExecute(AContext: TIdContext);
var
Ctx: TMyContext;
tmp : String;
sync : Tlog;
begin
Ctx := TMyContext(AContext);
tmp := Ctx.Connection.IOHandler.ReadLn;
sync := Tlog.Create(tmp);
try
sync.FMsg := tmp;
sync.Synchronize;
finally
Sync.Free;
end;
end;
If I add lockList in OnExecute I have this correct sequence of message
REQ|HeartBit (send to Client1)
REQ|HeartBit (send to Client2)
ANS|Client1|I'm Alive
ANS|Client2|I'm Alive
Is it Correct?
procedure TForm1.tsExecute(AContext: TIdContext);
var
Ctx: TMyContext;
tmp : String;
sync : Tlog;
begin
Ctx := TMyContext(AContext);
tmp := Ctx.Connection.IOHandler.ReadLn;
Ctx.FContextList.LockList;
try
sync := Tlog.Create(tmp);
try
sync.FMsg := tmp;
sync.Synchronize;
finally
Sync.Free;
end;
finally
Ctx.FContextList.UnlockList;
end;
end;
Update
In my project, the listView and WriteListLog() is in the unit FLogMsg, not in the same unit of the IdTCSPServer.
This is how is defined the tlistview in dfm
object ListLog: TListView
Left = 0
Top = 0
Width = 737
Height = 189
Align = alClient
Columns = <
item
Caption = 'Data'
Width = 140
end
item
Caption = 'Da'
end
item
Caption = 'A'
end
item
Caption = 'Tipo'
end
item
Caption = 'Messaggio'
Width = 900
end>
ColumnClick = False
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
FlatScrollBars = True
OwnerData = True
ReadOnly = True
ParentFont = False
TabOrder = 0
ViewStyle = vsReport
OnData = ListLogData
end
Code of unit FlogMsg:
type
TTipoMessaggio = (tmSend,tmReceived,tmSystem);
TDataItem = class
private
FDITimeStamp: TDateTime;
FDIRecipient: String;
FDISender: String;
FDITipo: TTipoMessaggio;
FDIMessaggio: String;
public
property DITimeStamp: TDateTime read FDITimeStamp;
property DISender : String read FDISender;
property DIRecipient : String read FDIRecipient;
property DITipo : TTipoMessaggio read FDITipo;
property DIMessaggio: String read FDIMessaggio;
end;
TfrmLog = class(TForm)
ListLog: TListView;
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
procedure ListLogData(Sender: TObject; Item: TListItem);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
FItems: TObjectList;
FActiveItems: TList;
FFilterLogStation: String;
procedure SetFilterLogStation(const Value: String);
public
{ Public declarations }
property FilterLogStation : String read FFilterLogStation write SetFilterLogStation;
end;
procedure WriteListLog(aTimeStamp : TDateTime;
aSender,aRecipient: String;aTipo:TTipoMessaggio;strMessaggio: String);
var
frmLog: TfrmLog;
implementation
{$R *.dfm}
procedure WriteListLog(aTimeStamp : TDateTime;
aSender,aRecipient: String;aTipo:TTipoMessaggio;strMessaggio: String);
var
DataItem: TDataItem;
begin
DataItem := TDataItem.Create;
try
DataItem.FDITimeStamp := aTimeStamp;
DataItem.FDISender := aSender;
DataItem.FDIRecipient := aRecipient;
DataItem.FDITipo := aTipo;
DataItem.FDIMessaggio := strMessaggio;
frmLog.FItems.Add(DataItem);
if (frmLog.FilterLogStation = '') or (frmLog.FilterLogStation = aRecipient) or
(frmLog.FilterLogStation = aSender) then
begin
frmLog.FActiveItems.Add(DataItem);
frmLog.ListLog.AddItem('',DataItem);
end;
except
DataItem.Free;
raise;
end;
frmLog.ListLog.Repaint;
end;
procedure TfrmLog.FormCreate(Sender: TObject);
begin
FFilterLogStation := '';
FItems := TObjectList.Create;
FActiveItems := TList.Create;
end;
procedure TfrmLog.FormDestroy(Sender: TObject);
begin
FActiveItems.clear;
FreeAndNil(FActiveItems);
FreeAndNil(FItems);
end;
procedure TfrmLog.ListLogData(Sender: TObject; Item: TListItem);
var
DataItem: TDataItem;
begin
DataItem := FActiveItems[Item.Index];
Item.Caption := FormatDateTime('dd/mm/yyy hh.nn.ss', DataItem.DITimeStamp);
Item.SubItems.Add(DataItem.DISender);
Item.SubItems.Add(DataItem.DIRecipient);
// Tipo Messaggio
case DataItem.DITipo of
tmSend: Item.SubItems.Add('Inviato');
tmReceived: Item.SubItems.Add('Ricevuto');
tmSystem: Item.SubItems.Add('Sistema');
end;
Item.SubItems.Add(DataItem.DIMessaggio);
Item.MakeVisible(true);
end;
procedure TfrmLog.SetFilterLogStation(const Value: String);
var
I: Integer;
begin
FFilterLogStation := Value;
ListLog.Items.BeginUpdate;
try
ListLog.Clear;
FActiveItems.Clear;
for I := 0 to FItems.Count - 1 do
if (CompareText(UpperCase(FFilterLogStation),UpperCase(TDataItem(FItems[I]).DISender)) = 0) or
(CompareText(UpperCase(FFilterLogStation),UpperCase(TDataItem(FItems[I]).DIRecipient)) = 0)
or (FFilterLogStation = '') then
begin
FActiveItems.Add(FItems[I]);
end;
ListLog.Items.Count := FActiveItems.Count;
finally
ListLog.Items.EndUpdate;
ListLog.Repaint;
end;
end;
procedure TfrmLog.FormDestroy(Sender: TObject);
begin
FActiveItems.clear;
FreeAndNil(FActiveItems);
FreeAndNil(FItems);
end;
UPDATE 2 - Try with TMemo
this is the result:
(First SendBroadCast HeartBit)
ANS|CARICO1|I'm Alive
ANS|CARICO2|I'm Alive
(Second SendBroadCast HeartBit)
ANS|CARICO1|I'm Alive
ANS|CARICO2|I'm Alive
(Third SendBroadCast HeartBit)
ANS|CARICO1|I'm Alive
ANS|CARICO1|I'm Alive
I add a TStringList variable in my TMyContext class.
In debug session, for each Context, if I inspect the queue of message that is saved on my TStringList variable the messages are correct!
So, I think that the problem is in Synchronize...
type
TTipoStazione = (tsNone,tsCarico,tsScarico);
TLog = class(TIdSync)
private
FMsg : string;
FFrom : String;
protected
procedure DoSynchronize; override;
public
end;
TMyContext = class(TIdContext)
public
IP: String;
UserName: String;
Stazione : Integer;
tipStaz : TTipoStazione;
Con: TDateTime;
isValid : Boolean;
ls : TStringList;
// compname:string;
procedure ProcessMsg;
end;
TForm1 = class(TForm)
ts: TIdTCPServer;
Memo1: TMemo;
btconnect: TButton;
edport: TEdit;
Button2: TButton;
procedure btconnectClick(Sender: TObject);
procedure tsConnect(AContext: TIdContext);
procedure tsExecute(AContext: TIdContext);
procedure tsDisconnect(AContext: TIdContext);
constructor Create(AOwner: TComponent);override;
procedure FormDestroy(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
procedure SendMsgBroadcast(aMsg : String);
public
{ Public declarations }
procedure MyWriteListLog(strMessaggio : String);
end;
implementation
constructor TLog.Create(const aFrom: String; const AMsg: String);
begin
inherited Create;
FMsg := AMsg;
FFrom := aFrom;
end;
procedure TLog.DoSynchronize;
begin
Form1.MyWriteListLog(FMsg);
end;
procedure TMyContext.ProcessMsg;
var
str,TypeMsg:string;
myTLog: TLog;
begin
if Connection.IOHandler.InputBufferIsEmpty then
exit;
str:=self.Connection.IOHandler.ReadLn;
ls.Add('1='+str);
myTLog := Tlog.Create;
try
myTLog.FMsg := str;
myTLog.FFrom := UserName;
myTLog.Synchronize;
ls.Add('2='+str);
finally
myTLog.Free;
end;
end;
constructor TForm1.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ts.ContextClass := TMyContext;
DMVern := TDMVern.Create(nil);
end;
procedure TForm1.btconnectClick(Sender: TObject);
begin
ts.DefaultPort:=strtoint(edport.Text);
ts.Active:=true;
MyWriteListLog('Listening');
end;
procedure TForm1.tsConnect(AContext: TIdContext);
var
strErr : String;
I: Integer;
tmpNrStaz: String;
tmpMsg : String;
begin
strErr := '';
ts.Contexts.LockList;
try
with TMyContext(AContext) do
begin
ls := TStringList.Create;
isValid := false;
Con := Now;
if (Connection.Socket <> nil) then
IP :=Connection.Socket.Binding.PeerIP;
tmpMsg := Connection.IOHandler.ReadLn;
try
if not (Pos('START|',tmpMsg) > 0) then
begin
strErr := 'Comando non valido';
exit;
end;
UserName := Copy(tmpMsg,Length('START|')+1,Length(tmpMsg));
if Trim(UserName) = '' then
begin
strErr := 'How Are You?';
exit;
end;
tipStaz := tsNone;
if UpperCase(Copy(UserName,1,6)) = 'CARICO' then
tipStaz := tsCarico
else if UpperCase(Copy(UserName,1,7)) = 'SCARICO' then
tipStaz := tsCarico;
if tipStaz = tsNone then
begin
strErr := 'Tipo Stazione non valida.';
exit;
end;
tmpNrStaz := '';
for I := Length(UserName) downto 1 do
begin
if (UserName[i] in ['0'..'9']) then
tmpNrStaz:= UserName[i] + tmpNrStaz
else if tmpNrStaz <> '' then
break;
end;
if tmpNrStaz = '' then
begin
strErr := 'Numero Stazione non specificato.';
exit;
end;
Stazione := StrToInt(tmpNrStaz);
isValid := true;
tmpMsg := 'HELLO|' + UserName;
Connection.IOHandler.WriteLn(tmpMsg);
finally
if strErr <> '' then
begin
Connection.IOHandler.WriteLn(strErr);
Connection.Disconnect;
end;
end;
end;
finally
ts.Contexts.UnlockList;
end;
end;
procedure TForm1.tsExecute(AContext: TIdContext);
var
Ctx: TMyContext;
tmp : String;
begin
Ctx := TMyContext(AContext);
Ctx.ProcessMsg;
end;
procedure TForm1.tsDisconnect(AContext: TIdContext);
begin
TMyContext(AContext).ProcessMsg;
end;
procedure TForm1.MyWriteListLog(strMessaggio: String);
begin
Memo1.Lines.Add(strMessaggio);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
aMsg: String;
begin
aMsg := 'REQ|HeartBit';
SendMsgBroadcast(aMsg);
end;
procedure TForm1.SendMsgBroadcast(aMsg: String);
var
List: TList;
I: Integer;
Context: TMyContext;
begin
List := ts.Contexts.LockList;
try
for I := 0 to List.Count-1 do
begin
Context := TMyContext(List[I]);
if Context.isValid then
begin
try
Context.Connection.IOHandler.WriteLn(aMsg);
except
end;
end;
end;
finally
ts.Contexts.UnlockList;
end;
end;
You are using a virtual ListView, but I see two mistakes you are making with it:
You are calling AddItem() and Clear() on it. Do not do that. The whole point of a virtual ListView is to not put any real data in it at all. After you add or remove objects in your FActiveItems list, all you have to do is update the TListView.Items.Count property to reflect the new item count. It will invalidate itself by default to trigger a repaint (but if you want to trigger a repaint manually, use Invalidate() instead of Repaint(), and call it only when you have done something to modify FActiveItems).
Your OnData handler is calling TListItem.MakeVisible(). That call does not belong in that event, it belongs in WriteListLog() instead. OnData triggered whenever the ListView needs data for an item for any reason, including during drawing. Don't perform any UI management operations in a data management event.
Try this instead:
procedure WriteListLog(aTimeStamp : TDateTime;
aSender,aRecipient: String;aTipo:TTipoMessaggio;strMessaggio: String);
var
DataItem: TDataItem;
Index, ActiveIndex: Integer;
begin
DataItem := TDataItem.Create;
try
DataItem.FDITimeStamp := aTimeStamp;
DataItem.FDISender := aSender;
DataItem.FDIRecipient := aRecipient;
DataItem.FDITipo := aTipo;
DataItem.FDIMessaggio := strMessaggio;
Index := frmLog.FItems.Add(DataItem);
try
if (frmLog.FilterLogStation = '') or
AnsiSameText(frmLog.FilterLogStation, aRecipient) or
AnsiSameText(frmLog.FilterLogStation, aSender) then
begin
ActiveIndex := frmLog.FActiveItems.Add(DataItem);
frmLog.ListLog.Items.Count := frmLog.FActiveItems.Count;
frmLog.Items[ActiveIndex].MakeVisible(true);
end;
except
frmLog.FItems.Delete(Index);
DataItem := nil;
raise;
end;
except
DataItem.Free;
raise;
end;
end;
procedure TfrmLog.FormCreate(Sender: TObject);
begin
FFilterLogStation := '';
FItems := TObjectList.Create(True);
FActiveItems := TList.Create;
end;
procedure TfrmLog.FormDestroy(Sender: TObject);
begin
FItems.Free;
FActiveItems.Free;
end;
procedure TfrmLog.ListLogData(Sender: TObject; Item: TListItem);
var
DataItem: TDataItem;
begin
DataItem := TDataItem(FActiveItems[Item.Index]);
Item.Caption := FormatDateTime('dd/mm/yyy hh.nn.ss', DataItem.DITimeStamp);
Item.SubItems.Add(DataItem.DISender);
Item.SubItems.Add(DataItem.DIRecipient);
// Tipo Messaggio
case DataItem.DITipo of
tmSend: Item.SubItems.Add('Inviato');
tmReceived: Item.SubItems.Add('Ricevuto');
tmSystem: Item.SubItems.Add('Sistema');
else
Item.SubItems.add('');
end;
Item.SubItems.Add(DataItem.DIMessaggio);
end;
procedure TfrmLog.SetFilterLogStation(const Value: String);
var
I: Integer;
DataItem: TDataItem;
begin
if FFilterLogStation = Value then Exit;
ListLog.Items.Count := 0;
FActiveItems.Clear;
FFilterLogStation := Value;
try
for I := 0 to FItems.Count - 1 do
begin
DataItem := TDataItem(FItems[I]);
if (FFilterLogStation = '') or
AnsiSameText(FFilterLogStation, DataItem.DISender) or
AnsiSameText(FFilterLogStation), DataItem.DIRecipient) then
begin
FActiveItems.Add(DataItem);
end;
end;
finally
ListLog.Items.Count := FActiveItems.Count;
end;
end;
I have a source that promises to get the active url from any browser using UIAutomation, but I have difficulty about how to call the main function and show result in a ListBox for example. Then, how would it?
Here is my code:
uses
UIAutomationClient_TLB, activeX;
var
Firefox_quebrou: boolean;
function GetURL(hTargetWnd: HWND): string;
function Enumerar(pParent: IUIAutomationElement; Scope: TreeScope; pCondition: IUIAutomationCondition): String;
var
found : IUIAutomationElementArray;
ALen : Integer;
i : Integer;
iElement : IUIAutomationElement;
retorno: integer;
value : WideString;
iInter: IInterface;
ValPattern : IUIAutomationValuePattern;
begin
Result := '';
Firefox_quebrou := false;
if pParent = nil then
Exit;
pParent.FindAll(Scope, pCondition, found);
found.Get_Length(ALen);
for i := 1 to ALen - 1 do
begin
found.GetElement(i, iElement);
iElement.Get_CurrentControlType(retorno);
if (
(retorno = UIA_EditControlTypeId) or
(retorno = UIA_GroupControlTypeId)
) then //UIA_DocumentControlTypeId
begin
iElement.GetCurrentPattern(UIA_ValuePatternId, iInter);
if Assigned(iInter) then
begin
if iInter.QueryInterface(IID_IUIAutomationValuePattern, ValPattern) = S_OK then
begin
ValPattern.Get_CurrentValue(value);
Result := trim(value);
Firefox_quebrou := true;
Break;
end;
end;
end;
if not Firefox_quebrou then
begin
Result := Enumerar(iElement, Scope, pCondition);
end;
end;
end;
var
UIAuto : IUIAutomation;
Ret : Integer;
RootElement : IUIAutomationElement;
Scope : TreeScope;
varProp : OleVariant;
pCondition : IUIAutomationCondition;
begin
Result := '';
try
UIAuto := CoCUIAutomation.Create;
if Succeeded(UIAuto.ElementFromHandle(hTargetWnd, RootElement)) then
begin
TVariantArg(varProp).vt := VT_BOOL;
TVariantArg(varProp).vbool := True;
UIAuto.CreatePropertyCondition(UIA_IsControlElementPropertyId,
varProp,
pCondition);
Scope := TreeScope_Element or TreeScope_Children;
Result := Enumerar(RootElement, Scope, pCondition);
end;
except
Result := '';
end;
end;
I programming with adodb/dbgo and try to use this code:
procedure TfrMain.dbeNoMejaKeyPress(Sender: TObject; var Key: Char);
begin
dmWarbam.TblTrans_temp.Filtered := False;
dmWarbam.TblTrans_temp.Filter := 'ID_ITEM = ' + QuotedStr(dbeNoMeja.Text);
dmWarbam.TblTrans_temp.Filtered := True;
end;
and
procedure TfrMain.dbeNoMejaChange(Sender: TObject);
begin
dmWarbam.TblTrans_temp.Filtered := False;
dmWarbam.TblTrans_temp.Filter := 'ID_ITEM = ' + QuotedStr(dbeNoMeja.Text);
dmWarbam.TblTrans_temp.Filtered := True;
end;
But none of above can work, when i press key on dbeNoMeja it didn't filter but instead the dataset inserting broken/incomplete data to database.
Can someone give me some example that working (full code)
If the dbedit is connected to the same table as the one you want to filter you have a problem, because the table goes into the dsEdit state once you start entering text.
Use a normal TEdit, and append a wildcard (*) to the string in the filter
dmWarbam.TblTrans_temp.Filter := 'ID_ITEM = ' + QuotedStr(edtNoMeja.Text+'*');
Code example adapted from Delphi-NeftalĂ. Nice and simple!
procedure TForm1.Edit1Change(Sender: TObject);
begin
// incremental search
ClientDataSet1.Locate('FirstName', Edit1.Text, [loCaseInsensitive, loPartialKey]);
Exit;
// actual data filtering
if (Edit1.Text = '') then begin
ClientDataSet1.Filtered := False;
ClientDataSet1.Filter := '';
end
else begin
ClientDataSet1.Filter := 'FirstName >= ' + QuotedStr(Edit1.Text);
ClientDataSet1.Filtered := True;
end;
end;
Setting ClientDataSet's provider to ADO DB (in your case):
Path := ExtractFilePath(Application.ExeName) + 'Data.MDB';
// Exist the MDB?
if FileExists(path) then begin
ClientDataSet1.ProviderName := 'DSProvider';
ADOQ.Open;
ClientDataSet1.Active := True;
ADOQ.Close;
ClientDataSet1.ProviderName := '';
lbldata.Caption := ExtractFileName(path);
Exit;
end;
I found a good solution in Expert Exchange,
unit dbg_filter_u;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Grids, DBGrids, DBTables, Db, StdCtrls;
type
TForm1 = class(TForm)
Table1: TTable;
DataSource1: TDataSource;
Query1: TQuery;
DBGrid1: TDBGrid;
cbFilterBox: TComboBox; //a hidden combobox (Style = csDropDownList)
procedure Table1AfterOpen(DataSet: TDataSet);
procedure Table1AfterPost(DataSet: TDataSet);
procedure DBGrid1TitleClick(Column: TColumn);
procedure cbFilterBoxChange(Sender: TObject);
procedure cbFilterBoxClick(Sender: TObject);
procedure cbFilterBoxExit(Sender: TObject);
private
Procedure FillPickLists(ADBGrid : TDBGrid);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
//For Accessing some Protected Methods
type TCDBGrid = class(TCustomDBGrid);
//Storing the Values into the Picklist-Propertys of the asscociated Columns,
//this may cost time depending on the amount of the dataset
Procedure TForm1.FillPickLists(ADBGrid : TDBGrid);
const
SQL_Text = 'Select Distinct %s From %s';
var
q : TQuery;
i : integer;
Begin
If (Assigned(ADBGrid)) and
(Assigned(ADBGrid.Datasource)) and
(Assigned(ADBGrid.Datasource.DataSet)) Then
Begin
If (ADBGrid.Datasource.DataSet is ttable) Then
begin
q := TQuery.Create(self);
try
try
q.DatabaseName := TTable(ADBGrid.Datasource.DataSet).DataBaseName;
for i := 0 to ADBGrid.Columns.Count - 1 do //for each column
begin
if ADBGrid.Columns[i].Field.FieldKind = fkData then //only physical fields
begin
ADBGrid.Columns[i].ButtonStyle := cbsNone; //avoid button-showing
ADBGrid.Columns[i].PickList.Clear;
q.Close;
q.SQL.text := Format(SQL_Text,[ADBGrid.Columns[i].Field.FieldName,TTable(ADBGrid.Datasource.DataSet).TableName]);
q.Open;
While not q.eof do
begin
ADBGrid.Columns[i].PickList.Add(q.Fields[0].AsString);
q.next;
end;
q.close;
end;
end;
finally
q.free;
end;
except
raise;
end;
end else
Raise exception.Create('This Version works only for TTables');
end else
Raise Exception.Create('Grid not properly Assigned');
end;
//Initial-Fill
procedure TForm1.Table1AfterOpen(DataSet: TDataSet);
begin
FillPickLists(DBGrid1);
end;
//Refill after a change
procedure TForm1.Table1AfterPost(DataSet: TDataSet);
begin
FillPickLists(DBGrid1);
end;
//Show a Dropdownbox for selecting, instead the title on Titleclick
procedure TForm1.DBGrid1TitleClick(Column: TColumn);
var
ARect : Trect;
DummyTC : TColumn;
begin
If column.PickList.Count > 0 then
begin
cbFilterbox.Items.Assign(column.PickList);
ARect := TCDBGrid(Column.Grid).CalcTitleRect(Column,0,DummyTC);
cbfilterBox.top := Column.Grid.Top+1;
cbfilterBox.left := Column.Grid.left+Arect.Left+1;
cbFilterbox.Width := Column.Width;
cbFilterBox.Tag := Integer(Column); //Store the columnPointer
cbFilterBox.Show;
cbFilterBox.BringToFront;
cbFilterBox.DroppedDown := True;
end;
end;
//Build up the Filter
procedure TForm1.cbFilterBoxChange(Sender: TObject);
begin
cbFilterBox.Hide;
if cbFilterBox.Text <> TColumn(cbFilterBox.Tag).Title.Caption then
begin
Case TColumn(cbFilterBox.Tag).Field.DataType of
//Some Fieldtypes
ftstring :
TTable(TDBGrid(TColumn(cbFilterBox.Tag).Grid).Datasource.Dataset).Filter :=
TColumn(cbFilterBox.Tag).Field.FieldName+' = '+QuotedStr(cbFilterBox.Text);
ftInteger,
ftFloat :
TTable(TDBGrid(TColumn(cbFilterBox.Tag).Grid).Datasource.Dataset).Filter :=
TColumn(cbFilterBox.Tag).Field.FieldName+' = '+cbFilterBox.Text;
end;
TTable(TDBGrid(TColumn(cbFilterBox.Tag).Grid).Datasource.Dataset).Filtered := True;
end;
end;
//some Hiding-events
procedure TForm1.cbFilterBoxClick(Sender: TObject);
begin
cbFilterBox.Hide;
end;
procedure TForm1.cbFilterBoxExit(Sender: TObject);
begin
cbFilterBox.Hide;
end;
end.