方法 : サブクラスをネイティブのコールバック関数を使用したボタン

[このドキュメントはプレビュー版であり、後のリリースで変更されることがあります。 空白のトピックは、プレースホルダーとして挿入されています。]

ここでは、練習方法をサブクラス化する、Windows フォーム コントロール マネージ Windows プロシージャ (WndProc) を使用してネイティブ コードからコールバックを受信するデモンストレーションを行う。 このサンプル プログラムについては 管理ウィンドウ プロシージャでコントロールをサブクラス化 で詳しく説明します。

この例プログラムは、グラデーションの塗りつぶしが Button からサブクラス化されたコントロールに表示する方法を示します。 Windows メッセージを処理する必要があります。 ボタンに、グラデーションの塗りつぶしを表示する簡単では Control から派生したカスタム コントロールを作成します。 カスタマイズ例については、「方法 : グラデーションの塗りつぶしを表示します。」を参照してください。

グラデーション塗りつぶしを表示するボタン コントロールをサブクラス化

  1. Microsoft Visual Studio 2005 でスマート デバイスの Pocket PC プロジェクトを作成します。

  2. Gradientfill クラスをプロジェクトに追加します。

                                public
                                sealed
                                class GradientFill
    {
        // This method wraps the PInvoke to GradientFill.// Parameters://  gr - The Graphics object we are filling//  rc - The rectangle to fill//  startColor - The starting color for the fill//  endColor - The ending color for the fill//  fillDir - The direction to fill//// Returns true if the call to GradientFill succeeded; false// otherwise.publicstaticbool Fill(
            Graphics gr,
            Rectangle rc,
            Color startColor, Color endColor,
            FillDirection fillDir)
        {
    
            // Initialize the data to be used in the call to GradientFill.
            Win32.TRIVERTEX[] tva = new Win32.TRIVERTEX[2];
            tva[0] = new Win32.TRIVERTEX(rc.X, rc.Y, startColor);
            tva[1] = new Win32.TRIVERTEX(rc.Right, rc.Bottom, endColor);
            Win32.GRADIENT_RECT[] gra = new Win32.GRADIENT_RECT[] {
    new Win32.GRADIENT_RECT(0, 1)};
    
            // Get the hDC from the Graphics object.
            IntPtr hdc = gr.GetHdc();
    
            // PInvoke to GradientFill.bool b;
    
            b = Win32.GradientFill(
                    hdc,
                    tva,
                    (uint)tva.Length,
                    gra,
                    (uint)gra.Length,
                    (uint)fillDir);
            System.Diagnostics.Debug.Assert(b, string.Format(
                "GradientFill failed: {0}",
                System.Runtime.InteropServices.Marshal.GetLastWin32Error()));
    
            // Release the hDC from the Graphics object.
            gr.ReleaseHdc(hdc);
    
            return b;
        }
    
        // The direction to the GradientFill will followpublicenum FillDirection
        {
            //// The fill goes horizontally//
            LeftToRight = Win32.GRADIENT_FILL_RECT_H,
            //// The fill goes vertically//
            TopToBottom = Win32.GRADIENT_FILL_RECT_V
        }
    }
    
  3. GradientFilledButton クラスをプロジェクトに追加します。

                                // Extends the standard button control and does some custom
                                // drawing with a GradientFill background
                                public
                                class GradientFilledButton : Button
    {
        // Creates a new instance of the objectpublic GradientFilledButton()
        {
            // Messages required to override// in this control's window procedure.
            WndProcHooker.HookWndProc(this,
                new WndProcHooker.WndProcCallback(this.WM_Paint_Handler),
                Win32.WM_PAINT);
            WndProcHooker.HookWndProc(this,
                new WndProcHooker.WndProcCallback(this.WM_LButtonDown_Handler),
                Win32.WM_LBUTTONDOWN);
            WndProcHooker.HookWndProc(this,
                new WndProcHooker.WndProcCallback(this.WM_LButtonUp_Handler),
                Win32.WM_LBUTTONUP);
            WndProcHooker.HookWndProc(this,
                new WndProcHooker.WndProcCallback(this.WM_MouseMove_Handler),
                Win32.WM_MOUSEMOVE);
            WndProcHooker.HookWndProc(this,
                new WndProcHooker.WndProcCallback(this.WM_KeyDown_Handler),
                Win32.WM_KEYDOWN);
            WndProcHooker.HookWndProc(this,
                new WndProcHooker.WndProcCallback(this.WM_KeyUp_Handler),
                Win32.WM_KEYUP);
        }
    
        // Controls the direction in which the button is filledpublic GradientFill.FillDirection FillDirection
        {
            get
            {
                return fillDirectionValue;
            }
            set
            {
                fillDirectionValue = value;
                Invalidate();
            }
        }
        private GradientFill.FillDirection fillDirectionValue;
    
        // The start color for the GradientFill. This is the color// at the left or top of the control depending on the value// of the FillDirection property.public Color StartColor
        {
            get { return startColorValue; }
            set
            {
                startColorValue = value;
                Invalidate();
            }
        }
        private Color startColorValue = Color.Red;
    
        // The end color for the GradientFill. This is the color// at the right or bottom of the control depending on the// value of the FillDirection propertypublic Color EndColor
        {
            get { return endColorValue; }
            set
            {
                endColorValue = value;
                Invalidate();
            }
        }
        private Color endColorValue = Color.Blue;
    
        // This is the offset from the left or top edge of the button// to start the gradient fill.publicint StartOffset
        {
            get { return startOffsetValue; }
            set
            {
                startOffsetValue = value;
                Invalidate();
            }
        }
        privateint startOffsetValue;
    
        // This is the offset from the right or bottom edge// of the button to end the gradient fill.publicint EndOffset
        {
            get { return endOffsetValue; }
            set
            {
                endOffsetValue = value;
                Invalidate();
            }
        }
        privateint endOffsetValue;
    
        // Used to double-buffer our drawing to avoid flicker between// painting the background, border, focus-rect and the// text of the control.private Bitmap DoubleBufferImage
        {
            get
            {
                if (bmDoubleBuffer == null)
                    bmDoubleBuffer = new Bitmap(
                        this.ClientSize.Width,
                        this.ClientSize.Height);
                return bmDoubleBuffer;
            }
            set
            {
                if (bmDoubleBuffer != null)
                    bmDoubleBuffer.Dispose();
                bmDoubleBuffer = value;
            }
        }
        private Bitmap bmDoubleBuffer;
    
        // Called when the control is resized. When that happens, we need to// recreate the bitmap we use for double-buffering.// e - The arguments for this eventprotectedoverridevoid OnResize(EventArgs e)
        {
            DoubleBufferImage = new Bitmap(
                this.ClientSize.Width,
                this.ClientSize.Height);
            base.OnResize(e);
        }
    
        // Called when the control gets focus. We need to repaint the control// to ensure the focus rectangle is drawn correctly.// e - The arguments for this controlprotectedoverridevoid OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            this.Invalidate();
        }
    
        // Called when the control loses focus. We need to repaint the control// to ensure the focus rectangle is removed.// e - The arguments for this controlprotectedoverridevoid OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);
            this.Invalidate();
        }
    
        // This is set to true when we get a MouseDown event. It is used// to determine if we should fire the Click event when we get// a MouseUpbool gotMouseDown = false;
        bool gotKeyDown = false;
    
        // Called when a mouse button is pressed while the cursor is// in the control.// e - The arguments for this event.protectedoverridevoid OnMouseDown(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                gotMouseDown = true;
            base.OnMouseDown(e);
        }
    
        // Called when a mouse button is released while the cursor is// in the control// e - The arguments for this eventprotectedoverridevoid OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            // If the MouseDown event was fired before this event then// that constitutes a Click.if ((e.Button == MouseButtons.Left) && gotMouseDown)
            {
                base.OnClick(EventArgs.Empty);
                gotMouseDown = false;
            }
        }
    
        // The callback called when the window receives a WM_MOUSEMOVE// message. If we have the mouse captured (the user had previously// clicked down on the button), we redraw the button.// hwnd - The handle to the window that received the// message.// wParam - Indicates whether various virtual keys are// down.// lParam - The coordinates of the cursor// handled - Set to true if we don't want to pass this// message on to the original window procedure.// Returns zero if we process this message.int WM_MouseMove_Handler(
            IntPtr hwnd, uint msg, uint wParam, int lParam,
            refbool handled)
        {
            if (this.Capture)
            {
                Point coord = Win32.LParamToPoint(lParam);
                if (this.ClientRectangle.Contains(coord) !=
                    this.ClientRectangle.Contains(lastCursorCoordinates))
                {
                    DrawButton(hwnd,
                        this.ClientRectangle.Contains(coord));
                }
                lastCursorCoordinates = coord;
            }
            return -1;
        }
        // The coordinates of the cursor the last time we saw a WM_MOUSEMOVE,// WM_LBUTTONDOWN or WM_LBUTTONUP message.
        Point lastCursorCoordinates;
    
        // The callback called when the window receives a WM_LBUTTONDOWN// message. We capture the mouse and draw the button in the "pushed"// state.// hwnd - The handle to the window that received the// message.// wParam - Indicates whether various virtual keys are// down.// lParam - The coordinates of the cursor.// handled - Set to true if we don't want to pass this// message on to the original window procedure.// Returns zero if we process this message.int WM_LButtonDown_Handler(
            IntPtr hwnd, uint msg, uint wParam, int lParam,
            refbool handled)
        {
            // Start capturing the mouse input.this.Capture = true;
            // someone clicked on us so grab the focusthis.Focus();
    
            // draw the button
            DrawButton(hwnd, true);
    
            // Fire the MouseDown event
            lastCursorCoordinates = Win32.LParamToPoint(lParam);
            OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1,
                lastCursorCoordinates.X, lastCursorCoordinates.Y, 0));
    
            // We have handled this windows message and we don't want the// sub-classed window to do anything else.
            handled = true;
            return 0;
        }
    
        // The callback called when the window receives a WM_KEYDOWN message.// If the key was the spacebar, We draw the button in the "pushed"// state.// hwnd - The handle to the window that received the// message// wParam - Specifies the virtual-key code of the// non system key.// lParam - Specifies various attributes about the key// that is down.// handled - Set to true if we don't want to pass this// message on to the original window procedure// Returns>Zero if we process this message.int WM_KeyDown_Handler(
            IntPtr hwnd, uint msg, uint wParam, int lPAram,
            refbool handled)
        {
            if ((wParam == Win32.VK_SPACE) ||
                (wParam == Win32.VK_RETURN))
            {
                DrawButton(hwnd, true);
                handled = true;
                gotKeyDown = true;
            }
            return handled ? 0 : -1;
        }
    
        // The callback called when the window receives a WM_KEYUP message.// If the key was the spacebar, We draw the button in the "un-pushed"// state and fire the Click event.// hwnd - The handle to the window that received the// message// wParam - Specifies the virtual-key code of the non-// system key.// lParam - Specifies various attributes about the key// that is down.// handled - Set to true if we don't want to pass this// message// on to the original window procedure// Returns zero if we process this message.int WM_KeyUp_Handler(
            IntPtr hwnd, uint msg, uint wParam, int lParam,
            refbool handled)
        {
            if (gotKeyDown &&
                ((wParam == Win32.VK_SPACE) ||
                (wParam == Win32.VK_RETURN)))
            {
                DrawButton(hwnd, false);
                OnClick(EventArgs.Empty);
                handled = true;
                gotKeyDown = false;
            }
            return handled ? 0 : -1;
        }
    
        // The callback called when the window receives a WM_LBUTTONUP// message. We release capture on the mouse, draw the button in the// "un-pushed" state and fire the  OnMouseUp eventif the cursor was
        // let go of inside our client area.// hwnd - The handle to the window that received the// message// wParam - Indicates whether various virtual keys are// down.// lParam - The coordinates of the cursor// handled - Set to true if we don't want to pass this// message// on to the original window procedure// Returns zero if we process this message.int WM_LButtonUp_Handler(
            IntPtr hwnd, uint msg, uint wParam, int lParam,
            refbool handled)
        {
            this.Capture = false;
    
            DrawButton(hwnd, false);
    
            lastCursorCoordinates = Win32.LParamToPoint(lParam);
            if (this.ClientRectangle.Contains(lastCursorCoordinates))
                OnMouseUp(new MouseEventArgs(MouseButtons.Left, 1,
                    lastCursorCoordinates.X, lastCursorCoordinates.Y, 0));
            handled = true;
            return 0;
        }
    
    
        // The callback called when the window receives a WM_PAINT message.// We draw the button in the appropriate state.// hwnd - The handle to the window that received the// message// wParam - Indicates whether various virtual keys are// down.// lParam - The coordinates of the cursor// handled - Set to true if we don't want to pass this// message on to the original window procedure// Returns zero if we process this message.int WM_Paint_Handler(
            IntPtr hwnd, uint msg, uint wParam, int lParam,
            refbool handled)
        {
            Win32.PAINTSTRUCT ps = new Win32.PAINTSTRUCT();
    
            Graphics gr = Graphics.FromHdc(Win32.BeginPaint(hwnd, ref ps));
            DrawButton(gr, this.Capture &&
                (this.ClientRectangle.Contains(lastCursorCoordinates)));
            gr.Dispose();
            Win32.EndPaint(hwnd, ref ps);
            handled = true;
            return 0;
        }
    
        // Gets a Graphics object for the provided window handle and then// calls DrawButton(Graphics, bool).// hwnd - The handle to the window to draw as a// button// pressed - If true, the button is draw in the// depressed statevoid DrawButton(IntPtr hwnd, bool pressed)
        {
            IntPtr hdc = Win32.GetDC(hwnd);
            Graphics gr = Graphics.FromHdc(hdc);
            DrawButton(gr, pressed);
            gr.Dispose();
            Win32.ReleaseDC(hwnd, hdc);
        }
    
        // Draws the button on the specified Graphics in the specified// state.// gr - The Graphics object on which to draw the// button// pressed - If true, the button is draw in the// depressed statevoid DrawButton(Graphics gr, bool pressed)
        {
            // get a Graphics object from our background image
            Graphics gr2 = Graphics.FromImage(DoubleBufferImage);
    
            // fill solid up until where the gradient fill startsif (startOffsetValue > 0)
            {
                if (fillDirectionValue ==
                    GradientFill.FillDirection.LeftToRight)
                {
                    gr2.FillRectangle(
                        new SolidBrush(pressed ? EndColor : StartColor),
                        0, 0, startOffsetValue, Height);
                }
                else
                {
                    gr2.FillRectangle(
                        new SolidBrush(pressed ? EndColor : StartColor),
                        0, 0, Width, startOffsetValue);
                }
            }
    
            // draw the gradient fill
            Rectangle rc = this.ClientRectangle;
            if (fillDirectionValue == GradientFill.FillDirection.LeftToRight)
            {
                rc.X = startOffsetValue;
                rc.Width = rc.Width - startOffsetValue - endOffsetValue;
            }
            else
            {
                rc.Y = startOffsetValue;
                rc.Height = rc.Height - startOffsetValue - endOffsetValue;
            }
            GradientFill.Fill(
                gr2,
                rc,
                pressed ? endColorValue : startColorValue,
                pressed ? startColorValue : endColorValue,
                fillDirectionValue);
    
            // fill solid from the end of the gradient fill to the edge of the// buttonif (endOffsetValue > 0)
            {
                if (fillDirectionValue ==
                    GradientFill.FillDirection.LeftToRight)
                {
                    gr2.FillRectangle(
                        new SolidBrush(pressed ? StartColor : EndColor),
                        rc.X + rc.Width, 0, endOffsetValue, Height);
                }
                else
                {
                    gr2.FillRectangle(
                        new SolidBrush(pressed ? StartColor : EndColor),
                        0, rc.Y + rc.Height, Width, endOffsetValue);
                }
            }
    
            // draw the text
            StringFormat sf = new StringFormat();
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Center;
            gr2.DrawString(this.Text, this.Font,
                new SolidBrush(this.ForeColor),
                this.ClientRectangle, sf);
    
            // draw the border.// we need to shrink the width and height by 1 otherwise we// won't get any border on the right or bottom.
            rc = this.ClientRectangle;
            rc.Width--;
            rc.Height--;
            Pen pen = new Pen(SystemColors.WindowFrame);
            // focused buttons have a thicker border on deviceif (this.Focused)
                pen = new Pen(SystemColors.WindowFrame, 3f);
            gr2.DrawRectangle(pen, rc);
    
            // draw from the background image onto the screen
            gr.DrawImage(DoubleBufferImage, 0, 0);
            gr2.Dispose();
        }
    }
    
  4. プロジェクトに、Win32 ヘルパー クラスを追加します。 このコードは「方法 : 使用のプラットフォーム用のヘルパー クラスを呼び出す」にあります。

  5. WinProcHooker クラスをプロジェクトに追加します。 このコードは「方法 : Windows のプロシージャをフックのクラスを使用します。」にあります。

  6. 種類 buttonGFGradientFilledButton をという名前のフォーム変数を宣言します。

                                private GradientFilledButton buttonGF;
    
  7. Form1 クラスのコンストラクターに、サブクラス化されたボタン コントロールの初期化、次のコードを追加します。 このコードは、InitializeComponent メソッドを呼び出す従ってください。 開始および終了色のグラデーションの塗りつぶしと TopToBottom または LeftToRight 塗りつぶし方向を指定できます。

        InitializeComponent();
        this.buttonGF = new GradientFilledButton();
        this.buttonGF.EndColor = System.Drawing.Color.White;
        this.buttonGF.Location = new System.Drawing.Point(71, 24);
        this.buttonGF.Name = "button1";
        this.buttonGF.Size = new System.Drawing.Size(100, 23);
        this.buttonGF.StartColor = System.Drawing.Color.Turquoise;
        this.buttonGF.TabIndex = 1;
        this.buttonGF.Text = "button1";
        this.buttonGF.Click += new System.EventHandler(this.button_Click);
        this.Controls.Add(buttonGF);
    
    
  8. Click クラスに Form1 は、ボタンのイベントのイベント処理コードを追加します。

                                // The event handler called when a button is clicked
                                // sender - The object that raised this event.
                                // e - The arguments for this event.
                                void button_Click(object sender, System.EventArgs e)
        {
            MessageBox.Show("Clicked", "Click event handler");
        }
    
    
  9. グラデーションの塗りつぶしパターンを持つ、フォームの背景を描画するには、OnPaint メソッドをオーバーライド必要に応じてします。

                                // Paints the background of the form with a GradientFill pattern.
                                protected
                                override
                                void OnPaintBackground(PaintEventArgs e)
    {
        // On Windows Mobile Pocket PC 2003, the call to GradientFill// fails with GetLastError() returning 87 (ERROR_INVALID_PARAMETER)// when e.Graphics is used.// Instead, fill into a bitmap and then draw that onto e.Graphics.
        Bitmap bm = new Bitmap(Width, Height);
        Graphics gr = System.Drawing.Graphics.FromImage(bm);
    
        GradientFill.Fill(
            gr,
            this.ClientRectangle,
            Color.LightCyan, Color.SlateBlue,
            GradientFill.FillDirection.TopToBottom);
        e.Graphics.DrawImage(bm, 0, 0);
        gr.Dispose();
        bm.Dispose();
    }
    
  10. アプリケーションをコンパイルして実行します。

参照

処理手順

方法 : Windows のプロシージャをフックのクラスを使用します。

方法 : 使用のプラットフォーム用のヘルパー クラスを呼び出す

方法 : サブクラスをネイティブのコールバック関数を使用して、ツリー ビュー

概念

.NET コンパクトなフレームワーク方法を説明したトピックの検索

その他の技術情報

.NET Compact Framework の相互運用性