6

在一个应用程序中,当它在平板电脑上运行时,我使用屏幕键盘 (OSK)。我们创建了一个名为 OSK 的类,它有一个显示和隐藏方法。

当用户在屏幕键盘上按下“enter”时,osk 会隐藏。问题是当用户使用关闭 (x) 按钮关闭 OSK 时。OSK 隐藏,但发生这种情况时需要在 UI 中更改某些内容。

有没有办法(事件或类似的东西)知道用户何时按下 OSK 上的关闭按钮?

我将展示一些用于显示和隐藏 OSK 的代码。显示的代码在 Oxygene 中(但我认为它看起来很像 C#)

首先我们有一些 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; 

在 show 方法中有这样的代码:

  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; 

在 Hide 方法中,下一个代码用于隐藏 OSK:

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

更新:找到适用于 Windows 7 的工作解决方案....不适用于 Windows 8(我需要什么)

这就是我在 Windows 7 中解决问题的方法:主要思想是在 OSK 类中,当显示 osk 时我启动一个 Dispatchertimer。现在每秒检查 osk 窗口是否可见。如果是这样,则会触发一个事件,该事件可以在多个地方进行处理。(我还检查了计时器中的 _firstshown 布尔值,因为有时 osk 需要一段时间才能出现。

我是这样做的:首先我创建了 IsWindowVisible 方法的 dllImport

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

在 OSK.Show 中,我启动计时器并将 _firstShown 设置为 false(因为 osk 可能需要一段时间才能出现)在此之前,我将计时器间隔设置为 1 秒,并将 eventhandlerf 添加到 timer.Tick:

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

这是 _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;

当它在我的开发机器(windows 7)上工作时很高兴,这种喜悦是短暂的,因为当我在平板电脑(windows 8)上测试它时它不起作用。计时器等工作正常,看起来 Windows 8 不处理 iswindowVisible 方法。

无论如何,非常感谢所有帮助

4

4 回答 4

2

老实说,我不知道 Oxygene,但它看起来像 Pascal for .NET :)

由于您的程序启动 OSK 本身,您只需订阅Process.Exited将在用户关闭 OSK 时调用的事件。

因此,创建p一个全局变量并订阅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;

另外,我不明白你为什么需要FindWindow,你不能用p.MainWindowHandle吗?

更新2

之前的更新没用,突然想到:为什么不用微软的 Ink API 而不是hacking呢?

将程序集的引用添加Microsoft.Ink到您的项目中,您需要浏览到该文件,因为默认情况下它不在列表中(在microsoft.ink.dll下搜索Program Files)。

TextInputPanel然后为需要 OSK 的每个输入创建一个对象:

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

现在,文本输入面板将在textBox1获得焦点时显示,在失去焦点时隐藏。InPlaceVisibilityChanged如果您对用户何时隐藏面板感兴趣,您甚至可以将处理程序附加到事件:

tip.InPlaceVisibilityChanged += tip_InPlaceVisibilityChanged;

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

如果您使用的是 WPF,则可以使用此方法来HWND获取TextBox

HwndSource textHandle = (HwndSource)PresentationSource.FromVisual(wpfTextBox);
tip = new TextInputPanel();
tip.AttachedEditWindow = textHandle.Handle;
tip.InPlaceVisibleOnFocus = true;
tip.DefaultInPlaceState = InPlaceState.Expanded;
于 2013-07-16T06:17:07.850 回答
2

终于找到了使用注册表解决我的问题的方法。osk 使用名为 UserClosed 的注册表项。如果用户关闭 osk,则 userclosed 注册表项将为 1。

所以要做的第一件事就是将此注册表项设置为 0(在 App.Xaml.cs 中覆盖 OnStartup)

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

现在,当应用程序启动时,注册表项设置为 0。

现在,当调用 osk 时,计时器启动,并且在每个滴答声中,我们需要查看注册表项是否为 1(用户已关闭 OSK),如果是,我们触发事件,停止计时器并将注册表项设置回 0。

这是我的新 timer_tick 方法:

   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;
于 2013-08-06T10:56:53.937 回答
0

您可以在进程启动 osk 时启动计时器。在那个计时器上,您可以检查

var oskprocess = Process.GetProcessesByName("osk");

当 oskprocess.Length=0

然后没有 osk 运行。然后你可以停止计时器

于 2015-07-13T09:05:03.330 回答
0

有时我写了使用taskkill结束osk.exe的代码不再在windows10中工作但是当我写它时我认为我使用的是windows8这是在处理(java)中完成的,但这可能有帮助???

launch(new String[] {"c:/Windows/system32/osk.exe" }); // OSK open

launch(new String[] {"taskkill /IM osk.exe"}); // OSK close
于 2017-01-25T21:47:11.900 回答