2007年03月31日

固定なフォーム

コードは嘘を付きません。世には四月馬鹿などと言う行事もあるようですが、コードだけなら何も問題はありません。ま、私は明日更新するつもり無いですけど。

ごく簡単なランチャを 1 時間ぐらいででっちあげたんですが、そのときにフォームのサイズの変更を制限したいなと思いまして。取りあえず Resize イベント辺りで元サイズに書き戻すように実装してみたらこれがまあちらついて堪りません。

じゃあどうするかという時に、イベントを探すんではなく Windows メッセージを探すのが私の捻くれたところ。WM_WINDOWPOSCAHNGING をハンドルすれば目的を達成できそうでした。

ちょっとした問題点として、このメッセージの LParam が構造体へのポインタであることが挙げられます。C++/CLI ならともかく、C# では Marshal クラスで構造体にコピーするか unsafe コンテキストでポインタとして扱うかってことになりますが、それ以前に構造体の宣言が面倒です。

そこで、ここは一つ Marshal.ReadInt32/WriteInt32 で直接いじることにしました。注意するのはオフセットで、構造体の先頭に HWND 型があるため固定値決め撃ちってわけにはいきません。Marshal.SizeOf もありますが、もうちょっと簡単に IntPtr.Size プロパティを使って計算しましょう。

以下に、簡単な実装コードを書いておきます。なお、WM_WINDOWPOSCHANGING に使われる WINDOWPOS 構造体には Z オーダー位置、各種フラグが用意されていますが、簡便のため今回の実装では位置とサイズしか処理しません。興味のある方は調べてみて下さい。

ちなみに、これで高さを固定して、フォームの上辺を掴んでリサイズさせようとしたりすると結構気持ち悪い動きになります。

// C# 2.0 or later
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public class MovableForm : Form {
    protected override void WndProc(ref Message m) {
        const int WmWindowPosChanging = 0x46;
        switch (m.Msg) {
            case WmWindowPosChanging :
                OnMoving(m.LParam);
                break;
        }
        base.WndProc(ref m);
    }
    private void OnMoving(IntPtr lParam) {
        int offset = IntPtr.Size * 2;
        int x = Marshal.ReadInt32(lParam, offset);
        int y = Marshal.ReadInt32(lParam, offset + 4);
        int width = Marshal.ReadInt32(lParam, offset + 8);
        int height = Marshal.ReadInt32(lParam, offset + 12);
        FormMovingEventArgs e
            = new FormMovingEventArgs(x, y, width, height);
        this.OnMoving(e);
        Marshal.WriteInt32(lParam, offset, e.X);
        Marshal.WriteInt32(lParam, offset + 4, e.Y);
        Marshal.WriteInt32(lParam, offset + 8, e.Width);
        Marshal.WriteInt32(lParam, offset + 12, e.Height);
    }
    private static readonly object EventMoving = new object();
    protected virtual void OnMoving(FormMovingEventArgs e) {
        FormMovingEventHandler handler
            = this.Events[EventMoving] as FormMovingEventHandler;
        if (handler != null)
            handler(this, e);
    }
    public event FormMovingEventHandler Moving {
        add    { this.Events.AddHandler(EventMoving, value); }
        remove { this.Events.RemoveHandler(EventMoving, value); }
    }
}

public delegate void FormMovingEventHandler(
    object sender, FormMovingEventArgs e);

public class FormMovingEventArgs : EventArgs {
    public FormMovingEventArgs(Rectangle bounds)
        : this(bounds.X, bounds.Y, bounds.Width, bounds.Height) {
    }
    public FormMovingEventArgs(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }
    public int X {
        get { return this.x; }
        set { this.x = value; }
    }
    public int Y {
        get { return this.y; }
        set { this.y = value; }
    }
    public int Width {
        get { return this.width; }
        set { this.width = value; }
    }
    public int Height {
        get { return this.height; }
        set { this.height = value; }
    }
    private int x;
    private int y;
    private int width;
    private int height;
}

// 使用例
public class Test {
    [STAThread] public static void Main() {
        MovableForm f = new MovableForm();
        f.Moving += delegate(object sender, FormMovingEventArgs e) {
            if (e.Width > 300)
                e.Width = 300;
            if (e.X < 10)
                e.X = 0;
        };
        Application.Run(f);
    }
}
' VB8 or later
Imports System
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

