需要重写哪个方法来圆角NotiIcon的ContextMenuStrip?

问题描述 投票:0回答:3

在Windows 11中,我发现几乎每个系统托盘图标的上下文菜单都有一个圆角。 由于 WPF 应用程序没有 Systray 菜单,因此我使用了 Window 的表单 NotiIcon 和 ContextMenuStrip。 我已经使用 ProfessionalColorTable 完全自定义了 ToolStripProfessionalRenderer 以支持暗模式 根据客户的要求。现在我正在寻找拐角处。我使用了“RoundedEdges = true;” 但没有起作用。我需要确切地知道需要重写哪个方法才能绕过 ContextMenuStrip 的角。

enter image description here

下面是我的渲染器示例代码。

   ///Menu Renderer
public class MenuRenderer : ToolStripProfessionalRenderer
{
    //Fields
    private Color primaryColor;
    private Color textColor;
    private int arrowThickness;
    private WindowsTheme systrayTheme;

    [Browsable(false)]
    public WindowsTheme SystrayTheme
    {
        get { return systrayTheme; }
        set { systrayTheme = value; }
    }

    //Constructor
    public MenuRenderer(bool isMainMenu, Color primaryColor, Color textColor, Color menuItemMouseOverColor, Color menuItemMouseOverBorderColor, WindowsTheme theme)
        : base(new MenuColorTable(isMainMenu, primaryColor, menuItemMouseOverColor, menuItemMouseOverBorderColor, theme))
    {
        RoundedEdges = true;
        
        this.primaryColor = primaryColor;
        this.systrayTheme = theme;

        if (isMainMenu)
        {
            arrowThickness = 2;
            if (textColor == Color.Empty) //Set Default Color
                this.textColor = Color.Gainsboro;
            else//Set custom text color 
                this.textColor = textColor;
        }
        else
        {
            arrowThickness = 1;
            if (textColor == Color.Empty) //Set Default Color
                this.textColor = Color.DimGray;
            else//Set custom text color
                this.textColor = textColor;
        }
    }

    //Overrides
    protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
    {
        base.OnRenderItemText(e);
        e.Item.ForeColor = e.Item.Selected ? Color.White : textColor;
    }

    protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
    {
        //Fields
        var graph = e.Graphics;
        var arrowSize = new Size(5, 10);
        var arrowColor = e.Item.Selected ? Color.White : primaryColor;
        var rect = new Rectangle(e.ArrowRectangle.Location.X, (e.ArrowRectangle.Height - arrowSize.Height) / 2,
            arrowSize.Width, arrowSize.Height);
        using (GraphicsPath path = new GraphicsPath())
        using (Pen pen = new Pen(arrowColor, arrowThickness))
        {
            //Drawing
            graph.SmoothingMode = SmoothingMode.AntiAlias;
            path.AddLine(rect.Left, rect.Top, rect.Right, rect.Top + rect.Height / 2);
            path.AddLine(rect.Right, rect.Top + rect.Height / 2, rect.Left, rect.Top + rect.Height);
            graph.DrawPath(pen, path);
        }
    }

    protected override void OnRenderGrip(ToolStripGripRenderEventArgs e)
    {
        Rectangle rectangle = new Rectangle(e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width, e.AffectedBounds.Height);
        GraphicsPath graphicsPath = RoundedRect(rectangle, 8);

        using (Pen pen = new Pen(Color.Green,5))
        {
            e.Graphics.DrawPath(pen, graphicsPath);
            //e.Graphics.FillPath(pen.Brush, graphicsPath);
        }
    }

    protected override void OnRenderToolStripPanelBackground(ToolStripPanelRenderEventArgs e)
    {
        //base.OnRenderToolStripPanelBackground(e);
        Rectangle rectangle = new Rectangle(e.ToolStripPanel.Location.X, e.ToolStripPanel.Location.Y, 
            e.ToolStripPanel.ClientRectangle.Width, e.ToolStripPanel.ClientRectangle.Height);
        GraphicsPath graphicsPath = RoundedRect(rectangle, 8);

        using (Pen pen = new Pen(Color.Green, 5))
        {
            //e.Graphics.DrawPath(pen, graphicsPath);
            e.Graphics.FillPath(pen.Brush, graphicsPath);
        }
    }

