Laden...

Window ohne Rahmen auf 2. Bildschirm mit anderer Auflösung maximieren

Letzter Beitrag vor 13 Jahren 6 Posts 4.592 Views
Window ohne Rahmen auf 2. Bildschirm mit anderer Auflösung maximieren

Hallo zusammen.

Ich habe mal wieder ein Problem:

Ich habe ein Window ohne Rahmen. D.h. ich musste die Maximieren-Funktion nachbauen.
Oder anders gesagt, ich muss die Werte bestimmen, wie groß das Fenster maximiert sein darf.

Das mache ich wie üblich mit der Windows-Message WM_GETMINMAXINFO.
Darin besorg ich mir das MINMAXINFO und setze die MaxPosition und die MaxSize.
Anschließend setze ich das MINMAXINFO wieder auf den lParam von WM_GETMINMAXINFO.

Für MaxPosition und MaxSize beschaff ich mir die Maße des aktuellen Bildschirms.
Das habe ich debuggt und ich kann garantieren, dass die Werte, die als Maße gelten korrekt sind.

Dazu muss man sagen, dass ich 2 Bildschirme am Rechner, mit folgenden Auflösungen habe:

  1. Bildschirm (Primär): 1680x1050 (ohne Taskbar: 1680x1020)
  2. Bildschirm (Secondär): 1920x1080

Wenn ich meine Anwendung starte und auf das Fenster auf den 1. Bildschirm maximiere, wird gesetzt:

MaxPosition = POINT( 0, 0 )
MaxSize = POINT( 1680, 1020 ) -> Ohne Taskbar

Das Fenster wird korrekt maximiert.

Wenn ich das Fenster auf den 2. Bildschirm maximiere, wird gesetzt:

MaxPosition = POINT( -1920, 0 ) -> 2. Bildschirm links vom Hauptbildschirm
MaxSize = POINT( 1920, 1080 )

Das Fenster ragt plötzlich in den (1.) Hauptbildschirm rein ist also zu breit, außerdem ist auch die Höhe etwas zu hoch. Der untere Teil des Fensters wird abgeschnitten.
Das SizeChanged-Event zeigt, dass die Größe auf 2160x1100 gesetzt wurde.

Nach ein wenig hin und her habe ich es mal manuell mit der Auflösung des Primärbildschirms versucht. Also 1680x1050. Und zack! Hat das Fenster die richtige Größe.

Weiß jemand was das soll? Kann mir das jemand erklären?

Schön Dank.
Gruß,
Matscher

Mit Windowsmessages wie üblich?? Seit wann ist das denn wieder üblich? Kann mir kaum vorstellen dass das seit Silverlight wieder in ist.
Kannst du nicht einfach das Fenster auf maximiert setzen?

Kann chilic nur zustimmen. Einfach den WindowState entsprechend setzen und gut ist. Zur Not auch noch über die SystemParameters Klasse um die Größen rauszukriegen wenn mans komplizierter haben will, aber sowas macht man sicherlich nicht mit Window Messages in WPF.

Baka wa shinanakya naoranai.

Mein XING Profil.

Nana... nicht falsch verstehen. Aber danke für die Antworten.

Klar setze ich WindowState auf Maximized.
Bei einem rahmenlosen (WindowStyle == None) Fenster, legt sich das Fenster aber über die Taskleiste (Fullscreen).
Und da MUSS man über die Windows-Message WM_GETMINMAXINFO die Größe definieren. Also wie üblich. 😉

Siehe hier

D.h. ich setze beispielsweise beim Maximieren-Button WindowState = Maximized.
Darauf hin wird die Message WM_GETMINMAXINFO gesendet. Diese verarbeite ich.

Weiß jemand rat zu meinen eigentlichen Problem?

[EDIT]
Es ist irgendwie schwer zu vermitteln... mh...

Ich verstehe nur nicht, warum beim Maximieren auf den 2. Bildschirm (Auflösung 1920x1080) die Auflösung des 1. Bildschirms 1680x1050 als 1920x1080 interpretiert wird.

Das schwierige auf dem Hauptbildschirm hast du ja schon erledigt. Auf dem zweiten Bildschirm ist ja keine Taskbar. Das heißt, man kann wirklich den Fullscreen verwenden.

ich hab mir dazu ein paar Extensions geschrieben, die allerdings unschönerweise auf WinForms verweisen. Über SystemParameters dürfte das aber genauso funktionieren


