Automatically move application windows to their assigned monitor across multiple multi-monitor setups — home dock, work dock, laptop-only, and RDP sessions.
I almost always have multiple monitors attached to my laptop, although I do occasionally work without any monitors attached. At home I have a dock with two monitors attached, at work I have a similar setup using a different dock and two different monitors. Every time I switch from home to work, or vice-versa, all the apps I have open pile up on the main monitor necessitating dragging-and-dropping open windows to the monitors where they "live". This software automates that process, through the following features:
- Drag-and-drop layout editor — see your monitors as columns, drag apps to assign them
- Automatic profile switching — detects when monitors connect/disconnect and applies rules
- Multiple setups supported — home office, work office, laptop-only each get their own layout
- RDP awareness — separate layout profiles for remote desktop sessions
- System tray — runs in the background, auto-starts on login
- Windows 10 & 11 compatible with per-monitor DPI support
- Launch WindowMover — it detects your connected monitors and creates a "setup fingerprint"
- Assign apps to monitors — drag process names from the left panel onto monitor columns
- Save — your rules are stored as a JSON profile for this monitor setup
- Dock/undock — WindowMover detects the change and automatically applies the matching profile
Profiles are stored in %APPDATA%\WindowMover\profiles\.
| Requirement | Version | Notes |
|---|---|---|
| .NET 8 SDK | 8.0+ | Required to compile the app |
| Visual Studio 2022 | 17.8+ | With the .NET desktop development workload (or use the CLI alone) |
| Inno Setup 6 | 6.x | Required only for building the installer |
Inno Setup must be installed to the default location (
%ProgramFiles%\Inno Setup 6or%ProgramFiles(x86)%\Inno Setup 6) so the build script can findISCC.exe.
The build.ps1 script handles everything — version bump, publish, and installer creation:
.\build.ps1This will:
- Auto-increment the patch version in
Directory.Build.props - Publish a self-contained x64 release to
publish\ - Compile the Inno Setup installer to
dist\WindowMover-Setup-<version>.exe - Remove old installer versions from
dist\
| Flag | Description |
|---|---|
-NoBump |
Skip the automatic version increment |
-SkipPublish |
Skip dotnet publish and reuse the existing publish\ output |
-RetainOldVersions |
Keep previous installer EXEs in dist\ |
-Configuration Debug |
Build in Debug mode (default: Release) |
Examples:
# Rebuild the installer without bumping the version
.\build.ps1 -NoBump
# Full build, keeping old installers
.\build.ps1 -RetainOldVersions
# Quick installer rebuild using existing publish output
.\build.ps1 -NoBump -SkipPublishIf you prefer to run each step individually:
# Build only (no installer)
dotnet build
# Publish as self-contained x64
dotnet publish src\WindowMover -c Release -r win-x64 --self-contained --force -o publish
# Compile the installer (requires Inno Setup)
& "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" /DMyAppVersion=1.0.0 installer\WindowMover.issThe installer will be output to dist\WindowMover-Setup-<version>.exe.
WindowMover.sln
├── build.ps1 # Automated build, publish, and installer script
├── Directory.Build.props # Shared version number
├── src/
│ ├── WindowMover.Core/ # Core library (models, services, Win32 interop)
│ │ ├── Models/ # MonitorInfo, MonitorSetup, WindowRule, LayoutProfile
│ │ ├── Services/ # MonitorIdentifier, MonitorWatcher, WindowManager, etc.
│ │ └── Native/ # P/Invoke declarations (User32, Wtsapi32)
│ └── WindowMover/ # WPF application
│ ├── ViewModels/ # MVVM ViewModels
│ ├── Controls/ # Custom WPF controls
│ └── Resources/ # App manifest, assets
├── installer/
│ └── WindowMover.iss # Inno Setup installer script
├── publish/ # Self-contained app output (generated)
└── dist/ # Installer EXEs (generated)
Profiles are auto-saved as JSON files:
%APPDATA%\WindowMover\
├── profiles/
│ ├── A1B2C3D4E5F6G7H8.json # Home office profile
│ ├── X9Y8Z7W6V5U4T3S2.json # Work office profile
│ └── ...
└── .initialized # First-run marker
- Elevated processes: Cannot move windows of apps running as Administrator unless WindowMover is also elevated
- UWP Store apps: Some may resist standard window positioning APIs
- Monitor identification: Monitors without EDID data (rare) fall back to resolution-based identification
- Dock switch delay: Windows needs 2-3 seconds to stabilize after a dock/undock event; rules are applied after this debounce period
WindowMover is free and open source under the MIT license - see the LICENSE file for details.