How to assign a variable to a randomly generated string? - delphi

This is a continuation of my previous question: Can I use a variable created for a component with an on click event?
I have the following code:
procedure TForm1.btnTest1Click(Sender: TObject);
var
i, j: Integer;
sCorrectpanel : string;
function FindPanel(iChar, iNum: Integer): TPanel;
var
cChar: Char;
sPanelName: string;
begin
cChar := Char(Ord('A') + iChar);
sPanelName := 'pnl' + cChar + IntToStr(iNum);
Result := FindComponent(sPanelName) as TPanel;
end;
begin
// reset the OnClick events of the panels first...
for i := 0 to 7 do
begin
for j := 1 to 7 do
FindPanel(i, j).OnClick := pnlWrongClick;
end;
// now, pick a random panel and assign its OnClick event...
FindPanel(Random(8), Random(7) + 1).OnClick := pnlCorrectClick;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize;
end;
procedure TForm1.pnlCorrectClick(Sender: TObject);
begin
ShowMessage('Correct panel');
end;
procedure TForm1.pnlWrongClick(Sender: TObject);
begin
ShowMessage('Wrong panel');
end;
The code randomly selects a panel, but I want to know what panel is selected by assigning the chosen panel's name to a variable.
Someone suggested to:
simply declare your own variable to save the pointer that FindPanel() returns. Also, inside the OnClick handler, you can use the Sender parameter, too"
I just don't know how to do what he recommended, any help please?

You already have a variable, you just need to assign a value to it. For example:
procedure TForm1.btnTest1Click(Sender: TObject);
var
i, j: Integer;
sCorrectPanel : string;
pCurrentPanel : TPanel;
function FindPanel(iChar, iNum: Integer): TPanel;
var
cChar: Char;
sPanelName: string;
begin
cChar := Char(Ord('A') + iChar);
sPanelName := 'pnl' + cChar + IntToStr(iNum);
Result := FindComponent(sPanelName) as TPanel;
end;
begin
// reset the OnClick events of the panels first...
for i := 0 to 7 do
begin
for j := 1 to 7 do
FindPanel(i, j).OnClick := pnlWrongClick;
end;
// now, pick a random panel and assign its OnClick event...
pCorrectPanel := FindPanel(Random(8), Random(7) + 1);
pCorrectPanel.OnClick := pnlCorrectClick;
sCorrectPanel := pCorrectPanel.Name; // <-- here
// use sCorrectPanel as needed...
end;
...
procedure TForm1.pnlCorrectClick(Sender: TObject);
begin
ShowMessage('Correct panel: ' + TPanel(Sender).Name);
end;
procedure TForm1.pnlWrongClick(Sender: TObject);
begin
ShowMessage('Wrong panel: ' + TPanel(Sender).Name);
end;

Related

Can I use a variable created for a component with an on click event?

