保持窗口在顶部,并在WinForms中窃取焦点

时间:2008-11-10 15:56:14

标签: c# .net winforms

我意识到在正常情况下这将是完全不好的做法,但这仅适用于需要从条形码扫描仪(模拟键盘)输入的测试应用程序。问题是我需要在扫描时启动一些脚本,所以我需要窗口在我单击脚本运行它之后直接重新获得焦点。我尝试过使用Activate(),BringToFront(),Focus()以及一些Win32调用,如SetForegroundWindow(),Setcapture()和SetActiveWindow()...但是我能得到的最好的就是让任务栏项目开始闪烁,告诉我想要有焦点,但有些东西正在停止它。顺便说一下,我在XP SP2上使用.NET 2.0运行它。

这可能吗?

编辑:为了澄清,我通过在资源管理器中双击它们来运行脚本。所以我需要它从探索者和测试应用程序中窃取焦点。

5 个答案:

答案 0 :(得分:41)

我在类似的问题上挣扎了很长一段时间。经过多次实验和猜测,这就是我解决它的方法:

// Get the window to the front.
this.TopMost = true;
this.TopMost = false;

// 'Steal' the focus.
this.Activate();

答案 1 :(得分:12)

可见性

使窗口成为“最顶层”窗口。这是任务管理器可以保持在其他窗口之上的方式。这是Form的属性,您可以通过将值设置为 true 来使表单位于最顶层(浮动在其他窗口上方)。

您不需要使用最顶层的设置覆盖任何“活动窗口”行为。

焦点

我问了一个类似的问题previously here on StackOverflow,答案可以解决你的问题。您可以使应用程序使用低级输入挂钩,并获取来自扫描仪的密钥代码的通知。这样,即使应用程序没有焦点,您的应用程序也会始终获取这些键。

您可能需要增强解决方案以压缩密钥代码,以便它们不会传输到“对焦”应用程序(例如记事本)。

从Windows 2000开始,没有官方机制让应用程序在没有用户直接干预的情况下获取焦点。通过RawInputDevices钩子查看输入流是唯一合理的方法。

许多文章可能有所帮助(C#实施)

答案 2 :(得分:12)

我遇到了类似的问题,发现以下内容可以解决问题。适应here

中的C#
        // force window to have focus
        uint foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
        uint appThread = GetCurrentThreadId();
        const uint SW_SHOW = 5;
        if (foreThread != appThread)
        {
            AttachThreadInput(foreThread, appThread, true);
            BringWindowToTop(form.Handle);
            ShowWindow(form.Handle, SW_SHOW);
            AttachThreadInput(foreThread, appThread, false);
        }
        else
        {
            BringWindowToTop(form.Handle);
            ShowWindow(form.Handle, SW_SHOW);
        }
        form.Activate();

编辑:以下是C#必需的PInvoke定义:

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();

/// <summary>The GetForegroundWindow function returns a handle to the foreground window.</summary>
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(IntPtr hWnd);

[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(HandleRef hWnd);

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);

答案 3 :(得分:7)

我解决这个问题的方法是产生另一个线程,其唯一目的是确保Form是TopMost并始终具有焦点。此代码将使所有其他应用程序在运行时无法使用,这是我特定应用程序所需的。您可以在keepFocus中添加Sleep,或者让其他事件触发它。

using System.Threading;          // be sure to include the System.Threading namespace

    //Delegates for safe multi-threading.
    delegate void DelegateGetFocus();
    private DelegateGetFocus m_getFocus;

    //Constructor.
    myForm()
    {
        m_getFocus = new DelegateGetFocus(this.getFocus);   // initialise getFocus
        InitializeComponent();
        spawnThread(keepFocus);                             // call spawnThread method
    }

    //Spawns a new Thread.
    private void spawnThread(ThreadStart ts)
    {
        try
        {
            Thread newThread = new Thread(ts);
            newThread.IsBackground = true;
            newThread.Start();
        }
        catch(Exception e)
        {
            MessageBox.Show(e.Message, "Exception!", MessageBoxButtons.OK, 
                MessageBoxIcon.Error);
        }
    }

    //Continuously call getFocus.
    private void keepFocus()
    {
        while(true)
        {
            getFocus();
        }
    }

    //Keeps Form on top and gives focus.
    private void getFocus()
    {
        //If we need to invoke this call from another thread.
        if (this.InvokeRequired)
        {
            try
            {
                this.Invoke(m_getFocus, new object[] { });
            }
            catch (System.ObjectDisposedException e)
            {
                // Window was destroyed. No problem but terminate application.
                Application.Exit();
            }
        }
        //Otherwise, we're safe.
        else
        {
            this.TopMost = true;
            this.Activate();
        }
    }       
}

答案 4 :(得分:0)

您可以尝试关注特定输入,或尝试将.TopMost属性设置为true(然后再次取消设置)。

但我怀疑你的问题是这些方法都只是将消息放在windows事件队列中,并且你的程序必须等待所有现有事件完成处理才能处理那个并关注应用程序。