9 Stimmen

Überwindung der vom Betriebssystem auferlegten Mindestgrößenbeschränkung für Windows-Formulare

In einer Anwendung, die ich entwickle, muss ich in der Lage sein, ein Windows-Formular kleiner zu machen als die vom Betriebssystem vorgegebene Mindesthöhe (36 px in Vista). Ich habe versucht, WM_GETMINMAXINFO abzufangen und meine eigenen Informationen bereitzustellen, um die OS-Grenzen außer Kraft zu setzen, aber das funktioniert nur für den Benutzer. Über den Code kann ich die Höhe auf einen Wert setzen, der kleiner ist als der Grenzwert, aber meine Änderung funktioniert nur, bis WM_WINDOWPOSCHANGED in die Nachrichtenwarteschlange gestellt wird (was kurz nach der Änderung der Höhe geschieht).

17voto

Zach Johnson Punkte 22572

Nach vielen Experimenten und Versuchen habe ich eine Lösung gefunden. Ich habe OnResize überschrieben und die Größe des Formulars an die ListBox darin angepasst (siehe meinen Kommentar zu John Saunders Antwort).

Wie ich in meiner Frage erwähnt habe, habe ich festgestellt, dass die Größe des Formulars nach dem Senden von WM_WINDOWPOSCHANGED zurückgeht. Weitere Untersuchungen ergaben, dass der Größenrückgang tatsächlich beginnt, wenn WM_WINDOWPOSCHANGING gesendet wird.

WM_WINDOWPOSCHANGING ist die Schwestermeldung von WM_WINDOWPOSCHANGED, die auftritt, bevor sich die Fenstergröße tatsächlich ändert. Ich weiß nicht, warum, aber aus irgendeinem Grund passt WM_WINDOWPOSCHANGING die Größe des Formulars blind an die vom Betriebssystem vorgegebenen Grenzen an (offenbar wird das Fenster nicht mit WM_GETMINMAXINFO abgefragt). So musste ich WM_WINDOWPOSCHANGING abfangen und überschreiben es mit der Größe, die ich wirklich wollte.

Das bedeutet, dass ich die Größe des Formulars nicht mehr mit OnResize anpasse, sondern dass ich die Größe des Formulars anpasse, wenn ich WM_WINDOWPOSCHANGING erhalte. Das ist sogar besser als OnResize, weil es kein damit verbundenes Flackern gibt, das auftritt, wenn die Größe geändert wird und dann wieder geändert wird, wenn die Größe während OnResize angepasst wird.

Außerdem ist es notwendig, WM_GETMINMAXINFO abzufangen und zu überschreiben, andernfalls nützt Ihnen auch das Abfangen von WM_WINDOWPOSCHANGING nichts.

using System.Runtime.InteropServices;

private const int WM_WINDOWPOSCHANGING = 0x0046;
private const int WM_GETMINMAXINFO = 0x0024;

protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_WINDOWPOSCHANGING)
    {
        WindowPos windowPos = (WindowPos)m.GetLParam(typeof(WindowPos));

        // Make changes to windowPos

        // Then marshal the changes back to the message
        Marshal.StructureToPtr(windowPos, m.LParam, true);
    }

    base.WndProc(ref m);

    // Make changes to WM_GETMINMAXINFO after it has been handled by the underlying
    // WndProc, so we only need to repopulate the minimum size constraints
    if (m.Msg == WM_GETMINMAXINFO)
    {
        MinMaxInfo minMaxInfo = (MinMaxInfo)m.GetLParam(typeof(MinMaxInfo));
        minMaxInfo.ptMinTrackSize.x = this.MinimumSize.Width;
        minMaxInfo.ptMinTrackSize.y = this.MinimumSize.Height;
        Marshal.StructureToPtr(minMaxInfo, m.LParam, true);
   }
}

struct WindowPos
{
     public IntPtr hwnd;
     public IntPtr hwndInsertAfter;
     public int x;
     public int y;
     public int width;
     public int height;
     public uint flags;
}

struct POINT
{
    public int x;
    public int y;
}

struct MinMaxInfo
{
    public POINT ptReserved;
    public POINT ptMaxSize;
    public POINT ptMaxPosition;
    public POINT ptMinTrackSize;
    public POINT ptMaxTrackSize;
}

0 Stimmen

Zach, dein Ansatz funktioniert wie ein Zauber und hilft mir sehr. Danke!

0 Stimmen

Ausgezeichnet, ich schulde Ihnen ein Exemplar von WindowTabs, wenn Sie daran interessiert sind!

0 Stimmen

@MauriceFlanagan Klar, schick mir eine DM auf Twitter (ich bin @zachoverflow) oder schick mir eine E-Mail an zach at zachjohnson.net.

4voto

Ace Punkte 49

Alexey war so nah dran!

    protected override void SetBoundsCore(int x,int y,int width, int height,BoundsSpecified specified)
    {
        base.SetBoundsCore(x, y, this.MinimumSize.Width, this.MinimumSize.Height, specified);
    }

