Восстановить окно после сворачивания, WPF C#
Пытаюсь сделать нечто вроде гаджетов на рабочий стол (винда 10)
Использую WPF
Мне нужно окно,
- поверх рабочего стола, но ниже всех остальных окон
- его невозможно свернуть
Первая проблема решена с помощью
PInvoke.User32.SetWindowPos(MainWindowHandle, (IntPtr)1, 0, 0, ScreenW, ScreenH, 0);
Как решить вторую? А именно, при использовании функции "показать рабочий стол" (Win + D) восстановить окно обратно
Мои попытки:
-> Повесить WindowState = WindowState.Normal; на OnWindowStateChanged - не работает, Win + D сворачивает окно, но статус остается Normal
-> Сделать окно дочерним к окну рабочего стола Program Manager - кажется не работает, изменений не видно
-> SetWindowPos(...) не работает
-> User32.SetForegroundWindow(...) не работает
-> PInvoke.User32.ShowWindow(MainWindowHandle, SW_RESTORE); не работает
-> Код
PInvoke.User32.SetWindowPos(MainWindowHandle, (IntPtr)(-1), 0, 0, ScreenW, ScreenH, 0); // mosttop
Thread.Sleep(10);
PInvoke.User32.SetWindowPos(MainWindowHandle, (IntPtr)1, 0, 0, ScreenW, ScreenH, 0); // bottommost
Работает но выглядит как адский костыль
Чё делать?
Ответы (2 шт):
Попробуйте копать в этом направлении
public partial class MainWindow : Window
{
[DllImport("user32.dll", SetLastError = true)]
internal static extern int ShowWindow(int hwnd, int nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hwnd, StringBuilder ss, int count);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
private CancellationTokenSource _cts = new();
private void MainWindow_OnStateChanged(object? sender, EventArgs e)
{
if (WindowState == WindowState.Minimized)
{
_cts = new CancellationTokenSource();
var ct = _cts.Token;
ForceRestore(ct);
}
else
_cts.Cancel();
}
private async void ForceRestore(CancellationToken ct)
{
var process = Process.GetCurrentProcess();
try
{
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(ct))
{
cts.CancelAfter(TimeSpan.FromMilliseconds(1000));
while (!cts.IsCancellationRequested)
{
await Task.Delay(100, cts.Token);
if (IsAllMinimized())
{
await Task.Delay(100, cts.Token);
ShowWindow((int)process.MainWindowHandle, 1);
SetForegroundWindow(process.MainWindowHandle);
}
}
}
}
catch (OperationCanceledException)
{
}
}
private bool IsAllMinimized()
{
var handle = GetForegroundWindow();
GetWindowThreadProcessId(handle, out var processId);
var proccess = Process.GetProcessById(Convert.ToInt32(processId));
if (proccess.ProcessName != "explorer")
return false;
const int nChar = 256;
var t = GetWindowText(handle, new StringBuilder(nChar), nChar);
return t == 0;
}
И чем больше в системе открыто окон, тем больше нужна задержка. Если вызвать эти методы пока идет процесс сворачивания всех окон, то станет WindowState = WindowState.Normal, но визуально не появится. Нужно вызывать после того, как все окна в системе свернулись.
Вместо паузы можно попробовать смотреть на foreground процесс и титл - при свернутом это будет explorer с пустым титлом
upd: Обновил до этого варианта. Все равно не 100%. Пришлось навесить Delay и после детекта проводника, а то окно могло появиться и тут же исчезнуть
upd2: В принципе можно не таймаутами надеяться на авось, а добавить проверку, что окно стало foreground и пинать его пока не станет.
Похоже что я нашел решение, нужно сделать окно дочерним, но не к Progman, а к SHELLDLL_DefView