Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 621
Gert Hermans Avatar asked Jul 05 '13 08:07

Gert Hermans


1 Answers

To be honest, I don't know Oxygene, but it looks like Pascal for .NET :)

Since your program starts the OSK itself, you can just subscribe to the Process.Exited event which will be called when the user closes the OSK.

So, make p a global variable and subscribe to Exited

p.Exited += new EventHandler(_osk_Exited);

class method OSK._osk_Exited(sender: Object; e: EventArgs);
begin
    // will be called when OSK gets closed
end;

Also, I don't see why you need FindWindow, can't you use p.MainWindowHandle instead?

UPDATE2

Previous update didn't work, but suddenly I thought: why not use Microsoft's Ink API instead of hacking?

Add a reference to the Microsoft.Ink assembly to your project, you'll need to browse to the file, because it is not in the list by default (search for microsoft.ink.dll under Program Files).

Then create a TextInputPanel object for each input that needs the OSK:

tip = new TextInputPanel(textBox1);
tip.InPlaceVisibleOnFocus = true;
tip.DefaultInPlaceState = InPlaceState.Expanded;

Now, the text input panel will show up when textBox1 gets focus and hide when it loses focus. You can even attach a handler to the InPlaceVisibilityChanged event if you're interested in when the user hides the panel:

tip.InPlaceVisibilityChanged += tip_InPlaceVisibilityChanged;

void tip_InPlaceVisibilityChanged(object sender, InPlaceVisibilityChangeEventArgs e)
{
    // do something with e.Visible
}

If you're using WPF you can use this method to get the HWND of the TextBox:

HwndSource textHandle = (HwndSource)PresentationSource.FromVisual(wpfTextBox);
tip = new TextInputPanel();
tip.AttachedEditWindow = textHandle.Handle;
tip.InPlaceVisibleOnFocus = true;
tip.DefaultInPlaceState = InPlaceState.Expanded;
like image 88
huysentruitw Avatar answered Oct 27 '22 00:10

huysentruitw