Menggunakan SetWindowPos dengan beberapa monitor
Menggunakan user32.dlldan C # Saya menulis metode yang Anda lihat di bawah. Menggunakan pegangan proses untuk sebuah jendela, ini akan mengatur posisi jendela di (x, y)lokasi yang disediakan .
Namun, dalam lingkungan multi-monitor, kode di bawah ini hanya menetapkan posisi jendela ke monitor utama. Saya juga ingin dapat memilih monitor mana.
Adakah yang bisa menjelaskan bagaimana hal ini dapat dilakukan dengan menggunakan SetWindowPosatau mungkin kombinasi dengan user32.dllfungsi lain ?
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
private const int SWP_NOSIZE = 0x0001;
private const int SWP_NOZORDER = 0x0004;
private const int SWP_SHOWWINDOW = 0x0040;
public static void SetWindowPosition(Process p, int x, int y)
{
IntPtr handle = p.MainWindowHandle;
if (handle != IntPtr.Zero)
{
SetWindowPos(handle, IntPtr.Zero, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
}
}
Solusi berdasarkan komentar Jimi.
Berikut adalah konfigurasi monitor saya:
Perhatikan bahwa saya memiliki monitor sekunder di sebelah kiri monitor utama saya. Setelah membaca tautan Monitor Virtual yang disediakan Jimi, saya menemukan bahwa untuk memindahkan jendela ke monitor sekunder, saya harus menggunakan nilai x negatif karena berada di kiri asal monitor utama (pojok kiri atas, atau (0, 0)).
Oleh karena itu, jika saya ingin mengatur posisi jendela saya ke koordinat <0,0> monitor sekunder, saya harus MENGUNDUH x-width monitor sekunder dari monitor utama, seperti ini:
(0, 0) - (1920, 0) = (-1920, 0)
Sekarang, ketika saya memanggil SetWindowPositionkode klien saya, saya menyebutnya seperti ini:
SetWindowPosition(Process p, -1920, 0);
Catatan: Saya tidak tahu apa yang akan Anda lakukan jika monitor memiliki resolusi yang berbeda. Itu adalah topik yang lebih kompleks dan bukan pertanyaan yang saya tanyakan. Selain itu, saya tidak melihat adanya kebutuhan untuk mendalami topik tersebut karena contoh sederhana di atas menyelesaikan semua masalah saya.
Jawaban
Sistem Menampilkan disposisi dan Layar Virtual
Dalam Sistem Windows, Layar Utama (perspektif pemrograman) adalah perangkat Tampilan yang posisi sudut kiri atasnya disetel Point(0,0).
Ini menyiratkan bahwa Tampilan yang diposisikan di sebelah kiri Layar Utama, akan memiliki koordinat negatif X ( Ykoordinat bisa menjadi negatif jika Tampilan dalam tata letak Potret).
Tampilan di sebelah kanan akan memiliki koordinat positif X ( Ykoordinat bisa menjadi negatif jika Tampilan dalam tata letak Potret).
Tampilan di Kiri Layar Utama :
Dengan kata lain, Tampilan yang memiliki asal negatif .
The asal adalah jumlah dari semua sebelumnya , dikurangi dari asal koordinat Layar Primer. Point.X Point.XScreens[].WidthPoint.X
Tampilan di Kanan Layar Utama :
Dengan kata lain, Tampilan yang memiliki asal positif .
The asal adalah jumlah dari semua sebelumnya , Primary termasuk , ditambahkan ke asal koordinat Layar Primer. Point.X Point.XScreens[].WidthPoint.X
Catatan penting tentang Dpi Awareness :
Jika aplikasi bukan DPIAware, semua tindakan ini dapat dikompromikan oleh virtualisasi dan Penskalaan DPI otomatis yang dilakukan oleh Sistem. Semua ukuran akan diseragamkan ke 96 Dpi default: aplikasi akan menerima nilai berskala. Ini juga mencakup nilai yang diambil dari fungsi non-DpiAware Win32 Api. Lihat:
Pengembangan Aplikasi Desktop DPI Tinggi di Windows
Aktifkan dukungan untuk semua Sistem yang ditargetkan di app.manifestfile, hapus komentar di bagian yang diperlukan.
Tambahkan / Hapus komentar bagian DpiAware dan DpiAwareness di app.manifestfile.
The Kesadaran PerMonitorV2 Dpi modus dapat diatur dalam app.configberkas (tersedia dari Windows 10 Kreator Edition).
Lihat juga:
DPI dan Device-Independent Pixels
Mixed-Mode DPI Scaling dan API sadar-DPI
Contoh:
Pertimbangkan Sistem dengan 3 Monitor:
PrimaryScreen (\\.\DISPLAY1): Width: (1920 x 1080)
Secondary Display (Right) (\\.\DISPLAY2): Width: (1360 x 768)
Secondary Display (Left) (\\.\DISPLAY3): Width: (1680 x 1050)
PrimaryScreen:
Bounds: (0, 0, 1920, 1080) Left: 0 Right: 1920 Top: 0 Bottom: 1080
Secondary Display (Right):
Bounds: (1360, 0, 1360, 768) Left: 1360 Right: 2720 Top: 0 Bottom: 768
Secondary Display (Left):
Bounds: (-1680, 0, 1680, 1050) Left: -1680 Right: 0 Top: 0 Bottom: 1050
Jika kita mengubah, menggunakan applet Sistem, referensi Layar Utama, menyetelnya ke \\.\DISPLAY3, koordinat akan diubah sesuai:
Layar Virtual
Layar Virtual adalah tampilan virtual, yang dimensinya diwakili oleh:
Asal : koordinat asal paling kiri Screen
Lebar : jumlah dari semua LebarScreens .
Height : Ketinggian tertinggiScreen
Pengukuran ini dilaporkan oleh SystemInformation.VirtualScreen
Layar Utama Sizedilaporkan oleh SystemInformation.PrimaryMonitorSize
Semua ukuran dan posisi Layar saat ini juga dapat diambil menggunakan Screen.AllScreens dan memeriksa setiap \\.\DISPLAY[N]properti.
Menggunakan contoh sebelumnya sebagai referensi, dalam disposisi pertama, VirtualScreenbatasannya adalah:
Bounds: (-1680, 0, 3280, 1080) Left: -1680 Right: 3280 Top: 0 Bottom: 1080
Dalam disposisi kedua, VirtualScreenbatasannya adalah:
Bounds: (0, 0, 4960, 1080) Left: 0 Right: 4960 Top: 0 Bottom: 1080
Posisi Jendela di dalam area Tampilan :
Kelas Layar menawarkan beberapa metode yang dapat digunakan untuk menentukan di layar mana jendela tertentu saat ini ditampilkan:
Screen.FromControl([Control reference])
Mengembalikan Screenobjek yang berisi bagian terbesar dari Controlreferensi yang ditentukan .
Screen.FromHandle([Window Handle])
Mengembalikan Screenobjek yang berisi bagian terbesar dari Window \ Control yang direferensikan olehHandle
Screen.FromPoint([Point])
Mengembalikan Screenobjek yang berisi tertentuPoint
Screen.FromRectangle([Rectangle])
Mengembalikan Screenobjek yang berisi bagian terbesar dari yang ditentukanRectangle
Screen.GetBounds()(kelebihan beban)
Mengembalikan Rectanglestruktur yang mereferensikan Batas Layar yang berisi:
- spesifik Point
- bagian terbesar dari yang ditentukan Rectangle
- ControlReferensi
Untuk menentukan \\.\DISPLAY[N]di mana Formulir saat ini ditampilkan, panggil (misalnya):
Screen.FromHandle(this);
Untuk menentukan di mana Layar Formulir sekunder akan ditampilkan:
(Menggunakan Contoh yang Ditampilkan dalam contoh)
form2 = new Form2();
form2.Location = new Point(-1400, 100);
form2.Show();
Rectangle screenSize = Screen.GetBounds(form2);
Screen screen = Screen.FromHandle(form2.Handle);
screenSizeakan = to \\.\DISPLAY3Bounds.
screenakan menjadi Screenobjek yang mewakili \\.\DISPLAY3properti.
screenobjek juga akan melaporkan \\.\DISPLAY[N]nama Screenyang form2ditampilkan.
Dapatkan hMonitorHandle of a Screen object :
The NET Referensi Sumber menunjukkan bahwa hMonitordikembalikan panggilan[Screen].GetHashCode();
IntPtr monitorHwnd = new IntPtr([Screen].GetHashCode());
Atau menggunakan fungsi Win32 asli yang sama:
MonitorFromWindow , MonitorFromPoint dan MonitorFromRect
[Flags]
internal enum MONITOR_DEFAULTTO
{
NULL = 0x00000000,
PRIMARY = 0x00000001,
NEAREST = 0x00000002,
}
[DllImport("User32.dll", SetLastError = true)]
internal static extern IntPtr MonitorFromWindow(IntPtr hwnd, MONITOR_DEFAULTTO dwFlags);
[DllImport("User32.dll", SetLastError = true)]
internal static extern IntPtr MonitorFromPoint([In] POINT pt, MONITOR_DEFAULTTO dwFlags);
[DllImport("User32.dll", SetLastError = true)]
internal static extern IntPtr MonitorFromRect([In] ref RECT lprc, MONITOR_DEFAULTTO dwFlags);
- Untuk mendeteksi pergerakan Jendela di antara Monitor, Anda dapat menangani
WM_WINDOWPOSCHANGEDpesan, memanggilMonitoFromWindow, lalu GetScaleFactorForMonitor untuk menentukan apakah ada perubahan DPI dan bereaksi terhadap pengaturan baru, pada akhirnya.
Mendapatkan Menangani konteks perangkat dari Layar :
Metode umum untuk mengambil hDC dari Tampilan yang tersedia.
Koordinat Layar atau Perangkat Layar dapat ditentukan dengan menggunakan salah satu metode yang dijelaskan sebelumnya saat hanya diperlukan referensi Layar tertentu.
The Screen.DeviceName properti dapat digunakan sebagai lpszDriverparameter dari GDI CreateDC fungsi. Ini akan mengembalikan hDC dari tampilan yang dapat digunakan oleh Graphics.FromHdc untuk membuat objek Grafik yang valid, yang memungkinkan untuk dilukis pada layar tertentu.
Di sini, dengan asumsi setidaknya dua Tampilan tersedia:
[DllImport("gdi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData);
[DllImport("gdi32.dll", SetLastError = true, EntryPoint = "DeleteDC")]
internal static extern bool DeleteDC([In] IntPtr hdc);
public static IntPtr CreateDCFromDeviceName(string deviceName)
{
return CreateDC(deviceName, null, null, IntPtr.Zero);
}
Screen[] screens = Screen.AllScreens;
IntPtr screenDC1 = CreateDCFromDeviceName(screens[0].DeviceName);
IntPtr screenDC2 = CreateDCFromDeviceName(screens[1].DeviceName);
using (Graphics g1 = Graphics.FromHdc(screenDC1))
using (Graphics g2 = Graphics.FromHdc(screenDC2))
using (Pen pen = new Pen(Color.Red, 10))
{
g1.DrawRectangle(pen, new Rectangle(new Point(100, 100), new Size(200, 200)));
g2.DrawRectangle(pen, new Rectangle(new Point(100, 100), new Size(200, 200)));
}
DeleteDC(screenDC1);
DeleteDC(screenDC2);