    protected override void OnRenderStatusStripSizingGrip(ToolStripRenderEventArgs e)
    {
        //base.OnRenderStatusStripSizingGrip(e);
        Rectangle rectangle = new Rectangle(e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width, e.AffectedBounds.Height);
        GraphicsPath graphicsPath = RoundedRect(rectangle, 8);

        using (Pen pen = new Pen(Color.Green,5))
        {
            e.Graphics.DrawPath(pen, graphicsPath);
            //e.Graphics.FillPath(pen.Brush, graphicsPath);
        }
    }

    protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
    {
        //Fields
        //var graph = e.Graphics;
        //var borderZise = new Size(e.AffectedBounds.Width + 10, e.AffectedBounds.Height + 10);
        //var borderColor = Color.DeepPink;

        //var rect = new Rectangle(e.AffectedBounds.X, (e.AffectedBounds.Height - borderZise.Height) / 2,
        //    borderZise.Width, borderZise.Height);
        //using (GraphicsPath path = new GraphicsPath())
        //using (Pen pen = new Pen(borderColor, arrowThickness))
        //{
        //    //Drawing
        //    graph.SmoothingMode = SmoothingMode.AntiAlias;
        //    path.AddLine(rect.Left, rect.Top, rect.Right, rect.Top + rect.Height / 2);
        //    path.AddLine(rect.Right, rect.Top + rect.Height / 2, rect.Left, rect.Top + rect.Height);
        //    graph.DrawPath(pen, path);
        //}
        //DrawRoundedRectangle(e.Graphics, e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width - 5, e.AffectedBounds.Height, 15, Color.Red);

        //Rectangle rectangle = new Rectangle(e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width, e.AffectedBounds.Height);
        //GraphicsPath graphicsPath = RoundedRect(rectangle, 8);

        //using (Pen pen = new Pen(Color.Green, 2))
        //{
        //    //e.Graphics.DrawPath(pen, graphicsPath);
        //    e.Graphics.FillPath(pen.Brush, graphicsPath);
        //}
    }
    
    public GraphicsPath RoundedRect(Rectangle bounds, int radius)
    {
        int diameter = radius * 2;
        Size size = new Size(diameter, diameter);
        Rectangle arc = new Rectangle(bounds.Location, size);
        GraphicsPath path = new GraphicsPath();

        if (radius == 0)
        {
            path.AddRectangle(bounds);
            return path;
        }

        // top left arc  
        path.AddArc(arc, 180, 90);

        // top right arc  
        arc.X = bounds.Right - diameter;
        path.AddArc(arc, 270, 90);

        // bottom right arc  
        arc.Y = bounds.Bottom - diameter;
        path.AddArc(arc, 0, 90);

        // bottom left arc 
        arc.X = bounds.Left;
        path.AddArc(arc, 90, 90);

        path.CloseFigure();
        return path;
    }
}

////////Custom Menu     
public class CustomContextMenu : ContextMenuStrip
{
    //Fields
    private bool isMainMenu;
    private int menuItemHeight = 20;
    private int menuItemWidth = 20;
    private Color menuItemTextColor = Color.Empty;
    private Color primaryColor = Color.Empty;
    private Color MouseOverColor = Color.Empty;
    private Color MouseOverBorderColor = Color.Empty;
    private WindowsTheme systrayTheme = WindowsTheme.Light;
    private Bitmap menuItemHeaderSize;

    //Constructor
    public CustomContextMenu() 
    {
        
    }

    //Properties
    [Browsable(false)]
    public bool IsMainMenu
    {
        get { return isMainMenu; }
        set { isMainMenu = value; }
    }

    [Browsable(false)]
    public int MenuItemHeight
    {
        get { return menuItemHeight; }
        set { menuItemHeight = value; }
    }
    
    [Browsable(false)]
    public int MenuItemWidth
    {
        get { return menuItemWidth; }
        set { menuItemWidth = value; }
    }

    [Browsable(false)]
    public Color MenuItemTextColor
    {
        get { return menuItemTextColor; }
        set { menuItemTextColor = value; }
    }

