Search code examples
winapiwindows-8dllimporttableton-screen-keyboard

On Screen Keyboard close button event?


In an application I am using an onscreen keyboard(OSK) for when it is running on a tablet. We've made a class called OSK which has a show and hide method.

When the user presses 'enter' on the onscreen keyboard, the osk hides. The problem is when the user closes the OSK with the close (x) button. The OSK hides, but some things need to change in the UI when this happens.

Is there a way (an event or something like that) to know when the user pushes the close button on the OSK?

I'll show some of the code I"ve used for showing and hiding the OSK. The code shown is in Oxygene (but it looks a lot like C# I think)

First we've have some dllImports:

[DllImport("user32.dll", SetLastError := true)]
class method PostMessage(hWnd: IntPtr; Msg: UInt32; wParam, lParam: IntPtr): Boolean; external;
[DllImport("user32.dll", SetLastError := true)]
class method FindWindow(lpClassName, lpWindowName: String): IntPtr; external; 

In the show method there is this code:

  using p := new Process do
  begin
    p.StartInfo.UseShellExecute := true;
    p.StartInfo.FileName := 'C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe';
    p.Start();
  end; 

In the Hide method the next code is used to hide the OSK:

      var oskWindow := FindWindow("IPTip_Main_Window", nil);
      var WM_SYSCOMMAND  := 274;
      var SC_CLOSE := 61536;
      PostMessage(oskWindow, WM_SYSCOMMAND, SC_CLOSE, 0);

Update: Found a working solution for windows 7....not working for windows 8 (What i need)

This is what i did to solve the problem in windows 7: The main idea is that in the OSK class i start a Dispatchertimer when the osk is shown. Now every second is checked if the osk window is visible. If so an event is fired which can be handled in several places. (I also check a _firstshown boolean in the timer because it sometimes takes a while for the osk to appear.

Here's how I did that: first i made a dllImport of the IsWindowVisible method

[DllImport("user32.dll", CharSet:=CharSet.Auto)]
class method IsWindowVisible(hWnd:IntPtr):Boolean; external;

In OSK.Show i start the timer and set _firstShown to false (because it can take a while for the osk to appear) Before this i've set the timer interval on 1 second and added an eventhandlerf to timer.Tick:

  _timer.Interval := new TimeSpan(0,0,1);
  _timer.Tick += new EventHandler(_timer_Tick);

This is the code in _timer_tick:

class method OSK._timer_Tick(sender: Object; e: EventArgs);
begin
  var oskWindow := FindWindow("IPTip_Main_Window", nil);
  var IsOSKOpen := IsWindowVisible(oskWindow);

  if not _firstShown then begin
      if IsOSKOpen then _firstShown := true;

      exit;
  end;
  if not IsOSKOpen then begin        
      OSKClosed(nil,new EventArgs());      
      _timer.Stop();
      _firstShown := false;
  end;
end;

There was pleasure when this worked on my development machine (windows 7), the joy was shortlived because when i tested it on the tablet (windows 8) it didn't work. the timer etc works fine, it just looks like windows 8 doesn't handle the iswindowVisible method.

Anyway all help is very much appreciated


Solution

  • Finally found a solution to my problem using the registry. The osk uses a registrykey called UserClosed. If the user closes the osk the userclosed registry key will be one.

    So the first thing to do is to set this registry key to 0 (in App.Xaml.cs override OnStartup)

        var regKeyUserClosed := Registry.CurrentUser.OpenSubKey("Software\Microsoft\TabletTip\1.7",true);
        regKeyUserClosed.SetValue("UserClosed",0);
    

    Now the registry key is set to 0 when the application starts.

    Now when the osk is called the timer starts and on every tick we need to see if the registry key is 1 (the user has closed the OSK) if so we trigger the event, stop the timer and set the registry key back to 0.

    This is my new timer_tick method:

       class method OSK._timer_Tick(sender: Object; e: EventArgs);
    begin
    
    var regKeyUserClosed := Registry.CurrentUser.OpenSubKey("Software\Microsoft\TabletTip\1.7",true);
    
      var UserClosed := Convert.ToInt32(regKeyUserClosed.GetValue("UserClosed"));
    
      if UserClosed.Equals(1) then begin 
    
      OSKClosed(nil,new EventArgs()); 
    
      _timer.Stop();
    
      regKeyUserClosed.SetValue("UserClosed",0);
    
      end; 
    
    end;