public static void FillSecondaryScreen(this Window window)
{
	Rect area = ScreenBounds(window.GetSecondaryScreen());
	window.Left = area.Left;
	window.Top = area.Top;
	window.Width = area.Width;
	window.Height = area.Height;

	window.WindowState = WindowState.Maximized;
	window.WindowStyle = WindowStyle.None;
}

public static int GetSecondaryScreen(this Window window)
{
	if (Screen.AllScreens.Length == 1)
		return 0;

	int primary = window.GetCurrentScreen();
	for (int secondary = 0; secondary < Screen.AllScreens.Length; secondary++)
	{
		if (secondary == primary)
			continue;
		return secondary;
	}
	throw new Exception("no secondary screen found");
}

public static int GetCurrentScreen(this Window window)
{
	Screen screen = Screen.FromHandle(new WindowInteropHelper(window).Handle);
	for (int i = 0; i < Screen.AllScreens.Length; i++)
	{
		if (screen.Equals(Screen.AllScreens[i]))
			return i;
	}
	throw new Exception("target screen was not found");
}

public static Rect ScreenBounds(int screenNumber)
{
	if (screenNumber < Screen.AllScreens.Length)
		return Screen.AllScreens[screenNumber].Bounds.ToRect();
	throw new Exception(String.Format("Screen '{0}' is not available", screenNumber));
}

