HI,
You can't ignore WindowStartupLocation because WPF system will specify it as a window creation parameter, but you can move it around at the time the window is created.
namespace WpfApp1
{
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Windows;
using System.Windows.Interop;
using Gekka.Tools.Windows;
public partial class App : Application
{
private ShellHook hook;
private Dictionary<Type, Rect> dicRects = new Dictionary<Type, Rect>();
protected override void OnStartup(StartupEventArgs e)
{
hook = new ShellHook();
hook.WindowCreated += Hook_WindowCreated;
base.OnStartup(e);
}
private void Hook_WindowCreated(object sender, ShellHook.HookEventArgs e)
{
var w = HwndSource.FromHwnd(e.wParam).RootVisual as Window;
if (w != null && w.WindowStartupLocation == WindowStartupLocation.Manual)
{
if (!double.IsNaN(w.Left) && !double.IsNaN(w.Top))
{
List<Window> windows = this.Windows
.OfType<Window>()
.Where(_ => _ != w)
.Where(_ => _.IsVisible)
.ToList();
while (windows.Any(_ => _.Top == w.Top && _.Left == w.Left))
{
//If the window is overlapped, it is try move.
//But, you may see a ghost of the window slightly.
w.Left += SystemParameters.WindowCaptionHeight;
w.Top += SystemParameters.WindowCaptionHeight;
}
}
w.Closing += (a, b) =>
{
dicRects[w.GetType()] = new Rect(w.Left, w.Top, w.Width, w.Height);
};
}
}
protected override void OnExit(ExitEventArgs e)
{
hook?.Dispose();
base.OnExit(e);
}
}
}
namespace Gekka.Tools.Windows
{
using System;
using System.Runtime.InteropServices;
public static class Win32API
{
#region "API"
[DllImport("user32.dll")]
internal extern static int UnhookWindowsHookEx(int hHook);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
internal extern static int GetCurrentThreadId();
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal extern static int SetWindowsHookEx(int idHook, HookCallDelegate lpfn, IntPtr hmod, int dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
internal delegate int HookCallDelegate(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string name);
#endregion
internal enum WH
{
WH_CBT = 5,
WH_SHELL = 10
}
internal enum GWL
{
GWL_HINSTANCE = -6
}
internal enum HSHELL
{
HSHELL_WINDOWCREATED = 1,
HSHELL_WINDOWDESTROYED = 2,
HSHELL_ACTIVATESHELLWINDOW = 3
}
public static IntPtr GetHINSTANCE()
{
var mod = GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName);
var hinst = Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]);
return hinst;
}
}
public class ShellHook : IDisposable
{
public class HookEventArgs
{
public HookEventArgs(int nCode, IntPtr wParam, IntPtr lParam)
{
this.nCode = nCode;
this.wParam = wParam;
this.lParam = lParam;
}
public int nCode { get; }
public IntPtr wParam { get; }
public IntPtr lParam { get; }
}
public event EventHandler<HookEventArgs> WindowCreated;
private int _hHook = 0;
private Win32API.HookCallDelegate callback;
public ShellHook()
{
var threadid = Win32API.GetCurrentThreadId();
var hinst = Win32API.GetHINSTANCE();
this.callback = new Win32API.HookCallDelegate(ShellHookCall);
_hHook = Win32API.SetWindowsHookEx((int)Win32API.WH.WH_SHELL, callback, hinst, threadid);
}
private int ShellHookCall(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0)
{
return Win32API.CallNextHookEx(_hHook, nCode, wParam, lParam);
}
else
{
switch ((Win32API.HSHELL)nCode)
{
case Win32API.HSHELL.HSHELL_WINDOWCREATED:
WindowCreated?.Invoke(this, new HookEventArgs(nCode, wParam, lParam));
break;
case Win32API.HSHELL.HSHELL_ACTIVATESHELLWINDOW:
case Win32API.HSHELL.HSHELL_WINDOWDESTROYED:
break;
default:
break;
}
return 0;
}
}
public void UnHook()
{
if (_hHook != 0)
{
Win32API.UnhookWindowsHookEx(_hHook);
_hHook = 0;
}
}
#region IDisposable メンバ
private bool disposed = false;
protected virtual void Dispose(bool isnotFromFinalizer)
{
if (!this.disposed) { UnHook(); }
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~ShellHook() { this.Dispose(false); }
#endregion
}
}