كيفية رسم زر مخصص في شريط عنوان النافذة باستخدام نماذج Windows؟

StackOverflow https://stackoverflow.com/questions/106912

سؤال

كيف يمكنك رسم زر مخصص بجوار أزرار التصغير والتكبير والإغلاق داخل شريط عنوان النموذج؟

أعلم أنك بحاجة إلى استخدام استدعاءات Win32 API وتجاوز إجراء WndProc، لكنني لم أتمكن من اكتشاف حل يعمل بشكل صحيح.

لا أحد يعرف كيفية القيام بذلك؟وبشكل أكثر تحديدًا، هل يعرف أحد طريقة للقيام بذلك تعمل في نظام التشغيل Vista؟

هل كانت مفيدة؟

المحلول

سيعمل ما يلي في نظام التشغيل XP، وليس لدي جهاز يعمل بنظام Vista في متناول يدي لاختباره، ولكن أعتقد أن مشكلاتك تنبع من خطأ غير صحيح بطريقة أو بأخرى.على أية حال، مع رمز التعليق السيئ.

// The state of our little button
ButtonState _buttState = ButtonState.Normal;
Rectangle _buttPosition = new Rectangle();

[DllImport("user32.dll")]
private static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern int GetWindowRect(IntPtr hWnd, 
                                        ref Rectangle lpRect);
[DllImport("user32.dll")]
private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
protected override void WndProc(ref Message m)
{
    int x, y;
    Rectangle windowRect = new Rectangle();
    GetWindowRect(m.HWnd, ref windowRect);

    switch (m.Msg)
    {
        // WM_NCPAINT
        case 0x85:
        // WM_PAINT
        case 0x0A:
            base.WndProc(ref m);

            DrawButton(m.HWnd);

            m.Result = IntPtr.Zero;

            break;

        // WM_ACTIVATE
        case 0x86:
            base.WndProc(ref m);
            DrawButton(m.HWnd);

            break;

        // WM_NCMOUSEMOVE
        case 0xA0:
            // Extract the least significant 16 bits
            x = ((int)m.LParam << 16) >> 16;
            // Extract the most significant 16 bits
            y = (int)m.LParam >> 16;

            x -= windowRect.Left;
            y -= windowRect.Top;

            base.WndProc(ref m);

            if (!_buttPosition.Contains(new Point(x, y)) && 
                _buttState == ButtonState.Pushed)
            {
                _buttState = ButtonState.Normal;
                DrawButton(m.HWnd);
            }

            break;

        // WM_NCLBUTTONDOWN
        case 0xA1:
            // Extract the least significant 16 bits
            x = ((int)m.LParam << 16) >> 16;
            // Extract the most significant 16 bits
            y = (int)m.LParam >> 16;

            x -= windowRect.Left;
            y -= windowRect.Top;

            if (_buttPosition.Contains(new Point(x, y)))
            {
                _buttState = ButtonState.Pushed;
                DrawButton(m.HWnd);
            }
            else
                base.WndProc(ref m);

            break;

        // WM_NCLBUTTONUP
        case 0xA2:
            // Extract the least significant 16 bits
            x = ((int)m.LParam << 16) >> 16;
            // Extract the most significant 16 bits
            y = (int)m.LParam >> 16;

            x -= windowRect.Left;
            y -= windowRect.Top;

            if (_buttPosition.Contains(new Point(x, y)) &&
                _buttState == ButtonState.Pushed)
            {
                _buttState = ButtonState.Normal;
                // [[TODO]]: Fire a click event for your button 
                //           however you want to do it.
                DrawButton(m.HWnd);
            }
            else
                base.WndProc(ref m);

            break;

        // WM_NCHITTEST
        case 0x84:
            // Extract the least significant 16 bits
            x = ((int)m.LParam << 16) >> 16;
            // Extract the most significant 16 bits
            y = (int)m.LParam >> 16;

            x -= windowRect.Left;
            y -= windowRect.Top;

            if (_buttPosition.Contains(new Point(x, y)))
                m.Result = (IntPtr)18; // HTBORDER
            else
                base.WndProc(ref m);

            break;

        default:
            base.WndProc(ref m);
            break;
    }
}