I have 56 panels, and I created a loop running through them, selecting a random panel. After a random panel is selected, I use FindComponent() with the name of the random panel, and assign a variable to the random panel. Now I have the random panel as a variable, and what I want to do is use the OnClick event with the variable, but I have trouble using it. I want to display a ShowMessage() once that panel has been clicked.
procedure TForm1.btnStartClick(Sender: TObject);
var
iRandomNum, iCharRandom, iCnt: integer;
cChar: char;
sPanelName: string;
begin
Randomize;
iRandomNum := Random(7 - 1 + 1) + 1;
iCharRandom := Random(8 - 1 + 1) + 1;
case iCharRandom of
1:
cChar := 'A';
2:
cChar := 'B';
3:
cChar := 'C';
4:
cChar := 'D';
5:
cChar := 'E';
6:
cChar := 'F';
7:
cChar := 'G';
8:
cChar := 'H';
end;
sPanelName := 'pnl' + cChar + IntToStr(iRandomNum);
for iCnt := 1 to 56 do
begin
pnlCorrect := FindComponent(sPanelName) as TPanel;
end;
pnlCorrect.OnClick := showmessage('Correct panel');
end;
I tried to just display a ShowMessage() with the OnClick event of the variable, but it doesn’t work. I keep getting a runtime error.
Edit:
My new code is added below...
procedure btnTest1Click(Sender: TObject);
private
procedure pnlCorrectClick(Sender: TObject);
procedure pnlWrongClick(Sender: TObject);
{ Private declarations }
public
{ Public declarations }
end;
...
procedure TForm1.btnTest1Click(Sender: TObject);
var
i, j: Integer;
function FindPanel(iChar, iNum: Integer): TPanel;
var
cChar: Char;
sPanelName: string;
begin
cChar := Char(Ord('A') + iChar);
sPanelName := 'pnl' + cChar + IntToStr(iNum);
Result := FindComponent(sPanelName) as TPanel;
redGameTest.Lines.Add(sPanelName);
end;
begin
// reset the OnClick events of the panels first...
for i := 0 to 6 do
begin
for j := 1 to 8 do
FindPanel(i, j).OnClick := pnlWrongClick;
end;
// now, pick a random panel and assign its OnClick event...
FindPanel(Random(8), Random(7) + 1).OnClick := pnlCorrectClick;
end;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize;
end;
...
procedure TForm1.pnlCorrectClick(Sender: TObject);
begin
ShowMessage('Correct panel');
end;
procedure TForm1.pnlWrongClick(Sender: TObject);
begin
ShowMessage('Wrong panel');
end;
The Debugger Exception Notification says:
Project PAT_P.exe raised exception class $C0000005 with message 'access violation at 0x0062a218: write of address 0x00000124'.
You can't assign an expression like showmessage('Correct panel'); directly to an OnClick event as you are trying to do. Events expect to be assigned a class method instead.
Try something more like the following:
type
TForm1 = class(TForm)
published
btnStart: TButton;
PanelA1: TPanel;
...
procedure FormCreate(Sender: TObject);
procedure btnStartClick(Sender: TObject);
...
private
...
procedure pnlCorrectClick(Sender: TObject);
procedure pnlWrongClick(Sender: TObject);
...
end;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize;
end;
procedure TForm1.btnStartClick(Sender: TObject);
var
i, j: Integer;
function FindPanel(iChar, iNum: Integer): TPanel;
var
cChar: Char;
sPanelName: string;
begin
cChar := Char(Ord('A') + iChar);
sPanelName := 'pnl' + cChar + IntToStr(iNum);
Result := FindComponent(sPanelName) as TPanel;
end;
begin
// reset the OnClick events of the panels first...
for i := 0 to 7 do
begin
for j := 1 to 7 do
FindPanel(i, j).OnClick := pnlWrongClick;
end;
// now, pick a random panel and assign its OnClick event...
FindPanel(Random(8), Random(7) + 1).OnClick := pnlCorrectClick;
end;
procedure TForm1.pnlCorrectClick(Sender: TObject);
begin
ShowMessage('Correct panel');
end;
procedure TForm1.pnlWrongClick(Sender: TObject);
begin
ShowMessage('Wrong panel');
end;
That being said, I would suggest putting all of the Panels into an array up front, then you don't have to search for any panels by name when the button is clicked, eg:
type
TForm1 = class(TForm)
published
btnStart: TButton;
PanelA1: TPanel;
...
procedure FormCreate(Sender: TObject);
procedure btnStartClick(Sender: TObject);
...
private
Panels: array[0..55] of TPanel;
procedure pnlCorrectClick(Sender: TObject);
procedure pnlWrongClick(Sender: TObject);
...
end;
...
procedure TForm1.FormCreate(Sender: TObject);
var
i, j, k: Integer;
function FindPanel(iChar, iNum: Integer): TPanel;
var
cChar: Char;
sPanelName: string;
begin
cChar := Char(Ord('A') + iChar);
sPanelName := 'pnl' + cChar + IntToStr(iNum);
Result := FindComponent(sPanelName) as TPanel;
end;
begin
Randomize;
k := 0;
for i := 0 to 7 do
begin
for j := 1 to 7 do
begin
Panels[k] := FindPanel(i, j);
Inc(k);
end;
end;
end;
procedure TForm1.btnStartClick(Sender: TObject);
var
i: integer;
begin
// reset the OnClick event of the panels first...
for i := Low(Panels) to High(Panels) do
Panels[i].OnClick := pnlWrongClick;
// now, pick a random panel and assign its OnClick event...
Panels[Random(56)].OnClick := pnlCorrectClick;
end;
procedure TForm1.pnlCorrectClick(Sender: TObject);
begin
ShowMessage('Correct panel');
end;
procedure TForm1.pnlWrongClick(Sender: TObject);
begin
ShowMessage('Wrong panel');
end;

