Größenangabe beim Erstellen eines Fensters soll sich nur auf den Client-Bereich beziehen
-
O.k., die nächste Frage zur WinAPI:
Wenn ich ein Fenster mit CreateWindow oder CreateWindowEx erstelle und gebe ihm eine Größe an, dann ist am Ende tatsächlich das gesamte Fenster so groß. Ich möchte mit dieser Größenangabe aber nur die Client-Größe festlegen. Wenn da schon die Titelleiste mit einbezogen ist, habe ich ja je nach eingestelltem Layout des Benutzers eine andere Größe des Client-Bereichs. Wie kann ich das also richtig machen?Hier noch ein Beispiel, um das ganze zu verdeutlichen: Ein Fenster mit der Breite X und der Höhe Y würde bei unterschiedlichen Layouts folgendermaßen aussehen:
+--------------------+ +--------------------+ |Titelleiste _ O X| |Titelleiste _ O X| +--------------------+ |extragroß | | | +--------------------+ | | | | | | | | | | | | | | | | +--------------------+ +--------------------+Ich möchte es aber so haben:
+--------------------+ +--------------------+ |Titelleiste _ O X| |Titelleiste _ O X| +--------------------+ |extragroß | | | +--------------------+ | | | | | | | | | | | | | | | | | | | | | | | | +--------------------+ | | +--------------------+
-
N' Abend,
Hm mir fallen spontan 2 Mwöglichkeiten ein:
1. Möglichkeit:
Da du ja weißt, welche Styles dein Fenster enthält, kannst du bspw. die Höhe der Titelleiste, wenn vorhanden, durch GetSystemMetrics ermitteln und von der Höhenangabe des betroffenen Fensters abziehen. Das ist aber ne Menge Arbeit bzw. ne Menge Sucherei (der Konstanten für GetSystemMetrics) :p .2. Möglichkeit:
Du setzt beide Werte (Width, Height) auf 0 und setzt dann die korrekten Werte unter WM_SIZE.
-
Also, Möglichkeit 2 geht nicht, weil WM_SIZE aufgerufen wird, wenn man die Größe ändert. Und da kann ich ja nicht die ursprüngliche Größe angeben. Außerdem würde SetWindowPos das genauso machen wie die Create-Funktion. (Oder an welche Funktion zum Ändern der Größe hast Du gedacht?) Die Dialogfeldgrößen in der MFC sind auch unabhängig von dem Layout. Gibt es da keine Funktion, die dafür da ist? Ich meine, wie wird das denn in typischen Programmen gemacht, wo der Client-Bereich ja nicht plötzich kleiner sein darf, wenn ein Benutzer ein größeres Layout hat.
-
AdjustWindowRect
-
SNES-Spieler schrieb:
AdjustWindowRect
That's it!

