Hallo Leute,
ich möchte euch eine C#-Klasse zur Verfügung stellen, mit der sich ein WPF-Fenster erzeugen lässt, das immer ein bestimmtes Seitenverhältnis beibehält.
Weil ich im Internet keinen wirklich gut funktionierenden Code gefunden habe, habe ich ihn selbst entwickelt.
Ihr dürft den Code ohne Angabe von Copyright verwenden und auch modifizieren.
Das Fenster lässt sich an allen Kanten und Ecken anfassen und skalieren. Dabei wird immer das im Konstruktor übergebene Seitenverhältnis vom Inhaltsbereich (ohne Rahmen) beibehalten.
Ich habe zwar WPF für das Fenster (und die Windows-API für ein flüssiges Verhalten) verwendet, der Code lässt sich aber auch leicht in C++ übersetzen und außerhalb von WPF verwenden.
Tipp: Wenn man noch eine Viewbox verwendet, dann skaliert der Inhalt sogar mit.
Bei Fragen, Problemen oder Anregungen schreibt einfach eine Nachricht. Ich freue mich, wenn ich jemandem damit weiterhelfen kann.
|
C#-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace VirtualSAM
{
public class ConstantAspectRatioWindow : Window
{
// Datentypen
[StructLayout(LayoutKind.Sequential)]
private struct Win32Point
{
public int X;
public int Y;
};
[StructLayout(LayoutKind.Explicit)]
private struct Win32Rect
{
[FieldOffset(0)] public int Left;
[FieldOffset(4)] public int Top;
[FieldOffset(8)] public int Right;
[FieldOffset(12)] public int Bottom;
[FieldOffset(0)] public Win32Point TopLeft;
[FieldOffset(8)] public Win32Point BottomRight;
};
// Eigenschaften
private Win32Rect BorderSize; // Gibt an, wie viele Pixel zwischen Fensteraußenkante und Clientbereich liegen
private readonly double FactorW2H; // Seitenverhältnis des Clientbereichs Höhe / Breite
private const int WM_SIZING = 0x0214;
private const int WMSZ_LEFT = 1;
private const int WMSZ_RIGHT = 2;
private const int WMSZ_TOP = 3;
private const int WMSZ_TOPLEFT = 4;
private const int WMSZ_TOPRIGHT = 5;
private const int WMSZ_BOTTOM = 6;
private const int WMSZ_BOTTOMLEFT = 7;
private const int WMSZ_BOTTOMRIGHT = 8;
// Konstruktor
public ConstantAspectRatioWindow(double factorW2H)
{
SourceInitialized += Window_SourceInitialized;
FactorW2H = factorW2H;
}
// Methoden
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hwnd, ref Win32Rect rect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetClientRect(IntPtr hwnd, ref Win32Rect rect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ClientToScreen(IntPtr hwnd, ref Win32Point point);
// WindowsMessages über Hook abfangen
private void Window_SourceInitialized(object sender, EventArgs ea)
{
HwndSource source = (HwndSource)PresentationSource.FromVisual((Window)sender);
source.AddHook(WindowProc);
// Breite des Fensterrahmens bestimmen
Win32Rect window = new Win32Rect();
Win32Rect client = new Win32Rect();
GetWindowRect(source.Handle, ref window);
GetClientRect(source.Handle, ref client);
ClientToScreen(source.Handle, ref client.TopLeft);
ClientToScreen(source.Handle, ref client.BottomRight);
BorderSize = new Win32Rect
{
Left = client.Left - window.Left,
Top = client.Top - window.Top,
Right = window.Right - client.Right,
Bottom = window.Bottom - client.Bottom
};
}
// WindowsMessages abarbeiten
private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_SIZING)
{
// Neue Fenstergröße bestimmen
Win32Rect window = (Win32Rect)Marshal.PtrToStructure(lParam, typeof(Win32Rect));
// In Clientbereich umrechnen
window.Left += BorderSize.Left;
window.Top += BorderSize.Top;
window.Right -= BorderSize.Right;
window.Bottom -= BorderSize.Bottom;
// Abmessungen und Verhältnis bestimmen
long width = window.Right - window.Left;
long height = window.Bottom - window.Top;
double aspectError = (double)height / width - FactorW2H;
// Prüfen ob das Verhältnis gut genug stimmt
if (Math.Abs(aspectError) < 0.005)
return IntPtr.Zero;
// Bei Eckbewegung passende Kantenbewegung bestimmen
int grip = (int)wParam switch
{
WMSZ_TOPLEFT => aspectError > 0 ? WMSZ_TOP : WMSZ_LEFT,
WMSZ_TOPRIGHT => aspectError > 0 ? WMSZ_BOTTOM : WMSZ_LEFT,
WMSZ_BOTTOMRIGHT => aspectError > 0 ? WMSZ_BOTTOM : WMSZ_RIGHT,
WMSZ_BOTTOMLEFT => aspectError > 0 ? WMSZ_TOP : WMSZ_RIGHT,
_ => (int)wParam
};
// Seitenverhältnis des Clientbereichs anpassen
switch (grip)
{
case WMSZ_LEFT:
window.Top = (int)(window.Bottom - width * FactorW2H);
break;
case WMSZ_TOP:
window.Left = (int)(window.Right - height / FactorW2H);
break;
case WMSZ_RIGHT:
window.Bottom = (int)(window.Top + width * FactorW2H);
break;
case WMSZ_BOTTOM:
window.Right = (int)(window.Left + height / FactorW2H);
break;
}
// In Fenstergröße umrechnen
window.Left -= BorderSize.Left;
window.Top -= BorderSize.Top;
window.Right += BorderSize.Right;
window.Bottom += BorderSize.Bottom;
// Neue Fenstergröße aktualisieren
Marshal.StructureToPtr(window, lParam, true);
handled = true;
}
return IntPtr.Zero;
}
}
}
|