    [Browsable(false)]
    public Color PrimaryColor
    {
        get { return primaryColor; }
        set { primaryColor = value; }
    }

    [Browsable(false)]
    public Color MenuItemMouseOverColor
    {
        get { return MouseOverColor; }
        set { MouseOverColor = value; }
    }
    
    [Browsable(false)]
    public Color MenuItemMouseOverBorderColor
    {
        get { return MouseOverBorderColor; }
        set { MouseOverBorderColor = value; }
    }

    [Browsable(false)]
    public WindowsTheme SystrayTheme
    { 
        get { return systrayTheme; }
        set { systrayTheme = value; }
    }

    //Private methods
    private void LoadMenuItemHeight()
    {
        if (isMainMenu)
            menuItemHeaderSize = new Bitmap(menuItemWidth, menuItemHeight);
        else menuItemHeaderSize = new Bitmap(menuItemWidth-5, menuItemHeight);

        foreach (Forms.ToolStripMenuItem menuItemL1 in this.Items)
        {
            menuItemL1.ImageScaling = ToolStripItemImageScaling.None;
            if (menuItemL1.Image == null) menuItemL1.Image = menuItemHeaderSize;

            foreach (Forms.ToolStripMenuItem menuItemL2 in menuItemL1.DropDownItems)
            {
                menuItemL2.ImageScaling = ToolStripItemImageScaling.None;
                if (menuItemL2.Image == null) menuItemL2.Image = menuItemHeaderSize;

                foreach (Forms.ToolStripMenuItem menuItemL3 in menuItemL2.DropDownItems)
                {
                    menuItemL3.ImageScaling = ToolStripItemImageScaling.None;
                    if (menuItemL3.Image == null) menuItemL3.Image = menuItemHeaderSize;

                    foreach (Forms.ToolStripMenuItem menuItemL4 in menuItemL3.DropDownItems)
                    {
                        menuItemL4.ImageScaling = ToolStripItemImageScaling.None;
                        if (menuItemL4.Image == null) menuItemL4.Image = menuItemHeaderSize;
                        ///Level 5++
                    }
                }
            }
        }
    }

    //Overrides
    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        if (this.DesignMode == false)
        {
            switch (SystrayTheme)
            {
                case WindowsTheme.Light:
                    {
                        menuItemTextColor = Color.Black;
                    }
                    break;
                case WindowsTheme.Dark:
                    {
                        menuItemTextColor = Color.White;
                    }
                    break;
                case WindowsTheme.HighContrast:
                    {
                        menuItemTextColor = Utility.ToDrawingColor(System.Windows.SystemColors.MenuTextColor);
                    }
                    break;
            }

            this.Renderer = new MenuRenderer(isMainMenu, primaryColor, menuItemTextColor, MouseOverColor, MouseOverBorderColor, SystrayTheme);
            LoadMenuItemHeight();
        }
    }
 }

更新:

最近我尝试重写 OnPaint() 方法。但圆角不光滑,子菜单也不是圆角的。

