出会いセンター NativeWindowクラスを用いたサブクラス化(スナップウィンドウの作成)(C#)

NativeWindowクラスを用いたサブクラス化(スナップウィンドウの作成)

Visual Basicでプログラミングをしたことのある方なら”サブクラス化”という言葉を聞いた事があるのでは ないでしょうか。これはかつてVisual Basicを使っていて、「こんなことができればいいのに!」という 思いを実現するために多くの方が使っていた手法だと思います。ここではそれをC#で行う方法についての話です。

.NETでは継承でも解決できるが…

Visual Basic6.0つまりVBが.NETになる以前ではまだ継承が完全にはサポートされていませんでした。 従って、サブクラス化などという泥臭いことを強いられていたわけですが、 VB.NETでは完全にオブジェクト指向がサポートされたとこにより、継承も思う存分使うことができるように なりました。そうなると例えば、Formクラスなどを継承して、WndProc()メソッドをオーバーライドすることによって 、サブクラス化と同様の結果を得ることができます。これはC#についても同様です。

それで十分ではないかと思われるかもしれません。確かに継承という機構は非常に 強力なため多くの場合問題は解決されるでしょう。しかし、ある程度作ってしまったコントロール クラスに何かしらの機能を付け加えたいという場合や、同時に二つ以上の機能を付加したい場合 などには、わざわざ継承を用いるのは面倒です。こんなときにはフォームにコントロールを貼り付けるような 感覚で機能が追加できたらいいと思われます。このような要望に答えてくれ得るのがサブクラス化でしょう。

Win32APIで直接行うサブクラス化は危険

では、どのようにしてサブクラス化を行えばいいのでしょうか?VB6.0などでは、SetWindowLong()やCallWindowProc() などのWin32 APIを直接呼び出して行っていました。確かに.NETにおいてもこのような方法でサブクラス化を実現できます。 事実このあと紹介するNativeWindowクラスでも内部ではこれらのWin32 APIを使用しています。しかし、ガベージコレクション機能や、 関数ポインタが単なるプロキシである等複雑な状況下にあるマネージコードにおいては、 色々と考慮にいれて非常に高度な技術を要すると思われます。

サブクラス化はNativeWindowクラスで行う

以上のことから、マネージ環境においてはサブクラス化用に予め用意されているNativeWindowクラスを用いるのが 最良の選択であるということです。このクラスの使い方はこちらのMSDNライブラリにサンプルつきで説明がありますので、 ここでは以下のようなサンプルを作ってみました。

スナップウィンドウを作成する

WinAmpなどでおなじみのスナップ機能をフォームに付加するサンプルです。今回のサンプルプログラムでは 画面左端に吸着するようになっています。

#region using
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
#endregion

namespace NativeWindow_Subclassing
{
    /// <summary>
    /// Form1 の概要の説明です。
    /// </summary>
    public class Form1 : System.Windows.Forms.Form
    {
        /// <summary>
        /// 必要なデザイナ変数です。
        /// </summary>
        private System.ComponentModel.Container components = null;
        private NewNativeWindow nnwWindowPrc;

        #region コンストラクタ
        public Form1()
        {
            //
            // Windows フォーム デザイナ サポートに必要です。
            //
            InitializeComponent();
            nnwWindowPrc = new NewNativeWindow(this);
        }
        #endregion

        #region Dispose処理
        /// <summary>
        /// 使用されているリソースに後処理を実行します。
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }
        #endregion

        #region Windows フォーム デザイナで生成されたコード
        /// <summary>
        /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディタで変更しないでください。
        /// </summary>
        private void InitializeComponent()
        {
            //
            // Form1
            //
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
            this.ClientSize = new System.Drawing.Size(264, 273);
            this.Name = "Form1";
            this.Text = "Form1";

        }
        #endregion

        #region メインエントリポイント
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.Run(new Form1());
        }
        #endregion

    }

    #region NewNativeWindowクラス

    public class NewNativeWindow : NativeWindow
    {
        #region Win32
        private const int WM_MOVING = 0x0216;

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }
        #endregion

        private Form frmParent;
        private Point pntDragCenter = new Point(int.MaxValue, int.MaxValue);
        private int intDisplacement = 0;
        private const int cintSnapableGap = 10;

        public NewNativeWindow(Form parent)
        {
            parent.HandleCreated += new EventHandler(this.OnHandleCreated);
            parent.HandleDestroyed+= new EventHandler(this.OnHandleDestroyed);
            this.frmParent = parent;
        }

        internal void OnHandleCreated(object sender, EventArgs e)
        {
            AssignHandle(((Form)sender).Handle);
        }

        internal void OnHandleDestroyed(object sender, EventArgs e)
        {
            ReleaseHandle();
        }

        protected override void WndProc(ref Message m)
        {
            if(m.Msg == WM_MOVING)
            {
                if(frmParent.Left <= cintSnapableGap)
                {
                    if(Cursor.Position.X - pntDragCenter.X >0) ++intDisplacement;
                    RECT r = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
                    if(intDisplacement <= cintSnapableGap)
                    {
                        r.left = 0;
                        r.right = frmParent.Width;
                        pntDragCenter = Cursor.Position;
                    }
                    else
                    {
                        r.left = cintSnapableGap + 1;
                        r.right = r.left + frmParent.Width;
                        intDisplacement = 0;
                        pntDragCenter = new Point(int.MaxValue, int.MaxValue);
                    }
                    Marshal.StructureToPtr(r, m.LParam, false);
                }
            }
            base.WndProc(ref m);
        }
    }
    #endregion
}
無料 出会い系 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料出会い 無料 出会い系 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料 出会い掲示板 アクセスカウンター 無料 出会い 無料 出会い 無料 出会い デリヘル 広島 無料出会い 無料 出会い 無料 出会い 印鑑 アダルト動画 無料 出会い系 無料 出会い 無料 出会い 無料 出会い 無料 出会い 無料出会い 無料 出会い系 無料 出会い 無料 出会い 無料出会い 無料 出会い 無料 出会い オオクワガタ 無料 出会い