How capture the active url based in a substring without repetition?

I want capture the url of active window based in a substring and add to Memo only if sActiveURL is different of sOldURL.
The trouble in my code is that always is added to Memo the same url ignoring the verification if sActiveURL <> sOldURL.
How fix this?
Main:
type
TForm1 = class(TForm)
tmr1: TTimer;
mmo1: TMemo;
procedure tmr1Timer(Sender: TObject);
private
{ Private declarations }
sActiveURL,sOldURL : string;
public
{ Public declarations }
end;
var
Form1: TForm1;
Flag: Boolean;
implementation
uses
UIAutomationClient_TLB, Activex, StrUtils;
{$R *.dfm}
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 := '';
Flag := 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
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);
Flag := true;
Break;
end;
end;
end;
if not Flag 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;
procedure TForm1.tmr1Timer(Sender: TObject);
begin
sActiveURL := GetURL(GetForegroundWindow);
if sActiveURL <> sOldURL then
begin
if AnsiContainsText(sActiveURL, 'stackoverflow.com') then
begin
sOldURL := sActiveURL;
mmo1.Lines.Add('['+sActiveURL+']<'+DateToStr(Date)+'>');
end;
end;
end;
UIAutomationClient_TLB.pas
EDITION:
On debug i discovered that none value is attrib to sOldURL variable.
procedure TForm1.tmr1Timer(Sender: TObject);
var
sActiveURL,sOldURL : string;
begin
sActiveURL := GetURL(GetForegroundWindow);
mmo1.Lines.Add('[sOldURL = '+sOldURL+' ]');
mmo1.Lines.Add('[sActiveURL = '+sActiveURL+' ]');
mmo1.Lines.Add('');
if sActiveURL <> sOldURL then
begin
if AnsiContainsText(sActiveURL, 'stackoverflow.com') then
begin
sOldURL := sActiveURL;
mmo1.Lines.Add(sActiveURL);
mmo1.Lines.Add('');
mmo1.Lines.Add('');
end;
end;
end;
The reason is as I shortly described in comments and is visible when the focused window is not your browser, for example your applications wiindow with the mmo1: TMemo:
GetForegroundWindow() returns the window that has focus.
Your GetURL(GetForegroundWindow) searches for an edit control (UIA_EditControlTypeId), of the focused window, and finds your memo control and returns the content of the memo.
Further, if you change focus to your browser, its URL will be correctly recorded in the memo, and if you return focus to your application, the condition if AnsiContainsText(sActiveURL, 'stackoverflow.com') will be true.
You then write to the memo, adding what you think is a real URL, and then this will be repeated for every timer event.
You need to only check the real browser window (skip all other) for its current URL. Try this, if you are using IE, otherwise you must modify FindWindow():
procedure TForm24.tmr1Timer(Sender: TObject);
var //
hIEWnd: HWND; //
begin
hIEWnd := FindWindow('IEFrame', nil); //
sActiveURL := GetURL(hIEWnd); //
// sActiveURL := GetURL(GetForegroundWindow);
if sActiveURL <> sOldURL then
begin
if AnsiContainsText(sActiveURL, 'stackoverflow.com') then
begin
sOldURL := sActiveURL;
mmo1.Lines.Add('[' + sActiveURL + ']<' + DateToStr(Date) + '>');
end;
end;
end;
Modified lines are marked with //