Das hat bei mir funktioniert. Ich setze die Mindestgröße des Formulars auf die tatsächliche Größe des Formulars, die ich haben möchte.

In meinem Projekt ist das alles, was ich tun muss, um das Formular winzig zu machen. Das könnte daran liegen, dass die Einstellung der Mindestgröße SetBoundsCore auslöst, oder vielleicht tue ich etwas anderes, das es auslöst; in diesem Fall schätze ich, dass Sie Folgendes tun müssen irgendwie SetBoundsCore selbst auslösen.

2voto

Alexey Punkte 11

Beim Spielen mit der minimalen Formulargröße ist mir aufgefallen, dass die minimale Formulargröße in Form.SetBoundsCore(...) auf die minimale Systemformulargröße beschränkt ist. Wenn ich in IL disassemly schauen, fand ich, dass diese .Net-Methode immer korrigiert, was Sie es (Breite und Höhe) zu SystemInformation.MinimumWindowSize geben, wenn sie kleiner sind und das Formular nicht ein Elternteil und seine FormBorderStyle ist FixedSingle, Fixed3D, FixedDialog oder Sizable.

Die einfachste Lösung für dieses Problem ist, WM_WINDOWPOSCHANGING nicht zu behandeln, sondern einfach die FormBorderStyle = System.Windows.Forms.FormBorderStyle.None im Formularkonstruktor.

2 Stimmen

Das wird gut funktionieren, wenn Sie nicht wollen, einen Rahmen auf Ihrem Formular, aber wenn Sie den Rahmen zu halten, sondern haben ein etwas kleineres Fenster (denken Intellisense Vorschlag Fenster mit nur 1 Element in ihm), dann eine andere Route erforderlich sein wird.

1voto

smirkingman Punkte 5963

Ich wünschte, ich könnte Zach mehr als +1 dafür geben, es ist großartig und hat mir den Hintern gerettet. Für zukünftige Leser, hier ist die VB-Übersetzung von Zachs Code:

Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Imports System.Drawing

Public Class MyForm

    ' Ghastly hack to allow the form to be narrower than the widows-imposed limit (about 132 in WIndows 7)
    ' Thanks to http://stackoverflow.com/questions/992352/overcome-os-imposed-windows-form-minimum-size-limit

    Private Const WM_WINDOWPOSCHANGING As Integer = &H46
    Private Const WM_GETMINMAXINFO As Integer = &H24
    Protected Overrides Sub WndProc(ByRef m As Message)
        If m.Msg = WM_WINDOWPOSCHANGING Then
            Dim windowPos As WindowPos = CType(m.GetLParam(GetType(WindowPos)), WindowPos)

            ' Make changes to windowPos

            ' Then marshal the changes back to the message
            Marshal.StructureToPtr(windowPos, m.LParam, True)
        End If

        MyBase.WndProc(m)

        ' Make changes to WM_GETMINMAXINFO after it has been handled by the underlying
        ' WndProc, so we only need to repopulate the minimum size constraints
        If m.Msg = WM_GETMINMAXINFO Then
            Dim minMaxInfo As MINMAXINFO = DirectCast(m.GetLParam(GetType(MINMAXINFO)), MINMAXINFO)
            minMaxInfo.ptMinTrackSize.X = Me.MinimumSize.Width
            minMaxInfo.ptMinTrackSize.Y = Me.MinimumSize.Height
            Marshal.StructureToPtr(minMaxInfo, m.LParam, True)
        End If
    End Sub

    Private Structure WindowPos
        Public hwnd As IntPtr
        Public hwndInsertAfter As IntPtr
        Public x As Integer
        Public y As Integer
        Public width As Integer
        Public height As Integer
        Public flags As UInteger
    End Structure
    <StructLayout(LayoutKind.Sequential)> _
    Private Structure MINMAXINFO
        Dim ptReserved As Point
        Dim ptMaxSize As Point
        Dim ptMaxPosition As Point
        Dim ptMinTrackSize As Point
        Dim ptMaxTrackSize As Point
    End Structure

    .... rest of the form

End Class

0voto

John Saunders Punkte 159011

Sie meinen, abgesehen von der Verwendung eines anderen Betriebssystems?

Wie wäre es mit "Kein Formular verwenden"? Wie groß ist das Ding, das Sie anzeigen müssen? Ein Pixel? Braucht es die volle Windows Forms-Funktionalität?

Ich weiß zwar nicht genau, wie man das macht, aber es könnte ein Anfang für Sie sein - denken Sie über den Tellerrand hinaus.

1 Stimmen

Mein Formular enthält eine ListBox (die ein oder mehrere Elemente enthält) und ich versuche, das Formular an die Größe der ListBox anzupassen. Wenn die ListBox nur ein Element enthält, muss die Höhe des Formulars nur 16 px plus die Höhe der Ränder (die variiert) sein. In Vista, die Gesamthöhe kommt in der Regel bei etwa 32 px, und so Vista's 36 px minimale Höhe verlässt extra Leerraum am unteren Rand des Formulars (die hässlich aussieht). In XP ist die Situation die gleiche.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X