-
The AdjustWindowRect function calculates the required size of the window rectangle, based on the desired client-rectangle size. The window rectangle can then be passed to the CreateWindow function to create a window whose client area is the desired size.
Hä? Also: Die Funktion berechnet mir ein Rechteck. Da ist schonmal die erste Frage: Woraus will mir die Funktion ein Rechteck berechnen? Und Frage 2: Wie übergebe ich dieses Rechteck an CreateWindow? Ist das der lezte Parameter (LPVOID)?
-
NES-Spieler schrieb:
The AdjustWindowRect function calculates the required size of the window rectangle, based on the desired client-rectangle size. The window rectangle can then be passed to the CreateWindow function to create a window whose client area is the desired size.
Hä? Also: Die Funktion berechnet mir ein Rechteck. Da ist schonmal die erste Frage: Woraus will mir die Funktion ein Rechteck berechnen? Und Frage 2: Wie übergebe ich dieses Rechteck an CreateWindow? Ist das der lezte Parameter (LPVOID)?
Na es steht doch genau in deinem Zitat, wie die Funktion anzuwenden ist.
Du übergibst den RECT-Bereich deines Client-Fensters an, er errechnet dir die Fenstergröße, unter Berücksichtigung der Rahmenbreite usw.lpRect ist ein pointer auf eine Variable vom Typ RECT welche die Größe des Client-Bereichs angibt.
dwStyle listet die Styles des Fensters auf, mit denen es erstellt wird. Anhand dieser Styles wird dann errechnet, ob Rahmen vorhanden sind und wie breit diese sind.bMenu ist ein bool, ob das Fenster ein Menü hat oder nicht. Dadurch wird der Clientbereich ja nochmal verkleinert, deshalb ist dies wichtig für die Funktion zu wissen.
Wenn die Funktion ausgeführt wurde, findest du in der Variable des Typs RECT (der erste Parameter) die neue Größe des Fensters, wie du es erstellen musst, damit der Clientbereich genau so groß ist, wie du es vorher in der RECT-Variable eingestellt hast. Also gibst du da z.B. folgendes ein: (Als Beispiel soll der Clientbereich 100 x 100 Pixel groß werden)
RECT rect;
rect.top = 0;
rect.bottom = 100;
rect.left = 0;
rect.right = 100;und übergibst die Variable dann an die Funktion, steht nachher in der Variablen z.B. dieses hier:
(Angenommen der Rahmen ist links und rechts jeweils 2 Pixel breit)rect.bottom = 104 (4 mehr, da der Rahmen 2 Pixel breit ist und dieser oben und unten anliegt, also 2 mal)
rect.right = 104 (siehe rect.bottom)Diese Werte "rect.right - rect.left" gibst du dann in der CreateWindow-Funktion als Breite deines Fensters an, dann ist der Client-Bereich exakt so groß, wie du ihn vorher angegeben hast. Nämlich 100 Pixel, wobei das ganze Fenster dann 104 Pixel breit ist, durch den Rahmen.
Ich hoffe du hast das nun soweit verstanden.

-
3. Möglichkeit:
Du holst die 2 Rechtecke via GetWindowRect() und GetClientRect()
bildest dir da jeweils die Breite raus und holst dir die Differenz vom Window-Rect zum Client Rect.
Deine gewünschte Client-Größe + Differenz wäre dann die passende Window-Größe.
-
Danke. AdjustWindowRect(Ex) funktioniert soweit. Jetzt habe ich aber noch ein kleines Problem:
The AdjustWindowRectEx function does not add extra space when a menu bar wraps to two or more rows.
Was mache ich, wenn das Fenster tatsächlich so klein ist und das Menü so lang, daß es auf zwei Zeilen aufgeteilt werden muß? Dann fehlt unten wieder ein Stück.
-
NES-Spieler schrieb:
Danke. AdjustWindowRect(Ex) funktioniert soweit. Jetzt habe ich aber noch ein kleines Problem:
The AdjustWindowRectEx function does not add extra space when a menu bar wraps to two or more rows.
Was mache ich, wenn das Fenster tatsächlich so klein ist und das Menü so lang, daß es auf zwei Zeilen aufgeteilt werden muß? Dann fehlt unten wieder ein Stück.
Da kommste um ne dynamische Anpassung nicht herum, da das Menu ja dynamisch angelegt werden kann.
-
Wie sieht denn diese dynamische Anpassung aus? Ist es das mit GetSystemMetrics oder die Sache mit GetWindowRect/GetClientRect oder etwas ganz anderes?
Das Problem ist ja auch, daß man das eigentlich immer machen muß. Auch wenn ich ein festgelegtes Menü habe und das Fenster in der Größe nicht veränderbar ist. Denn dann kann es ja immer noch sein, daß jemand ein Layout "Extragroß" hat, bei dem das Menü dann nochmal vergrößert wird und so nur auf zwei Zeilen paßt.
Wieso haben die das nicht gleich so gemacht, daß sich eine Größenangabe immer auf den Client-Bereich bezieht? Ich meine, gibt es überhaupt reale Fälle, wo man bei der Größenangabe tatsächlich die wirkliche Größe, inklusive der Titelleiste und des Menüs, haben will?
-
NES-Spieler schrieb:
Wie sieht denn diese dynamische Anpassung aus? Ist es das mit GetSystemMetrics oder die Sache mit GetWindowRect/GetClientRect oder etwas ganz anderes?
Jo Genau so...bzw. WM_SIZE.
NES-Spieler schrieb:
[...]Denn dann kann es ja immer noch sein, daß jemand ein Layout "Extragroß" hat[...]
Huh?!, Wat dat denn ?