Public Class MovableForm
    Inherits Form
    Protected Overrides Sub WndProc(ByRef m As Message)
        Const WmWindowPosChanging As Integer = &H46
        Select Case (m.Msg)
            Case WmWindowPosChanging
                OnMoving(m.LParam)
                Exit Select
        End Select
        MyBase.WndProc(m)
    End Sub
    Private Sub OnMoving(ByVal lParam As IntPtr)
        Dim offset As Integer = IntPtr.Size * 2
        Dim x As Integer = Marshal.ReadInt32(lParam, offset)
        Dim y As Integer = Marshal.ReadInt32(lParam, offset + 4)
        Dim width As Integer = Marshal.ReadInt32(lParam, offset + 8)
        Dim height As Integer = Marshal.ReadInt32(lParam, offset + 12)
        Dim e As FormMovingEventArgs _
            = New FormMovingEventArgs(x, y, width, height)
        Me.OnMoving(e)
        Marshal.WriteInt32(lParam, offset, e.X)
        Marshal.WriteInt32(lParam, offset + 4, e.Y)
        Marshal.WriteInt32(lParam, offset + 8, e.Width)
        Marshal.WriteInt32(lParam, offset + 12, e.Height)
    End Sub
    Private Shared ReadOnly EventMoving As New Object()
    Protected Overridable Sub OnMoving(ByVal e As FormMovingEventArgs)
        RaiseEvent Moving(Me, e)
    End Sub
    Public Custom Event Moving As FormMovingEventHandler
        AddHandler(ByVal value As FormMovingEventHandler)
            Me.Events.AddHandler(EventMoving, value)
        End AddHandler
        RemoveHandler(ByVal value As FormMovingEventHandler)
            Me.Events.RemoveHandler(EventMoving, value)
        End RemoveHandler
        RaiseEvent(ByVal sender As Object, e As FormMovingEventArgs)
            Dim handler As FormMovingEventHandler _
              = TryCast(Me.Events(EventMoving), FormMovingEventHandler)
            If handler IsNot Nothing Then
                handler(Me, e)
            End If
        End RaiseEvent
    End Event
End Class

Public Delegate Sub FormMovingEventHandler( _
  ByVal sender As Object, ByVal e As FormMovingEventArgs)

Public Class FormMovingEventArgs
    Inherits EventArgs

    Public Sub New(ByVal bounds As Rectangle)
        Me.New(bounds.X, bounds.Y, bounds.Width, bounds.Height)
    End Sub
    Public Sub New(ByVal x As Integer, ByVal y As Integer, _
          ByVal width As Integer, ByVal height As Integer)
        Me._x = x
        Me._y = y
        Me._width = width
        Me._height = height
    End Sub
    Public Property X() As Integer
        Get
            Return Me._x
        End Get
        Set(ByVal value As Integer)
            Me._x = value
        End Set
    End Property
    Public Property Y() As Integer
        Get
            Return Me._y
        End Get
        Set(ByVal value As Integer)
            Me._y = value
        End Set
    End Property
    Public Property Width() As Integer
        Get
            Return Me._width
        End Get
        Set(ByVal value As Integer)
            Me._width = value
        End Set
    End Property
    Public Property Height As Integer
        Get
            Return Me._height
        End Get
        Set(ByVal value As Integer)
            Me._height = value
        End Set
    End Property
    Private _x As Integer
    Private _y As Integer
    Private _width As Integer
    Private _height As Integer
End Class


' 使用例
Public Class Test
    Public Shared Sub Main()
        Dim f As New MovableForm()
        AddHandler f.Moving, AddressOf PreventMove
        Application.Run(f)
    End Sub
    Private Shared Sub PreventMove(ByVal sender As Object, _
                                   ByVal e As FormMovingEventArgs)
        If e.Width > 300 Then
            e.Width = 300
        End If
        If e.X < 10 Then
            e.X = 0
        End If
    End Sub
End Class
posted by Hongliang at 22:16| Comment(0) | TrackBack(1) | .NET | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.seesaa.jp/tb/37387529

この記事へのトラックバック

[お仕事] C# の virtual 関係が自分的に勘違いしやすいのでまとめ。
Excerpt: 関連キーワード virtual, override, abstract, sealed, new, base クラスに関して abstract抽象クラス sealed継承禁止 abstract cl..
Weblog: drill256の日記
Tracked: 2007-04-22 10:03

ここ(hongliang.seesaa.net)で公開しているものについて、利用は自由に行って頂いて構いません。改変、再頒布もお好きになさって下さい。利用に対しこちらが何かを要求することはありません。

ただし、公開するものを使用、または参考したことによって何らかの損害等が生じた場合でも、私はいかなる責任も負いません。

あ、こんなのに使ったってコメントを頂ければ嬉しいです。

×

この広告は1年以上新しい記事の投稿がないブログに表示されております。