Solved

how to prevent focus changing?

Posted on 2002-06-18
13
1,573 Views
Last Modified: 2010-04-04
I have a Delphi app with a single edit control on a form.

I have second app with a single button on it.

I set the input focus to the edit control. When I click the button, I want to put "A" (or whatever) in the edit box.

This is easy to do, except:
I don't want the edit control to lose focus,
and I may have multiple apps with edit controls running, or a form may have multiple edit controls on it.

In any case, the app with the button needs to send the "A" to the edit control that has focus (assume an edit control always has the focus when the button is pressed), regardless of which form or application it resides in.

At runtime, I don't know or care about window titles, appnames or whatever. I just want to send a keystroke to the focused edit control, and leave it focused.

TIA.
0
Comment
Question by:mullet_attack
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 3
  • 2
  • +2
13 Comments
 
LVL 17

Expert Comment

by:geobul
ID: 7091999
Hi,
In order to demonstrate how it works I've put the code in a Timer.OnTimer event. The reason is that if you press a button on app B then the current active app A will lose the focus.
-----
procedure SendToApp(h:HWND; what:string);
begin
  SendMessage(h,wm_setText,0,Integer(what));
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  h1, h2: HWND;
  buf: string;
begin
  h1 := GetForegroundWindow(); // Get active window
  if IsWindow(h1) then begin
    // Associate the input of the active window
    if AttachThreadInput(GetCurrentThreadId, GetWindowThreadProcessId(h1, nil) ,true) then begin
      h2 := GetFocus; // Get the focused control
      if IsWindow(h2) then begin
        SetLength(buf,10);
        GetClassName(h2, PChar(buf), 11);
        // Check if it's a TEdit and assign the text
        if Trim(buf) = 'TEdit' then SendToApp(h2,'Hello');
      end;
      AttachThreadInput(GetCurrentThreadId, GetWindowThreadProcessId(h1, nil) ,false)
    end;
  end;
end;
-----
Regards, Geo
0
 

Expert Comment

by:penich
ID: 7092118
I think i will not say something new but you can do the following to do it faster.

1. If app which contains Edit's shoudl have special message method
For example :
{
....
const
  FU_UPDATEEDIT = WM_USER + 20;
...
    procedure FUUpdateFocus(var Message: TMessage); message FU_UPDATEEDIT;
}

2. Each time you need to pass some text into other app you will just generate your own message for all windows in your system.
Message may contain for example the name  of the Edit control and text to pass.

In this case you will have common mechanism for all app's in your enrinonment

good luck
0
 
LVL 9

Expert Comment

by:ginsonic
ID: 7092461
procedure TForm1.FormCreate(Sender: TObject);
begin
 h:=Edit1.Handle;
end;

procedure TForm1.Edit1Change(Sender: TObject);
begin
Windows.SetFocus(H);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text:='KO';
end;

I give you a sample for a single application. All what have to do is to get the edit box handle .
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 2

Author Comment

by:mullet_attack
ID: 7092698
thanx for your suggestions, but none of these ideas are appropriate, unfortunately.

The app that receives the characters may not be a Delphi app, i just used that as an example. It could be M$ Word, notepad etc, in fact any app that could normally receive keyboard characters.

check out

http://www.rjcooper.com/onscreen/index.html

for a product called "OnScreen" that provides an on-screen keyboard. It does exactly what I want, except I need to do it with a single button only.
0
 
LVL 17

Expert Comment

by:geobul
ID: 7092813
Well...

    if IsWindow(h2) then begin
//       SetLength(buf,10);
//       GetClassName(h2, PChar(buf), 11);
       // Check if it's a TEdit and assign the text
//       if Trim(buf) = 'TEdit' then
       SendToApp(h2,'Hello');
     end;

Regards, Geo
0
 
LVL 2

Author Comment

by:mullet_attack
ID: 7095286
thanks again, but the crutial ingredient is missing - the button click.

The problem is the button click takes the focus, and that is what I don't want to happen. The focus _must_ stay where it is when the button is clicked.

0
 

Expert Comment

by:penich
ID: 7095298
He he ;))
You should've said "it should look like focus is still there"
;)
0
 
LVL 17

Expert Comment

by:geobul
ID: 7095447
Open two notepad instances, run the app below and enjoy. There are a timer and two buttons on a form. Set timer interval to 100 for example. Set the focus to one of the notepad windows and press one of the buttons here. Btn1 sends 'A' and Btn2 sends 'B' to notepad. Select the other notepad and try again. Notepad windows do not lose their focus.
---
type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  CurrentWindow, CurrentControl: HWND;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  CurrentWindow := 0;
  CurrentControl := 0;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
 h: HWND;
