方法 : サブクラスをネイティブのコールバック関数を使用したボタン
[このドキュメントはプレビュー版であり、後のリリースで変更されることがあります。 空白のトピックは、プレースホルダーとして挿入されています。]
ここでは、練習方法をサブクラス化する、Windows フォーム コントロール マネージ Windows プロシージャ (WndProc) を使用してネイティブ コードからコールバックを受信するデモンストレーションを行う。 このサンプル プログラムについては 管理ウィンドウ プロシージャでコントロールをサブクラス化 で詳しく説明します。
この例プログラムは、グラデーションの塗りつぶしが Button からサブクラス化されたコントロールに表示する方法を示します。 Windows メッセージを処理する必要があります。 ボタンに、グラデーションの塗りつぶしを表示する簡単では Control から派生したカスタム コントロールを作成します。 カスタマイズ例については、「方法 : グラデーションの塗りつぶしを表示します。」を参照してください。
グラデーション塗りつぶしを表示するボタン コントロールをサブクラス化
Microsoft Visual Studio 2005 でスマート デバイスの Pocket PC プロジェクトを作成します。
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 } }
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(); } }
プロジェクトに、Win32 ヘルパー クラスを追加します。 このコードは「方法 : 使用のプラットフォーム用のヘルパー クラスを呼び出す」にあります。
WinProcHooker クラスをプロジェクトに追加します。 このコードは「方法 : Windows のプロシージャをフックのクラスを使用します。」にあります。
種類 buttonGFGradientFilledButton をという名前のフォーム変数を宣言します。
private GradientFilledButton buttonGF;
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);
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"); }
グラデーションの塗りつぶしパターンを持つ、フォームの背景を描画するには、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(); }
アプリケーションをコンパイルして実行します。
参照
処理手順
方法 : Windows のプロシージャをフックのクラスを使用します。
方法 : 使用のプラットフォーム用のヘルパー クラスを呼び出す
方法 : サブクラスをネイティブのコールバック関数を使用して、ツリー ビュー
概念
.NET コンパクトなフレームワーク方法を説明したトピックの検索