Freigeben über


Gewusst wie: Bilden von Unterklassen für eine Schaltfläche durch Verwenden von systemeigenen Rückrufen

Aktualisiert: November 2007

Dieses Thema stellt eine Übung dar, um zu veranschaulichen, wie Unterklassen für ein Windows Forms-Steuerelement gebildet werden, um Rückrufe von systemeigenem Code mit einer verwalteten Fensterprozedur (WndProc) zu empfangen. Dieses Beispielprogramm wird unter Bilden von Steuerelementunterklassen mit einer verwalteten Fensterprozedur ausführlich beschrieben.

Dieses Beispielprogramm zeigt, wie eine graduelle Füllung mit einem Steuerelement angezeigt wird, das als untergeordnete Klasse von Button definiert ist. Hierfür ist die Behandlung von Windows-Meldungen erforderlich. Eine einfachere Möglichkeit, eine graduelle Füllung bei einer Schaltfläche anzuzeigen, besteht im Erstellen eines von Control abgeleiteten benutzerdefinierten Steuerelements. Ein Beispiel finden Sie unter Gewusst wie: Anzeigen einer graduellen Füllung.

So bilden Sie Unterklassen für ein Button-Steuerelement zum Anzeigen einer graduellen Füllung

  1. Erstellen Sie in Microsoft Visual Studio 2005 ein Pocket PC-Projekt für intelligente Geräte.

  2. Fügen Sie dem Projekt die Gradientfill-Klasse hinzu.

    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.
        public static bool 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 follow
        public enum FillDirection
        {
            //
            // The fill goes horizontally
            //
            LeftToRight = Win32.GRADIENT_FILL_RECT_H,
            //
            // The fill goes vertically
            //
            TopToBottom = Win32.GRADIENT_FILL_RECT_V
        }
    }
    
  3. Fügen Sie dem Projekt die GradientFilledButton-Klasse hinzu.

    // Extends the standard button control and does some custom
    // drawing with a GradientFill background
    public class GradientFilledButton : Button
    {
        // Creates a new instance of the object
        public 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 filled
        public 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 property
        public 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.
        public int StartOffset
        {
            get { return startOffsetValue; }
            set
            {
                startOffsetValue = value;
                Invalidate();
            }
        }
        private int startOffsetValue;
    
        // This is the offset from the right or bottom edge
        // of the button to end the gradient fill.
        public int EndOffset
        {
            get { return endOffsetValue; }
            set
            {
                endOffsetValue = value;
                Invalidate();
            }
        }
        private int 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 event
        protected override void 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 control
        protected override void 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 control
        protected override void 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 MouseUp
        bool 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.
        protected override void 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 event
        protected override void 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,
            ref bool 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,
            ref bool handled)
        {
            // Start capturing the mouse input.
            this.Capture = true;
            // someone clicked on us so grab the focus
            this.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,
            ref bool 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,
            ref bool 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 event if 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,
            ref bool 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,
            ref bool 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 state
        void 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 state
        void 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 starts
            if (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
            // button
            if (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 device
            if (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. Fügen Sie dem Projekt die Win32-Hilfsklasse hinzu. Dieser Code ist unter Gewusst wie: Verwenden einer Hilfsklasse für Plattformaufrufe verfügbar.

  5. Fügen Sie dem Projekt die WinProcHooker-Klasse hinzu. Dieser Code ist unter Gewusst wie: Verwenden einer Klasse zum Verknüpfen von Windows-Prozeduren verfügbar.

  6. Deklarieren Sie eine Formularvariable mit der Bezeichnung buttonGF und dem Typ GradientFilledButton.

    private GradientFilledButton buttonGF;
    
  7. Fügen Sie dem Konstruktor der Form1-Klasse den folgenden Code hinzu, durch den das als Unterklasse erstellte Button-Steuerelement initialisiert wird. Dieser Code sollte auf den Aufruf an die InitializeComponent-Methode folgen. Sie können die Start- und Endfarbe der graduellen Füllung sowie die Füllrichtung TopToBottom oder LeftToRight angeben.

    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. Fügen Sie den Ereignisbehandlungscode für das Click-Ereignis der Schaltfläche zur Form1-Klasse hinzu.

    // 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. Überschreiben Sie optional die OnPaint-Methode, um den Hintergrund des Formulars mit dem graduellen Füllmuster zu zeichnen.

    // 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. Kompilieren Sie die Anwendung, und führen Sie sie aus.

Siehe auch

Aufgaben

Gewusst wie: Verwenden einer Klasse zum Verknüpfen von Windows-Prozeduren

Gewusst wie: Verwenden einer Hilfsklasse für Plattformaufrufe

Gewusst wie: Bilden von Unterklassen für TreeView durch Verwenden von systemeigenen Rückrufen

Konzepte

Gewusst-wie-Themen für .NET Compact Framework

Weitere Ressourcen

Interoperabilität in .NET Compact Framework