Freigeben über


SO WIRD'S GEMACHT: Unterklasse einer Schaltfläche durch Verwenden von systemeigene Rückrufen

Dieser Dokumentation für die Vorschau nur ist und in späteren Versionen geändert. Leere Themen wurden als Platzhalter eingefügt.]

Dieses Thema ist eine Übung für das Vorführen wie um eine Unterklasse eines Windows Forms steuern, um Rückrufe von systemeigenem Code mit einer verwaltete Windows-Prozedur (WndProc) zu empfangen. Dieses Beispielprogramm wird Bilden von Unterklassen von Steuerelementen mit einer verwalteten Windows-Prozedur detailliert beschrieben.

Dieses Beispielprogramm zeigt, wie eine graduelle Füllung mit einem Steuerelement Unterklassen von Button anzuzeigen. Behandeln von Windows-Meldungen benötigt. Eine einfachere Möglichkeit, eine graduelle Füllung bei einer Schaltfläche anzuzeigen ist die Erstellung ein benutzerdefiniertes Steuerelements von Control abgeleitet. Ein Beispiel finden Sie unter SO WIRD'S GEMACHT: Ein Verlauf anzeigen.

Um eine Unterklasse ein Button-Steuerelement eine graduelle Füllung anzuzeigen

  1. Erstellen Sie in Microsoft Visual Studio 2005 ein Smart Device Pocket PC-Projekt.

  2. Fügen Sie die Gradientfill-Klasse hinzu Ihres Projekts.

                                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. Fügen Sie die GradientFilledButton-Klasse hinzu Ihres Projekts.

                                // 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. Fügen Sie die Hilfsklasse Win32 Ihrem Projekt hinzu. Dieser Code ist im SO WIRD'S GEMACHT: Verwenden einer Hilfsklasse für Platform Invokes verfügbar.

  5. Fügen Sie die Klasse WinProcHooker unsere Projekt hinzu. Dieser Code ist im SO WIRD'S GEMACHT: Verwenden einer Klasse für Windows-Prozeduren verknüpfen verfügbar.

  6. Deklarieren Sie eine Formularvariable mit dem Namen buttonGF von Typ GradientFilledButton.

                                private GradientFilledButton buttonGF;
    
  7. Fügen Sie folgenden Code hinzu, der als Unterklasse erstellte Button-Steuerelement initialisiert an den Konstruktor der Klasse Form1. Dieser Code sollte den Aufruf der Methode InitializeComponent folgen. Sie können das Start- und Farben die graduelle Füllung und einem TopToBottom oder LeftToRight ausfüllen Richtung beenden 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 die Schaltfläche Click Ereignis die 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 und führen Sie die Anwendung.

Siehe auch

Aufgaben

SO WIRD'S GEMACHT: Verwenden einer Klasse für Windows-Prozeduren verknüpfen

SO WIRD'S GEMACHT: Verwenden einer Hilfsklasse für Platform Invokes

SO WIRD'S GEMACHT: Unterklasse a TreeView by Using Native Callbacks

Konzepte

.NET compact Framework Gewusst-wie-Themen

Weitere Ressourcen

Interoperabilität in .NET Compact Framework