Automatically move app windows to the right monitor across multiple docking stations and multiple multi-monitor setups.
  • C# 96.9%
  • PowerShell 1.9%
  • Inno Setup 1.2%
Find a file
Hannah Vernon d01a923240
Merge pull request #22 from HannahVernon/dev
Release: installer fix, version injection, KeePass blocking
2026-05-21 17:00:49 -05:00
.github Fix installer path and version injection, use non-blocking window title API 2026-05-21 17:00:28 -05:00
installer Fix installer path and version injection, use non-blocking window title API 2026-05-21 17:00:28 -05:00
src Fix installer path and version injection, use non-blocking window title API 2026-05-21 17:00:28 -05:00
.gitignore Initial commit. 2026-03-11 12:05:36 -05:00
build.ps1 Fix NETSDK1047: add RuntimeIdentifiers to csproj, simplify build.ps1 2026-03-11 16:39:53 -05:00
CODE_OF_CONDUCT.md Add Contributor Covenant Code of Conduct 2026-03-11 15:44:11 -05:00
CONTRIBUTING.md Add CONTRIBUTING.md and SECURITY.md 2026-04-20 10:32:55 -05:00
Directory.Build.props UI polish: cloaked window filter, delete/unassign buttons, help tooltips, version 1.1.48 2026-05-21 16:31:41 -05:00
LICENSE Updated copyright in LICENSE 2026-03-12 10:18:27 -05:00
README.md Add window position capture, z-order tracking, and auto-save 2026-05-21 16:31:41 -05:00
SECURITY.md Add CONTRIBUTING.md and SECURITY.md 2026-04-20 10:32:55 -05:00
WindowMover.sln Rename WindowMover.App to WindowMover, add Activate profile button, fix new profile overwrite 2026-03-11 15:29:15 -05:00

WindowMover

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:

Features

  • Drag-and-drop layout editor — see your monitors as columns, drag apps to assign them
  • Window position & size capture — remembers where each window was placed, restores proportionally across resolution/scale changes
  • Z-order tracking — tracks window stacking order in real time as you switch between apps
  • Auto-save — optionally saves layout changes automatically as you work
  • 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

How It Works

  1. Launch WindowMover — it detects your connected monitors and creates a "setup fingerprint"
  2. Assign apps to monitors — drag process names from the left panel onto monitor columns
  3. Save — your rules are stored as a JSON profile for this monitor setup
  4. Dock/undock — WindowMover detects the change and automatically applies the matching profile

Profiles are stored in %APPDATA%\WindowMover\profiles\.

Building

Development Prerequisites

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 6 or %ProgramFiles(x86)%\Inno Setup 6) so the build script can find ISCC.exe.

The build.ps1 script handles everything — version bump, publish, and installer creation:

.\build.ps1

This will:

  1. Auto-increment the patch version in Directory.Build.props
  2. Publish a self-contained x64 release to publish\
  3. Compile the Inno Setup installer to dist\WindowMover-Setup-<version>.exe
  4. Remove old installer versions from dist\

Build script options

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 -SkipPublish

Manual build steps

If 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.iss

The installer will be output to dist\WindowMover-Setup-<version>.exe.

Project Structure

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)

Configuration

Profiles are auto-saved as JSON files:

%APPDATA%\WindowMover\
├── profiles/
│   ├── A1B2C3D4E5F6G7H8.json    # Home office profile
│   ├── X9Y8Z7W6V5U4T3S2.json    # Work office profile
│   └── ...
└── .initialized                   # First-run marker

Known Limitations

  • 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

License

WindowMover is free and open source under the MIT license - see the LICENSE file for details.