(Teile des Codes sind vielleicht ein bisschen spezifisch auf unsere Software, aber im groben und ganzen ist es glaub ich klar.

Ich schiebe das Fenster einfach auf Koordinaten des zweiten Bildschirms und maximiere es dann dort. In meinem Fall ist es ja auch gewollt, dass das Fenster in einen echten Fullscreen geht und in der Regel wird man auf dem zweiten Bildschirm auch nichts anderes haben.
Problematisch könnte es allerdings sein, wenn man eine zusätzliche Taskleiste auf dem zweiten Bildschirm hat.

Ich hab aber auch noch eine weitere Methode, die evtl auch noch helfen könnte. ich muss allerdings zugeben, dass ich nicht mehr weiß, wo ich diese gefunden habe. Diese nehm ich für den PrimaryScreen her, sollte man aber auch auf dem Secondary verwenden können. Durch "MonitorFromWindow" wird ja der richtige Monitor gesucht.


// Make window borderless
this.WindowStyle = WindowStyle.None;
this.ResizeMode = ResizeMode.NoResize;

// Get handle for nearest monitor to this window
WindowInteropHelper wih = new WindowInteropHelper(this);
IntPtr hMonitor = MonitorFromWindow(wih.Handle, MONITOR_DEFAULTTOPRIMARY);

// Get monitor info
MONITORINFOEX monitorInfo = new MONITORINFOEX();
monitorInfo.cbSize = Marshal.SizeOf(monitorInfo);
GetMonitorInfo(new HandleRef(this, hMonitor), monitorInfo);

// Create working area dimensions, converted to DPI-independent values
HwndSource source = HwndSource.FromHwnd(wih.Handle);
if (source == null) return; // Should never be null
if (source.CompositionTarget == null) return; // Should never be null
System.Windows.Media.Matrix matrix = source.CompositionTarget.TransformFromDevice;
RECT workingArea = monitorInfo.rcWork;
Point dpiIndependentSize =
	matrix.Transform(
	new Point(
		workingArea.Right - workingArea.Left,
		workingArea.Bottom - workingArea.Top));

// Maximize the window to the device-independent working area ie
// the area without the taskbar.
// NOTE - window state must be set to Maximized as this adds certain
// maximized behaviors eg you can't move a window while it is maximized,
// such as by calling Window.DragMove
this.Top = 0;
this.Left = 0;
this.MaxWidth = dpiIndependentSize.X;
this.MaxHeight = dpiIndependentSize.Y;
this.WindowState = WindowState.Maximized;

Edit: Hmmm... das Ding hab ich schon ewig nimmer angesehen und jetzt seh ich, dass da ein Fehler drin ist. Im unteren Codeschnipsel darf am Ende natürlich nicht Left und Top auf 0 gesetzt werden, sondern muss auf workingArea.Left bzw. workingArea.Right gesetzt werden

Danke für deine Antwort.

Ich habe mich mal mit deinem Quellcode befasst. Leider ist das nicht die Lösung von meinem Problem. Obwohl der zweite Quellcode schon in die Richtung geht.
Das mit dem CompositionTarget gibt mir leider nur 1 zu 1 die Werte zurück, die ich reingebe in das Transform.

Ich habe halt auch schon so gemacht, einfach die Maße des Primärbildschirms anzugeben als Maximialgröße (damit habe ich auf dem 2. Bildschirm Fullscreen).
Allerdings KANN es ja sein, dass auch auf dem 2. Bildschirm die WorkingArea kleiner ist als die Gesamtfläche. Bspw. ICQ an die Seite docken oder so.

Ich habe mir jetzt ein Berechnung geschrieben, die die Maße des Primärbildschirms mit dem relevanten Bildschirm ins Verhältnis setzt. Daraus wird die WorkingArea des relevanten Bildschirms berechnet.


public struct Screen
{
	public const int MONITOR_DEFAULTTOPRIMARY = 1;
	public const int MONITOR_DEFAULTTONEAREST = 2;

	[DllImport( "user32.dll" )]
	public static extern IntPtr MonitorFromWindow( IntPtr hwnd, uint dwFlags );

	private IntPtr _handle;
	private Int32Rect _bounds;
	private Int32Rect _workingArea;

	private Screen( IntPtr hScreen )
	{
		this._handle = hScreen;

		MONITORINFO info = MONITORINFO.Empty;
		User32.GetMonitorInfo( hScreen, ref info );

		this._bounds = new Int32Rect( info.rcMonitor.left, info.rcMonitor.top, info.rcMonitor.right - info.rcMonitor.left, info.rcMonitor.bottom - info.rcMonitor.top );
		this._workingArea = new Int32Rect( info.rcWork.left, info.rcWork.top, info.rcWork.right - info.rcWork.left, info.rcWork.bottom - info.rcWork.top );
	}

	/// <summary>
	/// Gibt die Bildschirm zurück, auf dem sich derzeit 
	/// </summary>
	public static Screen FromWindow( Window window )
	{
		return Screen.FromWindow( new System.Windows.Interop.WindowInteropHelper( window ).Handle );
	}

	/// <summary>
	/// Gibt die Bildschirm zurück, auf dem sich derzeit 
	/// </summary>
	public static Screen FromWindow( IntPtr hWnd )
	{
		IntPtr hScreen = MonitorFromWindow( hWnd, MONITOR_DEFAULTTONEAREST );

		return new Screen( hScreen );
	}

	/// <summary>
	/// Gibt den Hauptbildschirm zurück.
	/// </summary>
	public static readonly Screen PrimaryScreen = new Screen( MonitorFromWindow( IntPtr.Zero, MONITOR_DEFAULTTOPRIMARY ) );

	/// <summary>
	/// Gibt die Maße des Bildschirms zurück.
	/// </summary>
	public Int32Rect Bounds
	{
		get { return this._bounds; }
	}

	/// <summary>
	/// Gibt die Maße der Arbeitsfläche zurück.
	/// </summary>
	public Int32Rect WorkingArea
	{
		get { return this._workingArea; }
	}

	/// <summary>
	/// Gibt die Größe zum Maximieren zurück.
	/// </summary>
	public Int32Rect MaximizeBounds
	{
		get
		{
			Rect primaryBounds = Helper.ConvertToRect( Screen.PrimaryScreen.Bounds );
			Rect bounds = Helper.ConvertToRect( this.Bounds );
			Rect workingArea = Helper.ConvertToRect( this.WorkingArea );

			double relationWidth = primaryBounds.Width / bounds.Width;
			double relationHeight = primaryBounds.Height / bounds.Height;

			return new Int32Rect( ( int )workingArea.X, ( int )workingArea.Y, ( int )( workingArea.Width * relationWidth ), ( int )( workingArea.Height * relationHeight ) );
		}
	}
}

Bei der WM_GETMINMAXINFO-Message setze ich diese Werte nun:


private void WmGetMinMaxInfo( IntPtr hWnd, IntPtr lParam )
{
	Win32.MINMAXINFO info = ( Win32.MINMAXINFO )Marshal.PtrToStructure( lParam, typeof( Win32.MINMAXINFO ) );

	Screen screen = Screen.FromWindow( hWnd );
	Int32Rect maxBounds = screen.MaximizeBounds;

	info.ptMaxPosition = new Win32.POINT( 0, 0 );
	info.ptMaxSize = new Win32.POINT( maxBounds.Width, maxBounds.Height );

	Marshal.StructureToPtr( info, lParam, true );
}

Damit scheint das Grundsätzlich erstmal zu laufen. Auch wenn mir nicht klar ist, warum das überhaupt gemacht wird.

Falls jemand noch schickere Lösungen kennt... nur zu! Ist alles irgendwie komisch.

Schön Gruß,
Matscher