9

私が開発しているアプリケーションでは、Windows フォームをオペレーティング システムによって課される最小の高さ制限 (Vista では 36 ピクセル) よりも小さくする必要があります。WM_GETMINMAXINFO をインターセプトし、独自の情報を提供して OS の制限をオーバーライドしようとしましたが、これはユーザーに対してのみ機能します。コードから高さを制限より小さい値に設定できますが、私の変更は WM_WINDOWPOSCHANGED がメッセージ キューにポストされるまでしか機能しません (これは高さを変更した直後に発生します)。

4

7 に答える 7

16

多くの実験と試行錯誤の後、解決策を発見しました。OnResize をオーバーライドし、フォームのサイズをその中の ListBox に合わせていました (John Saunders の回答に関する私のコメントを参照してください)。

質問で述べたように、WM_WINDOWPOSCHANGED が送信された後、フォームのサイズが縮小することに気付きました。さらに調査した結果、WM_WINDOWPOSCHANGING が送信されたときにサイズの回帰が実際に開始されることが明らかになりました。

WM_WINDOWPOSCHANGING は、ウィンドウ サイズが実際に変更される前に発生する WM_WINDOWPOSCHANGED の姉妹メッセージです。理由はわかりませんが、何らかの理由で、WM_WINDOWPOSCHANGING はフォームのサイズを OS で指定された制限に盲目的に適合させます (明らかに、WM_GETMINMAXINFO でウィンドウを照会しません)。したがって、WM_WINDOWPOSCHANGING をインターセプトし、必要なサイズでオーバーライドする必要がありました。

これは、OnResize を使用してフォームのサイズを調整するのではなく、WM_WINDOWPOSCHANGING を受け取ったときにフォームのサイズを調整することを意味します。これは、サイズが変更され、OnResize 中にサイズが適合されるときに再度変更されるときに発生するちらつきがないため、OnResize よりもさらに優れています。

また、WM_GETMINMAXINFO をインターセプトしてオーバーライドする必要があります。そうしないと、WM_WINDOWPOSCHANGING をインターセプトしても何の役にも立ちません。

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;
}
于 2009-06-15T01:10:06.297 に答える
4

アレクセイはとても近かった!

    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);
    }

私のためにトリックをしました。フォームの最小サイズを、フォームの実際のサイズにしたいものに設定します。

私のプロジェクトでは、フォームを小さくするために必要なのはこれだけです。これは、最小サイズを設定すると SetBoundsCore がトリガーされるか、それをトリガーする何か他のことを行っているためです。その場合、何らかの方法でSetBoundsCore を自分でトリガーする必要があると思います。

于 2012-11-30T01:12:29.230 に答える
1

そのためにザックに+1以上を与えることができればいいのですが、それは素晴らしく、ベーコンを節約しました。将来の読者のために、ZachのコードのVB翻訳は次のとおりです。

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
于 2011-07-23T15:19:34.160 に答える
1

最小フォームサイズで遊んでいると、Form.SetBoundsCore(...)の最小フォームサイズがシステムの最小フォームサイズに制限されていることに気付きました。ILを逆に調べてみると、この.Netメソッドは、SystemInformation.MinimumWindowSizeが小さく、フォームに親がなく、FormBorderStyleがFixedSingle、Fixed3Dの場合、常にSystemInformation.MinimumWindowSizeに指定したもの(幅と高さ)を修正することがわかりました。 、FixedDialogまたはSizable。

この問題の最も簡単な解決策は、WM_WINDOWPOSCHANGINGを処理するのではなく、フォームコンストラクターでFormBorderStyle=System.Windows.Forms.FormBorderStyle.Noneを設定することです。

于 2011-11-11T04:06:41.387 に答える
0

別のOSを使用する以外に、ということですか?

「フォームを使わない」はどうですか?これはどのくらいの大きさで表示する必要がありますか? ピクセル?Windows フォームの完全な機能が必要ですか?

さて、私は上記を行う方法を正確には知りませんが、それはあなたにとっての出発点かもしれません - (境界) ボックスの外側を考えてください。

于 2009-06-14T23:39:10.930 に答える
0

私はザックの答えに従いましたが、それは私の問題をほとんど解決しました。ただし、デュアル モニター設定では、2 番目の画面で最大化するとフォームが消えました。何らかの理由で、Windows はフォームを可視領域の外に配置しました。プライマリ画面のテストを追加すると、この問題が解決されました。

if (m.Msg == (int)CWinApi.Messages.WM_GETMINMAXINFO)
{
    if (this.FormBorderStyle == System.Windows.Forms.FormBorderStyle.None)
    {
        Screen screen = Screen.FromControl(this);

        if (screen.Primary)
        {
            CWinApi.MINMAXINFO minMaxInfo = (CWinApi.MINMAXINFO)m.GetLParam(typeof(CWinApi.MINMAXINFO));

            minMaxInfo.ptMaxSize.x = screen.WorkingArea.Size.Width;
            minMaxInfo.ptMaxSize.y = screen.WorkingArea.Size.Height;
            minMaxInfo.ptMaxPosition.x = screen.WorkingArea.X;
            minMaxInfo.ptMaxPosition.y = screen.WorkingArea.Y;

            System.Runtime.InteropServices.Marshal.StructureToPtr(minMaxInfo, m.LParam, true);
        }
    }
}
于 2016-08-23T08:49:38.337 に答える