Can I change the display format for strings in the watch list?

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.

How can read in pictures and view them using a button Delphi

I know how to load and view one picture in delphi. However I would like to add a 'next image' button that brings up the next image in the file. I have 5 images in a file and i would like to scroll through them easily using a next button! I have tried to make the next button, But have no idea what code to put in!
Please help thanks.
Gpath is a global string variable.
procedure TPropertyForm.FormCreate(Sender: TObject);
begin
GPath := getcurrentdir + '\EmployeePhotos\';
EmployeeOpenPictureDialog.InitialDir := getcurrentdir + '\EmployeePhotos';
end;
procedure TPropertyForm.AttatchButtonClick(Sender: TObject);
var
st: string;
fsize, psize: integer;
begin
if EmployeeOpenPictureDialog.execute then
begin
st := EmployeeOpenPictureDialog.FileName;
psize := length(GPath);
fsize := length(st);
Properties.Photo := copy(st, psize + +1, fsize - psize)
end { endif };
PhotoImage.Hide;
if Properties.Photo <> '' then
begin
st := GPath + Properties.Photo;
if FileExists(st) then
begin
PhotoImage.Picture.LoadFromFile(st);
PhotoImage.Proportional := true;
PhotoImage.Show;
end
{ endif }
end; { endif }
end
procedure TPropertyForm.NextImageButtonClick(Sender: TObject);
begin
PhotoImage.Picture.LoadFromFile(st + 1);
end;
i think you want to load images from "Folder" and switch between them, if so try this code
place 2 TButtons and 1 TImage
uses jpeg;
public
{ Public declarations }
var
SL:TStringList;
ImgIndex:integer;
GPath:String;
procedure ListFileDir(Path: string; FileList: TStrings);
var
SR: TSearchRec;
begin
if FindFirst(Path + '*.jpg', faAnyFile, SR) = 0 then
begin
repeat
if (SR.Attr <> faDirectory) then
begin
FileList.Add(SR.Name);
end;
until FindNext(SR) <> 0;
FindClose(SR);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
GPath:= getcurrentdir + '\EmployeePhotos\';
SL:=TStringList.Create;
ListFileDir(GPath,SL);
Image1.Picture.LoadFromFile(GPath + SL.Strings[ImgIndex] );
end;
procedure TForm1.btnNextClick(Sender: TObject);
begin
ImgIndex:=ImgIndex+1;
if ImgIndex=SL.Count then ImgIndex :=0;
Image1.Picture.LoadFromFile(GPath + SL.Strings[ImgIndex] );
end;
procedure TForm1.btnPrevClick(Sender: TObject);
begin
ImgIndex:=ImgIndex-1;
if ImgIndex=-1 then ImgIndex :=SL.Count-1;
Image1.Picture.LoadFromFile(GPath + SL.Strings[ImgIndex] );
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
SL.Free;
end;

How do I detect the Enter key in an edit box?

