Keep window on top and steal focus in WinForms

后端 未结 5 1535
后悔当初
后悔当初 2020-11-27 16:31

I realize that this would be COMPLETELY bad practice in normal situations, but this is just for a test app that needs to be taking input from a bar code scanner (emulating a

相关标签:
5条回答
  • 2020-11-27 16:36

    Visibility

    Make the window a "Top-Most" window. This is the way the Task-Manager can remain on top of other windows. This is a property of a Form and you make the form top-most (floating above other windows) by setting the value to true.

    You shouldn't need to override any of the "Active window" behaviour with the top-most setting.

    Focus

    I asked a similar question previously here on StackOverflow and the answer would solve your problem. You can make the application use a low-level input hook and get notification of the key-codes coming from the scanner. This way, your application always gets these keys even though the application does not have focus.

    You may need to enhance the solution to squash the key-codes so that they are not transmitted to the "in-focus" application (e.g. notepad).

    Since Windows 2000, there is no official mechanism for an application to grab focus without direct intervention of the user. Peeking at the input streams through the RawInputDevices hook is the only sensible way to go.

    A number of articles may help (C# implementations)

    • RawInput article on CodeProject
    • MSDN documentation of RawInput
    0 讨论(0)
  • 2020-11-27 16:36

    You might try focusing on a specific input, or try the setting .TopMost property to true (and then unsetting it again).

    But I suspect your problem is that these methods all just place messages in the windows event queue, and your program has to wait for all existing events to finish processing before it will handle that one and focus the app.

    0 讨论(0)
  • 2020-11-27 16:53

    I struggled with a similar problem for quite a while. After much experimentation and guessing, this is how I solved it:

    // Get the window to the front.
    this.TopMost = true;
    this.TopMost = false;
    
    // 'Steal' the focus.
    this.Activate();
    
    0 讨论(0)
  • 2020-11-27 16:56

    The way I approached this problem was to spawn another thread whose only purpose was to ensure the Form is TopMost and has focus at all times. This code will make all other applications unusable while it is running, which is what I needed for my specific applications. You can add in a Sleep in keepFocus or have some other event trigger it.

    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();
            }
        }       
    }
    
    0 讨论(0)
  • 2020-11-27 17:03

    I had a similar problem and found the following to do the trick. Adapted to C# from here

            // 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();
    

    EDIT: Here are the necessary PInvoke definitions for C#:

    [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);
    
    0 讨论(0)
提交回复
热议问题