Solved

how to prevent focus changing?

Posted on 2002-06-18
13
1,437 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
  • 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
 
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
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 

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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

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

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Migrating to Microsoft Office 365 is becoming increasingly popular for organizations both large and small. If you have made the leap to Microsoft’s cloud platform, you know that you will need to create a corporate email signature for your Office 365…
Hi friends,  in this video  I'll show you how new windows 10 user can learn the using of windows 10. Thank you.

920 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

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now