Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1671
  • Last Modified:

how to prevent focus changing?

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
mullet_attack
Asked:
mullet_attack
  • 6
  • 3
  • 2
  • +2
1 Solution
 
geobulCommented:
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
 
penichCommented:
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
 
ginsonicCommented:
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
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
mullet_attackAuthor Commented:
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
 
geobulCommented:
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
 
mullet_attackAuthor Commented:
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
 
penichCommented:
He he ;))
You should've said "it should look like focus is still there"
;)
0
 
geobulCommented:
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
 
mullet_attackAuthor Commented:
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
 
geobulCommented:
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
 
geobulCommented:
Forgot to say:
btnShift.AllowAllUp := true;
btnShift.GroupIndex := 1;
0
 
geobulCommented:
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
 
CleanupPingCommented:
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

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

  • 6
  • 3
  • 2
  • +2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now