Сохранить окно сверху и украсть фокус в WinForms
Я понимаю, что это будет ПОЛНОСТЬЮ плохая практика в нормальных ситуациях, но это только для тестового приложения, которое должно принимать входные данные от сканера штрих-кода (эмуляция клавиатуры). Проблема в том, что мне нужно запустить некоторые скрипты во время сканирования, поэтому мне нужно окно, чтобы восстановить фокус непосредственно после того, как я нажму на script, чтобы запустить его. Я попытался использовать Активировать(), BringToFront(), Focus(), а также некоторые вызовы Win32, такие как SetForegroundWindow(), Setcapture() и SetActiveWindow()... однако лучшее, что я могу сделать, это сделать сделайте элемент панели задач мигающим, чтобы сказать мне, что он хочет сосредоточиться, но что-то останавливает его. BTW, я запускаю это на XP SP2 и использую .NET 2.0.
Возможно ли это?
Изменить: Чтобы уточнить, я запускаю скрипты, дважды щелкнув по ним в проводнике. Поэтому мне нужно, чтобы украсть фокус обратно из проводника и тестового приложения.
Ответы
Ответ 1
Видимость
Сделайте окно "самым большим". Так Менеджер задач может оставаться поверх других окон. Это свойство Form
, и вы делаете форму самой верхней (плавающей над другими окнами), устанавливая значение true
.
Вам не нужно переопределять поведение "активного окна" с самой верхней настройкой.
Фокус
Я задал аналогичный вопрос fooobar.com/questions/241241/..., и ответ решит вашу проблему. Вы можете заставить приложение использовать низкоуровневый входной крючок и получить уведомление о кодах клавиш, поступающих со сканера. Таким образом, ваше приложение всегда получает эти ключи, даже если приложение не имеет фокуса.
Вам может потребоваться усилить решение для сквоша ключей-кодов, чтобы они не передавались в приложение "в фокусе" (например, блокнот).
Начиная с Windows 2000, нет официального механизма приложения для захвата фокуса без прямого вмешательства пользователя. Подглядывание на входные потоки через крюк RawInputDevices - единственный разумный путь.
Несколько статей могут помочь (реализации С#)
Ответ 2
Я долгое время боролся с подобной проблемой. После долгих экспериментов и угадываний я решил это:
// Get the window to the front.
this.TopMost = true;
this.TopMost = false;
// 'Steal' the focus.
this.Activate();
Ответ 3
У меня была аналогичная проблема, и я нашел следующее, чтобы сделать трюк. Адаптирован к С# из здесь
// 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: Вот необходимые определения 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);
Ответ 4
То, как я подошел к этой проблеме, состоял в том, чтобы создать еще один поток, целью которого было только то, что Form является TopMost и всегда имеет фокус. Этот код сделает все остальные приложения непригодными для использования во время работы, что я и нуждаюсь в своих конкретных приложениях. Вы можете добавить в Sleep in keepFocus или вызвать другое событие.
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();
}
}
}
Ответ 5
Вы можете попробовать сосредоточиться на определенном входе или попробовать установить свойство .TopMost в значение true (а затем снова отменить его).
Но я подозреваю, что ваша проблема заключается в том, что эти методы просто размещают сообщения в очереди событий Windows, и ваша программа должна дождаться завершения всех существующих событий до того, как она обработает это, и сосредоточьте приложение.