AxAcroPDF 在获得焦点后会立即吞下所有与键相关的事件,包括快捷键、按键等。我添加了一个消息过滤器,它也没有收到任何与键相关的消息。它是一个 COM 组件,这可能是相关的吗?
有没有办法在控制开始吞下它们之前捕捉它们?
Hans 是正确的,Acrobat Reader 会生成两个子 AcroRd32 进程,您无法从托管代码中直接访问它们。
我已经对此进行了实验,您有三个可行的选择:
您可以创建一个全局系统挂钩,然后查找并过滤/响应发送到您的子 AcroRd32 窗口的 WM_SETFOCUS 消息。您可以使用包装库在 C# 中完成其中的一些操作,例如这里的:http: //www.codeproject.com/KB/system/WilsonSystemGlobalHooks.aspx
您还需要识别正确的进程,因为您的应用程序可能有多个实例,或 AcroRd32 的其他实例。这是最具确定性的解决方案,但是因为您的应用程序现在将过滤发送到存在的每个窗口的消息,所以我通常不推荐这种方法,因为这样您的程序可能会对系统稳定性产生负面影响。
查找备用 PDF 查看控件。有关一些商业组件,请参阅此答案:.net PDF 查看器控件,或自行滚动:http: //www.codeproject.com/KB/applications/PDFViewerControl.aspx
找到一个可接受的 hack。根据您的应用程序需要的健壮程度,以下代码可能是合适的(它适合我的情况):
DateTime _lastRenav = DateTime.MinValue;
public Form1()
{
InitializeComponent();
listBox1.LostFocus += new EventHandler(listBox1_LostFocus);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
axAcroPDF1.src = "sample.pdf"; //this will cause adobe to take away the focus
_lastRenav = DateTime.Now;
}
void listBox1_LostFocus(object sender, EventArgs e)
{
//restores focus if it were the result of a listbox navigation
if ((DateTime.Now - _lastRenav).TotalSeconds < 1)
listBox1.Focus();
}
我可能终于有一个简单得可笑的答案了。到目前为止,在测试中这是有效的。
在遭受这个问题很长一段时间后,并建立了一个复杂的系统,每个自定义控件记录它们中的最后一个有焦点并使用计时器将焦点翻转回来(当 acropdf 抓住它时)我重新审视了这个问题并阅读了大量答案(寻找最近的解决方案)。收集到的信息帮助我有了这个想法。
这个想法是在加载时禁用(acropdf)控件,如下例所示(为清楚起见,代码减少)
AxAcroPDF_this.Enabled = False
AxAcroPDF_this.src = m_src
然后在计时器上,说 1 秒后。
AxAcroPDF_this.Enabled = True
基本上这个想法是告诉 Windows 在允许之前不要让用户使用 acropdf 控件,因此要求 Windows 阻止它获得焦点(因为那里不允许用户使用)。
到目前为止,这一直存在,如果有任何变化,我将对其进行编辑。如果它不完全适合你,那么这个想法可能指向一个有用的方向。
它是一个进程外 COM 组件,这就是问题所在。完全违反了 SetParent() 中规定的 Windows SDK 要求。一旦其窗口获得焦点,acroread.exe 进程中的消息循环将获取所有消息,您的消息过滤器将无法再看到任何消息。
从技术上讲,它可以通过使用 SetWindowsHookEx() 将 DLL 注入进程并使用 WH_GETMESSAGE 监视消息来修复。但是你不能用 C# 语言编写这样的 DLL。
少校很烂,我知道。该程序似乎从来没有缺少它。
出于某种原因,蒂姆的回答,直接禁用 AxAcroPDF 控件,在我的情况下不起作用。先前选择的文本框上的 Leave 事件也永远不会触发。
正在工作的是将 AxAcroPDF 控件嵌套在禁用的 GroupBox 内。由于我的应用程序的用户只需要查看 PDF,而不需要与之交互,因此 GroupBox 的 Enabled 属性在设计器中设置为 False。