enter image description here

    private GraphicsPath GetGraphicsPath(RectangleF rect,float radious)
    {
        GraphicsPath path = new GraphicsPath();
        path.StartFigure();
        path.AddArc(rect.X, rect.Y, radious, radious, 180, 90);
        path.AddArc(rect.Width - radious, rect.Y, radious, radious, 270, 90);
        path.AddArc(rect.Width - radious, rect.Height - radious, radious, radious, 0, 90);
        path.AddArc(rect.X, rect.Height - radious, radious, radious, 90, 90);

        path.CloseFigure();

        return path;
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

        RectangleF rectSurface = new RectangleF(0, 0, this.Width, this.Height);
        RectangleF rectBorder = new RectangleF(1, 1, this.Width - 0.8F, this.Height - 1);

        if (borderRadius > 2) //Rounder
        {
            using (GraphicsPath pathSurface = GetGraphicsPath(rectSurface, borderRadius))
            using (GraphicsPath pathBorder = GetGraphicsPath(rectBorder, borderRadius - 1F))
            using (Pen penSurface = new Pen(this.BackColor, 2))
            using (Pen penBorder = new Pen(borderColor, borderSize))
            {
                penBorder.Alignment = PenAlignment.Inset;
                //Menu surface
                this.Region = new Region(pathSurface);
                //Draw surface border for HD result
                e.Graphics.DrawPath(penSurface, pathSurface);

                //Menu Border
                if (borderSize >= 1)
                    e.Graphics.DrawPath(penBorder, pathBorder);
            }
        }
        else //Normal contex menu
        {
            //Menu surface
            this.Region = new Region(rectSurface);
            //Menu border
            if (borderSize >= 1)
            {
                using (Pen penBorder = new Pen(borderColor, borderSize))
                {
                    penBorder.Alignment = PenAlignment.Inset;
                    e.Graphics.DrawRectangle(penBorder, 0, 0, this.Width - 1, this.Height - 1);
                }
            }
        }

    }
            
    //Overrides
    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        

        if (this.DesignMode == false)
        {
            //this.Parent.BackColorChanged += Parent_BackColorChanged;

            switch (SystrayTheme)
            {
                case WindowsTheme.Light:
                    {
                        menuItemTextColor = Color.Black;
                    }
                    break;
                case WindowsTheme.Dark:
                    {
                        menuItemTextColor = Color.White;
                    }
                    break;
                case WindowsTheme.HighContrast:
                    {
                        menuItemTextColor = Utility.ToDrawingColor(System.Windows.SystemColors.MenuTextColor);
                    }
                    break;
            }
            this.RenderMode = ToolStripRenderMode.Professional; 
            this.Renderer = new MenuRenderer(isMainMenu, primaryColor, menuItemTextColor, MouseOverColor, MouseOverBorderColor, SystrayTheme);
            LoadMenuItemHeight();
        }
    }

    private void Parent_BackColorChanged(object sender, EventArgs e)
    {
        if (this.DesignMode)
            this.Invalidate();
    }

提前致谢。任何帮助将不胜感激。

c# wpf contextmenustrip
3个回答
1
投票

您可以在此页面找到解决方案:https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-rounded-corners

我还在此处提供了解决方案,以防外部链接被删除。

// The enum flag for DwmSetWindowAttribute's second parameter, which tells the function what attribute to set.
public enum DWMWINDOWATTRIBUTE
{
    DWMWA_WINDOW_CORNER_PREFERENCE = 33
}

// The DWM_WINDOW_CORNER_PREFERENCE enum for DwmSetWindowAttribute's third parameter, which tells the function
// what value of the enum to set.
public enum DWM_WINDOW_CORNER_PREFERENCE
{
   DWMWCP_DEFAULT      = 0,
   DWMWCP_DONOTROUND   = 1,
   DWMWCP_ROUND        = 2,
   DWMWCP_ROUNDSMALL   = 3
}

// Import dwmapi.dll and define DwmSetWindowAttribute in C# corresponding to the native function.
[DllImport("dwmapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern long DwmSetWindowAttribute(IntPtr hwnd,
                                                 DWMWINDOWATTRIBUTE attribute,
                                                 ref DWM_WINDOW_CORNER_PREFERENCE pvAttribute,
                                                 uint cbAttribute);

并将圆角应用到 ContextMenuStrip:

ContextMenuStrip notifyIconMenu = new ContextMenuStrip();
...
var attribute = DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE;
var preference = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND;
DwmSetWindowAttribute(notifyIconMenu.Handle, attribute, ref preference, sizeof(uint));

UPD 下面是子菜单的实现:

ToolStripMenuItem submenu = new ToolStripMenuItem("submenu");
notifyIconMenu.Items.Add(submenu);

ToolStripMenuItem submenuItem1 = new ToolStripMenuItem("submenu1");
submenu.DropDownItems.Add(submenuItem1);
DwmSetWindowAttribute(submenu.DropDown.Handle, attribute, ref preference, sizeof(uint));

如果您想采用圆角的专有实施路线,这里有一种创建“平滑”圆角的方法:

Graphics graphics = ...;
...
graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.SmoothingMode = SmoothingMode.AntiAlias;

0
投票

您是否尝试在

RendererMode
中设置除默认值之外的不同
CustomContextMenu

protected override void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);
    if (this.DesignMode == false)
    {
        switch (SystrayTheme)
        {
            case WindowsTheme.Light:
            {
                menuItemTextColor = Color.Black;
            }
            break;
            case WindowsTheme.Dark:
            {
                menuItemTextColor = Color.White;
            }
            break;
            case WindowsTheme.HighContrast:
            {
                menuItemTextColor = Utility.ToDrawingColor(System.Windows.SystemColors.MenuTextColor);
            }
            break;
        }
        this.RenderMode = ToolStripRenderMode.Professional;
        this.Renderer = new MenuRenderer(isMainMenu, primaryColor, menuItemTextColor, MouseOverColor, MouseOverBorderColor, SystrayTheme);
        LoadMenuItemHeight();
    }
}