-
Jo Genau so...bzw. WM_SIZE.
Ja, welches ist jetzt besser. GetSystemMetrics oder GetWindowRect/GetClientRect?
Layout "Extragroß": http://img243.imageshack.us/img243/6603/extragronx4.png (Der Link verweist direkt auf das Bild, nicht auf die Seite mit ihren ganzen Popups.)
-
NES-Spieler schrieb:
Jo Genau so...bzw. WM_SIZE.
Ja, welches ist jetzt besser. GetSystemMetrics oder GetWindowRect/GetClientRect?
NES-Spieler schrieb:
Layout "Extragroß": http://img243.imageshack.us/img243/6603/extragronx4.png (Der Link verweist direkt auf das Bild, nicht auf die Seite mit ihren ganzen Popups.)
WM_SETTINGCHANGE oder WM_SIZE (bin mir da nit ganz so sicher; TryAndError)
-
Was WM_SIZE ist, weiß ich. Meine Frage war: Welche Funktion(en) soll ich zum Berechnen generell lieber nehmen?
a) GetSystemMetrics
b) GetWindowRect und GetClientRectchar ErmittleAntwort () { char antwort; do { cin>>antwort; antwort=tolower (antwort); } while (antwort!='a' && antwort!='b'); return antwort; }
-
NES-Spieler schrieb:
Was WM_SIZE ist, weiß ich. Meine Frage war: Welche Funktion(en) soll ich zum Berechnen generell lieber nehmen?
a) GetSystemMetrics
b) GetWindowRect und GetClientRectWo ist dein Problem...? Da hast du doch alle Infos. Was willst du mit GetSystemMetrics ?
WM_SIZE
The WM_SIZE message is sent to a window after its size has changed.WM_SIZE
fwSizeType = wParam; // resizing flag
nWidth = LOWORD(lParam); // width of client area
nHeight = HIWORD(lParam); // height of client areaParameters
fwSizeType
Value of wParam. Specifies the type of resizing requested. This parameter can be one of the following values: Value Meaning
SIZE_MAXHIDE Message is sent to all pop-up windows when some other window is maximized.
SIZE_MAXIMIZED The window has been maximized.
SIZE_MAXSHOW Message is sent to all pop-up windows when some other window has been restored to its former size.
SIZE_MINIMIZED The window has been minimized.
SIZE_RESTORED The window has been resized, but neither the SIZE_MINIMIZED nor SIZE_MAXIMIZED value applies.nWidth
Value of the low-order word of lParam. Specifies the new width of the client area.
nHeight
Value of the high-order word of lParam. Specifies the new height of the client area.
Return Values
If an application processes this message, it should return zero.Remarks
If the SetScrollPos or MoveWindow function is called for a child window as a result of the WM_SIZE message, the bRedraw parameter should be nonzero to cause the window to be repainted.Although the width and height of a window are 32-bit values, the nWidth and nHeight parameters of the WM_SIZE message contain only the low-order 16 bits.
QuickInfo
Windows NT: Requires version 3.1 or later.
Windows: Requires Windows 95 or later.
Windows CE: Requires version 1.0 or later.
Header: Declared in winuser.h.
-
Ich hab jetzt nicht den kompletten Thread gelesen, kann also sein, dass das schon mal jemand gepostet hat