begin
 h := GetForegroundWindow;
 if IsWindow(h) then begin
   if Application.MainForm.Handle <> h then begin
     CurrentWindow := h;
     if AttachThreadInput(GetCurrentThreadId, GetWindowThreadProcessId(h, nil) ,true) then begin
       try
         CurrentControl := GetFocus; // Get the focused control
       finally
         AttachThreadInput(GetCurrentThreadId, GetWindowThreadProcessId(h, nil), false)
       end;
     end;
   end;
 end;
end;

function AddToWindowText(h:HWND; what:string) : boolean;
var
 buf : string;
 NewStr : string;
 len : integer;
begin
 result := true;
 try
   len := SendMessage(h,wm_getTextLength,0,0);
   SetLength(buf,len);
   SendMessage(h,wm_getText,len+1,Integer(buf));
   NewStr := buf + what;
   SendMessage(h,wm_setText,0,Integer(NewStr));
 except
   result := false;
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if CurrentControl <> 0 then AddToWindowText(CurrentControl,'A');
  if CurrentWindow <> 0 then SetForegroundWindow(CurrentWindow);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if CurrentControl <> 0 then AddToWindowText(CurrentControl,'B');
  if CurrentWindow <> 0 then SetForegroundWindow(CurrentWindow);
end;

end.
---
Regards, Geo
0
 
LVL 2

Author Comment

by:mullet_attack
ID: 7095910
Geo, thanx again, but....

The Notepad still loses focus, although successive chars are sent to it. I suggest you put more than one page of text into notepad, and then try adding to it. The pages scroll to the top when the notepad loses focus.

Please dl the "Onscreen" app I mentioned earlier, and you will get a good idea of how I want it to work.

Increasing points to 300. Is that a limit or can I offer say 1000 points simply because I need an answer?
0
 
LVL 17

Accepted Solution

by:
geobul earned 300 total points
ID: 7098310
I finally got the idea:

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    btnA: TButton;
    btnB: TButton;
    btnShift: TSpeedButton;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure SendKey(Key: byte);
    procedure btnAClick(Sender: TObject);
    procedure btnBClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  CurrentWindow: HWND;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  CurrentWindow := 0;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
 h: HWND;
begin
 h := GetForegroundWindow;
 if IsWindow(h) then begin
   if Application.MainForm.Handle <> h then begin
     CurrentWindow := h;
   end;
 end;
end;

procedure TForm1.SendKey(Key: byte);
begin
   if CurrentWindow <> 0 then begin
    SetForegroundWindow(CurrentWindow);
    if btnShift.Down then keybd_event(VK_SHIFT, 0, 0, 0);
    keybd_event(Key, 0, 0, 0);
    keybd_event(Key, 0, KEYEVENTF_KEYUP, 0);
    if btnShift.Down then keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
  end;
end;

procedure TForm1.btnAClick(Sender: TObject);
begin
  SendKey($41);
end;

procedure TForm1.btnBClick(Sender: TObject);
begin
  SendKey($42);
end;

Regards, Geo
0
 
LVL 17

Expert Comment

by:geobul
ID: 7098372
Forgot to say:
btnShift.AllowAllUp := true;
btnShift.GroupIndex := 1;
0
 
LVL 17

Expert Comment

by:geobul
ID: 7098385
I'm sorry, one more thing I've forgotten:

procedure TForm1.btnShiftClick(Sender: TObject);
begin
  if CurrentWindow <> 0 then SetForegroundWindow(CurrentWindow);
end;

And how to implement 'Enter' key:

procedure TForm1.btnEnterClick(Sender: TObject);
begin
  SendKey(VK_RETURN);
end;

Regards, Geo
0
 

Expert Comment

by:CleanupPing
ID: 9343182
mullet_attack:
This old question needs to be finalized -- accept an answer, split points, or get a refund.  For information on your options, please click here-> http:/help/closing.jsp#1 
EXPERTS:
Post your closing recommendations!  No comment means you don't care.
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
Come and listen to Percona CEO Peter Zaitsev discuss what’s new in Percona open source software, including Percona Server for MySQL (https://www.percona.com/software/mysql-database/percona-server) and MongoDB (https://www.percona.com/software/mongo-…
Michael from AdRem Software outlines event notifications and Automatic Corrective Actions in network monitoring. Automatic Corrective Actions are scripts, which can automatically run upon discovery of a certain undesirable condition in your network.…

717 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question