private void DrawButton(IntPtr hwnd)
{
    IntPtr hDC = GetWindowDC(hwnd);
    int x, y;

    using (Graphics g = Graphics.FromHdc(hDC))
    {
        // Work out size and positioning
        int CaptionHeight = Bounds.Height - ClientRectangle.Height;
        Size ButtonSize = SystemInformation.CaptionButtonSize;
        x = Bounds.Width - 4 * ButtonSize.Width;
        y = (CaptionHeight - ButtonSize.Height) / 2;
        _buttPosition.Location = new Point(x, y);

        // Work out color
        Brush color;
        if (_buttState == ButtonState.Pushed)
            color = Brushes.LightGreen;
        else
            color = Brushes.Red;

        // Draw our "button"
        g.FillRectangle(color, x, y, ButtonSize.Width, ButtonSize.Height);
    }

    ReleaseDC(hwnd, hDC);
}

private void Form1_Load(object sender, EventArgs e)
{
    _buttPosition.Size = SystemInformation.CaptionButtonSize;
}

نصائح أخرى

أعلم أنه قد مر وقت طويل منذ آخر إجابة ولكن هذا ساعدني حقًا مؤخرًا وأود تحديث الكود الذي قدمه كريس بتعليقاتي وتعديلاتي.يعمل الإصدار بشكل مثالي على نظامي Win XP وWin 2003.في نظام التشغيل Win 2008، يوجد خطأ صغير لم أتمكن من التعرف عليه، عند تغيير حجم النوافذ.يعمل على نظام التشغيل Vista أيضًا (بدون Aero) ولكن لاحظ أن أزرار شريط العنوان ليست مربعة ويجب أن تأخذ أبعاد الزر ذلك في الاعتبار.

 switch (m.Msg)
            {
                // WM_NCPAINT / WM_PAINT        
                case 0x85:
                case 0x0A:
                    //Call base method
                    base.WndProc(ref m);
                    //we have 3 buttons in the corner of the window. So first's new button left coord is offseted by 4 widths
                    int crt = 4;
                    //navigate trough all titlebar buttons on the form
                    foreach (TitleBarImageButton crtBtn in titleBarButtons.Values)
                    {
                        //Calculate button coordinates
                        p.X = (Bounds.Width - crt * crtBtn.Size.Width);
                        p.Y = (Bounds.Height - ClientRectangle.Height - crtBtn.Size.Height) / 2;
                        //Initialize button and draw
                        crtBtn.Location = p;
                        crtBtn.ButtonState = ImageButtonState.NORMAL;
                        crtBtn.DrawButton(m.HWnd);
                        //increment button left coord location offset
                        crt++;
                    }
                    m.Result = IntPtr.Zero;
                    break;
                // WM_ACTIVATE      
                case 0x86:
                    //Call base method
                    base.WndProc(ref m);
                    //Draw each button
                    foreach (TitleBarImageButton crtBtn in titleBarButtons.Values)
                    {
                        crtBtn.ButtonState = ImageButtonState.NORMAL;
                        crtBtn.DrawButton(m.HWnd);
                    }
                    break;
                // WM_NCMOUSEMOVE        
                case 0xA0:
                    //Get current mouse position
                    p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits            
                    p.Y = (int)m.LParam >> 16;        // Extract the most significant 16 bits          
                    p.X -= windowRect.Left;
                    p.Y -= windowRect.Top;

                    //Call base method
                    base.WndProc(ref m);

                    ImageButtonState newButtonState;
                    foreach (TitleBarImageButton crtBtn in titleBarButtons.Values)
                    {
                        if (crtBtn.HitTest(p))
                        {//mouse is over the current button
                            if (crtBtn.MouseButtonState == MouseButtonState.PRESSED)
                                //button is pressed - set pressed state
                                newButtonState = ImageButtonState.PRESSED;
                            else
                                //button not pressed - set hoover state
                                newButtonState = ImageButtonState.HOOVER;
                        }
                        else
                        {
                            //mouse not over the current button - set normal state
                            newButtonState = ImageButtonState.NORMAL;
                        }

                        //if button state not modified, do not repaint it.
                        if (newButtonState != crtBtn.ButtonState)
                        {
                            crtBtn.ButtonState = newButtonState;
                            crtBtn.DrawButton(m.HWnd);
                        }
                    }
                    break;
                // WM_NCLBUTTONDOWN     
                case 0xA1:
                    //Get current mouse position
                    p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits
                    p.Y = (int)m.LParam >> 16;        // Extract the most significant 16 bits      
                    p.X -= windowRect.Left;
                    p.Y -= windowRect.Top;

                    //Call base method
                    base.WndProc(ref m);

                    foreach (TitleBarImageButton crtBtn in titleBarButtons.Values)
                    {
                        if (crtBtn.HitTest(p))
                        {
                            crtBtn.MouseButtonState = MouseButtonState.PRESSED;
                            crtBtn.ButtonState = ImageButtonState.PRESSED;
                            crtBtn.DrawButton(m.HWnd);
                        }
                    }
                    break;
                // WM_NCLBUTTONUP   
                case 0xA2:
                case 0x202:
                    //Get current mouse position
                    p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits   
                    p.Y = (int)m.LParam >> 16;        // Extract the most significant 16 bits 
                    p.X -= windowRect.Left;
                    p.Y -= windowRect.Top;

                    //Call base method
                    base.WndProc(ref m);
                    foreach (TitleBarImageButton crtBtn in titleBarButtons.Values)
                    {
                        //if button is press
                        if (crtBtn.ButtonState == ImageButtonState.PRESSED)
                        {
                            //Rasie button's click event
                            crtBtn.OnClick(EventArgs.Empty);

                            if (crtBtn.HitTest(p))
                                crtBtn.ButtonState = ImageButtonState.HOOVER;
                            else
                                crtBtn.ButtonState = ImageButtonState.NORMAL;
                        }

                        crtBtn.MouseButtonState = MouseButtonState.NOTPESSED;
                        crtBtn.DrawButton(m.HWnd);
                    }
                    break;
                // WM_NCHITTEST    
                case 0x84:
                    //Get current mouse position
                    p.X = ((int)m.LParam << 16) >> 16;// Extract the least significant 16 bits
                    p.Y = (int)m.LParam >> 16;        // Extract the most significant 16 bits
                    p.X -= windowRect.Left;
                    p.Y -= windowRect.Top;

                    bool isAnyButtonHit = false;
                    foreach (TitleBarImageButton crtBtn in titleBarButtons.Values)
                    {
                        //if mouse is over the button, or mouse is pressed 
                        //(do not process messages when mouse was pressed on a button)
                        if (crtBtn.HitTest(p) || crtBtn.MouseButtonState == MouseButtonState.PRESSED)
                        {
                            //return 18 (do not process further)
                            m.Result = (IntPtr)18;
                            //we have a hit
                            isAnyButtonHit = true;
                            //return 
                            break;
                        }
                        else
                        {//mouse is not pressed and not over the button, redraw button if needed  
                            if (crtBtn.ButtonState != ImageButtonState.NORMAL)
                            {
                                crtBtn.ButtonState = ImageButtonState.NORMAL;
                                crtBtn.DrawButton(m.HWnd);
                            }
                        }
                    }
                    //if we have a hit, do not process further
                    if (!isAnyButtonHit)
                        //Call base method
                        base.WndProc(ref m);
                    break;
                default:
                    //Call base method
                    base.WndProc(ref m);
                    //Console.WriteLine(m.Msg + "(0x" + m.Msg.ToString("x") + ")");
                    break;
            }

يوضح الكود الرسائل التي يجب معالجتها وكيفية التعامل معها.يستخدم الكود مجموعة من كائنات TitleBarButton المخصصة.هذا الفصل أكبر من أن يتم تضمينه هنا ولكن يمكنني تقديمه إذا لزم الأمر مع مثال.

يبدو أن الرسم هو الجزء السهل، وسيقوم بذلك ما يلي:

[يحرر:تمت إزالة الرمز، راجع إجابتي الأخرى]

المشكلة الحقيقية هي تغيير الحالة وكشف النقرات على الزر...لذلك ستحتاج إلى الارتباط بمعالج الرسائل العام للبرنامج، ويبدو أن .NET يخفي أحداث الماوس لنموذج بينما لا يكون في مناطق الحاوية الفعلية (على سبيل المثال.يتحرك الماوس وينقر على شريط العنوان).أنا أبحث عن معلومات حول ذلك، وقد وجدتها الآن، وأنا أعمل عليها، ولا ينبغي أن يكون ذلك صعبًا للغاية...إذا تمكنا من معرفة ما هي هذه الرسائل التي تمر بالفعل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top