0
投票

谢谢

anemomylos
。我制作了扩展函数以将RoundCorners应用于contextmenustripForm

ss1

如何使用

formload(){

   //---ctx MenuStrip   
   var myCtxMenuStrip = new ContextMenuStrip();
   myCtxMenuStrip.ApplyRoundCorners();

    //--- form Round
   var frmRound = new Form() { Text = " round form " };
   frmRound.ApplyRoundCorners();
   frmRound.Show();

   //--- form sharp Edge
   var frmSharp = new Form() { Text= "form Sharp Edged" };
   frmSharp.ApplyRoundCorners(false);
   frmSharp.Show();

}

将此类粘贴到您的项目中

DWM_RoundedCorners.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;


public static class DWM_RoundedCorners
{
    static DWM_RoundedCorners() { }

    /// <summary>
    /// uses dllimport DwmSetWindowAttribute
    /// </summary>
    public static void ApplyRoundCorners(this Control control , bool Round =true)
    {
        //ContextMenuStrip notifyIconMenu = new ContextMenuStrip();
        var attribute = DWM_windowAttribute.DWMWA_WINDOW_CORNER_PREFERENCE;
        var preference = DWM_windowCornerPreference.DWMWCP_ROUND;

        if (!Round)
            preference = DWM_windowCornerPreference.DWMWCP_DONOTROUND;

        DwmSetWindowAttribute(control.Handle, attribute, ref preference, sizeof(uint));
    }

    /// <summary>
    /// apply to  RoundedCorners to ContextMenuStrip and its 1st level Dropdown.
    /// </summary>
    public static void ApplyRoundCorners_toContextMenuStrip(ContextMenuStrip ctxMenuStrip)
    {
        //var submenu = new ToolStripMenuItem("submenu");
        //ctxMenuStrip.Items.Add(submenu);
        //var submenuItem1 = new ToolStripMenuItem("submenu1");
        //submenu.DropDownItems.Add(submenuItem1);

        ApplyRoundCorners(ctxMenuStrip);
        //ApplyRoundCorners(submenu.DropDown);

        foreach (var item in ctxMenuStrip.Items)
        {
            if ((item as ToolStripMenuItem) == null)
                continue;

            var tsmi = (ToolStripMenuItem)item;
            if (tsmi.DropDown.Items.Count <= 0)
                continue;

            ApplyRoundCorners(tsmi.DropDown);


        }

    }


    #region dllimport and enums

    // The enum flag for DwmSetWindowAttribute's second parameter, which tells the function what attribute to set.
    public enum DWM_windowAttribute
    {
        DWMWA_WINDOW_CORNER_PREFERENCE = 33
    }

    // The DWM_WINDOW_CORNER_PREFERENCE enum for DwmSetWindowAttribute's third parameter, which tells the function
    // what value of the enum to set.
    public enum DWM_windowCornerPreference
    {
        DWMWCP_DEFAULT = 0,
        DWMWCP_DONOTROUND = 1,
        DWMWCP_ROUND = 2,
        DWMWCP_ROUNDSMALL = 3
    }

    [DllImport("dwmapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern long DwmSetWindowAttribute(
        IntPtr hwnd,
        DWM_windowAttribute attribute,
        ref DWM_windowCornerPreference pvAttribute,
        uint cbAttribute);

    #endregion


}
© www.soinside.com 2019 - 2024. All rights reserved.