Aber: Warum probierst du es nicht so:
Du rechnest mit AdjustWindowRectEx deine Client-Größe aus. Damit erstellst du das Fenster. Dann guckst du mittels GetClientSize, wie groß deine Client-Area jetzt ist. Dann packst du drauf, was du noch brauchst.#define CLIENT_WIDTH 200 // Deine Client-Breite #define CLIENT_HEIGHT 300 // Deine Client-Höhe #define WINDOW_STYLE WS_MODALWINDOW // Dein Window-Style #define WINDOW_STYLEEX 0 // Dein Window-ExStyle ... WinMain( ... ) { RECT rcWnd = { 0, 0, 200, 300 }; AdjustWindowRectEx( &rcWnd, WINDOW_STYLE, TRUE /*Wolltest ja ein Menü...*/, WINDOW_STYLEEX ); // Jetzt ist in rcWnd deine Fenstergröße HWND hWnd = CreateWindow( ... ); // Die Größe des Client-Bereiches RECT rcClientTooBig; GetClientRect( hWnd, &rcClientTooBig ); // Wenn rcClientTooBig nicht deine geünschte Größe hat... if ( rcClientTooBig.right!=CLIENT_WIDTH && rcClientTooBig.bottom!=CLIENT_HEIGHT ) { // Holen wir deine Größe vom ganzen Fenster RECT rcWndTooBig; GetWindowRect( hWnd, &rcWndTooBig ); // Fenster-Größe richten rcWndTooBig.right += CLIENT_WIDTH - rcClientTooBig.right; rcWndTooBig.bottom += CLIENT_HEIGHT - rcClientTooBig.bottom; SetWindowPos( hWnd, NULL, 0, 0, rcWndTooBig.right-rcWndTooBig.left, rcWndTooBig.bottom-rcWndTooBig.top, SWP_NOZORDER|SWP_NOMOVE ); // Jetzt sollte dein Fenster die richtige Größe haben :+1: } }
-
Vielen Dank. Ich kam bisher noch nicht dazu, mich selbst mit den Funktionen zu befassen, demnach hast Du mir also eine Menge Arbeit erspart. Doch jetzt hab ich noch immer ein kleines Problemchen. Hier der Code der WinMain (nur das nötigste):
const DWORD STYLE=WS_OVERLAPPEDWINDOW, EX_STYLE=0; const UINT WIDTH=200, HEIGHT=150; //Die gewünschte Client-Größe HWND window; //Registrieren der Fensterklasse (WNDCLASSEX) ... window=CreateWindowEx (...); /*Die Größenangaben sind hier erstmal CW_USEDEFAULT, da sie ohnehin geändert werden*/ //Erstellen eines "Pseudomenüs" //----------------------------- HMENU menu=CreateMenu (), subMenu=CreatePopupMenu (); /*Je größer der Wert der Abbruchbedingung, desto größer und mehrzeiliger ist logischerweise das Menü*/ for (UINT i=1; i<=3; i++) { AppendMenu (menu, MF_STRING|MF_POPUP, reinterpret_cast<UINT> (subMenu), "ABCDEFGHIJKLMNOP"); AppendMenu (subMenu, MF_STRING, i, "Text"); } SetMenu (window, menu); //Festlegen der korrekten Größe //----------------------------- RECT clientRect; bool firstTime=true; do { RECT rect={0, 0, WIDTH, firstTime?HEIGHT:HEIGHT+(HEIGHT-(clientRect.bottom-clientRect.top))}; /*Beim erstem Mal bekommt das Rechteck die normale Höhe, beim zweiten Mal die normale Höhe plus das Stück, das durch das mehrzeilige Menü überdeckt wird*/ firstTime=false; AdjustWindowRectEx (&rect, STYLE, GetMenu (window)?true:false, EX_STYLE); SetWindowPos (window, NULL, 0, 0, rect.right-rect.left, rect.bottom-rect.top, 0); GetClientRect (window, &clientRect); } while (clientRect.bottom-clientRect.top!=HEIGHT); /*Die Anpassung wird solange vorgenommen, bis der Client-Bereich tatsächlich so groß ist, wie gewünscht.*/ //Anzeigen des Fensters etc. //...Das ganze funktioniert auch genau so, wie ich es mir vorstelle. Bis auf eine Ausnahme: Wenn das Menü so groß ist, daß es den Client-Bereich, der nach dem ersten Schleifendurchlauf (nach dem ersten Aufrufen von GetClientRect) existiert, komplett überdeckt (wenn man also bei der menüschleife den Wert zum beispiel auf 10 setzt), hängt sich das Programm in einer Endlosschleife auf, weil es irgendwie nie die korrekten Größenwerte erreicht. Wo liegt hier mein Fehler?
-
Badestrand schrieb:
// Die Größe des Client-Bereiches RECT rcClientTooBig; GetClientRect( hWnd, &rcClientTooBig ); // Wenn rcClientTooBig nicht deine geünschte Größe hat... if ( rcClientTooBig.right!=CLIENT_WIDTH && rcClientTooBig.bottom!=CLIENT_HEIGHT ) { // Holen wir deine Größe vom ganzen Fenster RECT rcWndTooBig; GetWindowRect( hWnd, &rcWndTooBig ); // Fenster-Größe richten rcWndTooBig.right += CLIENT_WIDTH - rcClientTooBig.right; rcWndTooBig.bottom += CLIENT_HEIGHT - rcClientTooBig.bottom; SetWindowPos( hWnd, NULL, 0, 0, rcWndTooBig.right-rcWndTooBig.left, rcWndTooBig.bottom-rcWndTooBig.top, SWP_NOZORDER|SWP_NOMOVE ); // Jetzt sollte dein Fenster die richtige Größe haben :+1: }Warum nicht das statt deiner Schleife?
-
Stimmt, die Schleife ist überflüssig. Vielen Dank für den Impuls.
Aber: Wenn ich einfach nur abfrage
if (rcClientTooBig.bottom!=CLIENT_HEIGHTdann kann es immer noch zu Problemen kommen, wenn das Menü am Anfang so riesengroß ist, daß es das gesamte ursprüngliche Fenster verdeckt. Da muß dann am Ende doch noch eine Schleife her. Ich habe das funktionierende Programm mal hier aufgeschrieben, falls es jemanden interessiert:
#include <windows.h> LRESULT CALLBACK WindowProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain (HINSTANCE instance, HINSTANCE, LPSTR, int cmdShow) { const DWORD STYLE=WS_OVERLAPPEDWINDOW, EX_STYLE=0; const UINT WIDTH=200, HEIGHT=150; HWND window; /*Punkt 1: Ermitteln der richtigen Fenstergröße ---------------------------------------------*/ RECT rect={0, 0, WIDTH, HEIGHT}; AdjustWindowRectEx (&rect, STYLE, true, EX_STYLE); /*Punkt 2: Erstellen des Fensters -------------------------------*/ WNDCLASSEX wndClassEx={0}; MSG msg; wndClassEx.cbSize=sizeof (WNDCLASSEX); wndClassEx.lpfnWndProc=WindowProc; wndClassEx.hInstance=instance; wndClassEx.hIcon=LoadIcon (NULL, IDI_APPLICATION); wndClassEx.hCursor=LoadCursor (NULL, IDC_ARROW); wndClassEx.hbrBackground=reinterpret_cast<HBRUSH> (BLACK_PEN); wndClassEx.lpszClassName="My Window Class"; wndClassEx.hIconSm=wndClassEx.hIcon; RegisterClassEx (&wndClassEx); window=CreateWindowEx (EX_STYLE, wndClassEx.lpszClassName, "Fenster", STYLE, CW_USEDEFAULT, CW_USEDEFAULT, rect.right-rect.left, rect.bottom-rect.top, NULL, NULL, instance, NULL); /*Punkt 3: Erstellen des Menüs ----------------------------*/ const UINT N=2; /*Diesen Wert ändern, um das Menü zu vergrößern. (Zum Beispiel mal auf 40 setzen.)*/ HMENU menu=CreateMenu (), subMenu=CreatePopupMenu (); for (UINT i=1; i<=N; i++) { AppendMenu (menu, MF_STRING|MF_POPUP, reinterpret_cast<UINT> (subMenu), "ABCDEFGHIJKLMNOP"); AppendMenu (subMenu, MF_STRING, i, "Irgendwas"); } SetMenu (window, menu); /*Punkt 4: Korrigieren der Größe ------------------------------*/ do { GetClientRect (window, &rect); if (rect.bottom!=HEIGHT) { RECT windowRect; GetWindowRect (window, &windowRect); windowRect.bottom+=HEIGHT-rect.bottom; SetWindowPos (window, NULL, 0, 0, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, SWP_NOMOVE); } } while (rect.bottom!=HEIGHT); /*Punkt 5: Start --------------*/ ShowWindow (window, cmdShow); UpdateWindow (window); while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg); DispatchMessage (&msg); } return msg.wParam; } LRESULT CALLBACK WindowProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CLOSE: DestroyWindow (wnd); break; case WM_DESTROY: PostQuitMessage (0); break; default: return DefWindowProc (wnd, msg, wParam, lParam); } return 0; }