I have a UDP messenger with a Send button, but it annoys me that I have to press the button instead of just hitting Enter. So I have created procedure TForm1.Edit2KeyPress. Now I have no idea how to define the Enter button in if (enter is pressed) then {code for sending message}.
After answering i have new problem. Anything I type is sended , letter by letter.. here is my code
unit chat1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdSocketHandle, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Edit1: TEdit;
Button2: TButton;
Edit2: TEdit;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Edit2KeyPress(Sender: TObject; var Key: Char);
private
Activated: Boolean;
procedure SearchEvent(ResultIP, ResultName: String);
procedure UDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
procedure UDPException(Sender: TObject);
public
end;
var
Form1: TForm1;
ss:string ;
implementation
uses UDP;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
UDPSearchForm.SearchEvent := SearchEvent;
UDPSearchForm.Left := Left;
UDPSearchForm.Top := Top;
UDPSearchForm.AktIP := Edit1.Text;
UDPSearchForm.SearchPartner;
end;
procedure TForm1.SearchEvent(ResultIP, ResultName: String);
begin
Edit1.Text := ResultIP;
Label1.Caption := ResultName;
end;
procedure TForm1.FormActivate(Sender: TObject);
var
s, s2: String;
begin
if Activated then exit;
Memo1.Clear;
Activated := true;
UDPSearchForm.OnUDPRead := UDPRead;
UDPSearchForm.OnException := UDPException;
UDPSearchForm.Active := true;
s := UDPSearchForm.LocalAddress;
s2 := UDPSearchForm.WSGetHostByAddr(s);
Memo1.Lines.Add('I''m (' + s + ') ' + s2);
end;
procedure TForm1.UDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
var
Buffer: Array [0..2047] of Byte;
count: Integer;
PeerIP: String;
PeerPort: Integer;
s: String;
i: Integer;
begin
PeerIP := ABinding.PeerIP;
PeerPort:= ABinding.PeerPort;
count := AData.Size;
if count > Length(Buffer) then begin
exit;
end;
AData.Read(Buffer, count);
if (Buffer[0] <> $00) and (Buffer[0] <> $01) then begin // not search
Edit1.Text:= PeerIP;
end;
case Buffer[0] of
$00: begin // search request
case count of
4: begin
case Buffer[1] of
0: begin
Buffer[0] := $01;
UDPSearchForm.Host := PeerIP;
UDPSearchForm.DoSend(Buffer, 4, Length(Buffer));
Memo1.Lines.Add('Inquiry [' + UDPSearchForm.WSGetHostByAddr(PeerIP) + '(' + PeerIP + ')' + ' Port: ' + IntToStr(PeerPort) +
']');
end;
end;
end;
end;
end;
$01: begin // Search Reply
case count of
4: begin
case Buffer[1] of 0:
begin
ss := UDPSearchForm.WSGetHostByAddr(PeerIP);
s := '[' + ss + '(' + PeerIP + ')' +
' Client Port: ' + IntToStr(PeerPort) +
']';
Memo1.Lines.Add('Inquiry Reply ' + s);
if PeerIp = UDPSearchForm.LocalAddress then begin
ss := '<myself>' + ss;
end;
UDPSearchForm.Add(PeerIP, ss);
end;
end;
end;
end;
end;
$10: begin // Text
case Buffer[1] of
0: begin
s := '';
for i := 4 to count-1 do begin
s := s + char(Buffer[i]);
end;
Memo1.Lines.Add(ss+' says: ' + s);
end;
end;
end;
end;
end;
procedure TForm1.UDPException(Sender: TObject);
begin
//nothing
end;
procedure TForm1.Button2Click(Sender: TObject);
var
x: Array[0..100] of Byte;
i: Integer;
begin
UDPSearchForm.Host := Edit1.Text;
UDPSearchForm.Active := true;
x[0] := $10; // Text
x[1] := 0; // Type 0
for i := 1 to Length(Edit2.Text) do begin
x[i+3] := Byte(Edit2.Text[i]);
end;
UDPSearchForm.DoSend(x, 4+Length(Edit2.Text), length(x));
Memo1.Text:=Memo1.Text+Edit2.Text+#13#10;
end;
procedure TForm1.Edit2KeyPress(Sender: TObject; var Key: Char);
var
x: Array[0..100] of Byte;
i: Integer;
begin
if Ord(Key) = VK_RETURN then
UDPSearchForm.Host := Edit1.Text;
UDPSearchForm.Active := true;
x[0] := $10; // Text
x[1] := 0; // Type 0
for i := 1 to Length(Edit2.Text) do begin
x[i+3] := Byte(Edit2.Text[i]);
end;
UDPSearchForm.DoSend(x, 4+Length(Edit2.Text), length(x));
Memo1.Text:=Memo1.Text+Edit2.Text+#13#10;
end;
end.
Set the Default property of the Send button to True. Its OnClick event will fire automatically when the user presses Enter.

Resources