无法在自定义上下文菜单中显示子菜单项

时间:2019-09-23 18:41:36

标签: c# wpf xaml contextmenu

以下代码创建了一个放射状的上下文菜单,该菜单可以很好地显示一级菜单。它通过重写ArrangeOverride方法在特定位置绘制图标来实现此目的,这对于一级菜单项非常有用,创建子菜单项却似乎不起作用。

任何子菜单项默认情况下都具有.IsVisible = false,绘制这些子菜单项不会使它们可见,并且.IsVisible不会更改。我正在设置.IsSubmenuOpen = true,这似乎触发了一些我试图用来绘制子菜单项的事件,但是没有运气。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;

namespace RoundMenu
{
    public class RadialContextMenu: ContextMenu
    {
        public RadialContextMenu()
        {
            var items = GetTopItems();
            foreach (var item in items)
            {
                this.AddChild(item);
            }
        }


        private IList GetTopItems()
        {
            List<RadialMenuItem> list = new List<RadialMenuItem>();

            var item1 = new RadialMenuItem() { Name = "AddItem", Icon = @"pack://application:,,,/RoundMenu;component/Images/Add_16x16.png" };
            var item2 = new RadialMenuItem() { Name = "CancelItem", Icon = @"pack://application:,,,/RoundMenu;component/Images/Cancel_16x16.png" };
            var item3 = new RadialMenuItem() { Name = "RefreshItem", Icon = @"pack://application:,,,/RoundMenu;component/Images/Refresh2_16x16.png" };
            var item4 = new RadialMenuItem() { Name = "ApplyItem", Icon = @"pack://application:,,,/RoundMenu;component/Images/Apply_16x16.png" };
            var item11 = new RadialMenuItem() { Name = "AddItem1", Icon = @"pack://application:,,,/RoundMenu;component/Images/Add_16x16.png" };
            var item12 = new RadialMenuItem() { Name = "AddItem2", Icon = @"pack://application:,,,/RoundMenu;component/Images/Add_16x16.png" };

            item1.Items.Add(item11);
            item1.Items.Add(item12);

            item1.ContextMenuOpening += Item1_ContextMenuOpening;
            list.Add(item1);
            list.Add(item2);
            list.Add(item3);
            list.Add(item4);

            return list;
        }

        private void Item1_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using System.Collections;

namespace RoundMenu
{
    public class RoundPanel : Panel
    {

        protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
        {
            Size infiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
            double width = 83 + (this.InternalChildren.Count / 4 * 50);
            return new Size(width, width);
        }

        private List<int> GetAnglesByCount(int count)
        {
            if (count == 1)
                return new List<int>() { 0, 359 };
            if (count == 2)
                return new List<int>() { 0, 178, 180, 358 };
            if (count == 3)
                return new List<int>() { 0, 88, 90, 178, 180, 358 };
            else
                return new List<int>() { 0, 88, 90, 178, 180, 268, 270, 358 };
        }

        private List<int> GetAngles(int track, int elementCount)
        {
            if (elementCount == 1)
                return GetAnglesByCount(0);
            switch (track)
            {
                case 1:
                    return GetAnglesByCount(2);
                default:
                    {
                        return GetAnglesByCount(elementCount);
                    }
            }
        }

        private int GetMaximumAllowedElements(int track)
        {
            switch (track)
            {
                case 1:
                    return 2;
                default:
                    return 4;
            }
        }

        protected override void OnRender(DrawingContext dc)
        {
            base.OnRender(dc);
        }

        protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
        {
            this.RenderTransform = new RotateTransform() { CenterX = finalSize.Width / 2, CenterY = finalSize.Height / 2 };

            DrawForSingleTrack(finalSize.Width / 2, finalSize.Height / 2);
            return finalSize; // Returns the final Arranged size
        }


        private void DrawForSingleTrack(double midX, double midY, IEnumerable items = null)
        {
            if (items == null)
                items = GetTopChildren();
            DrawChildren(midX, midY, items);
        }

        private IEnumerable GetTopChildren()
        {
            foreach (RadialMenuItem item in InternalChildren)
            {
                if (item.Items.Count > 0)
                {
                    foreach (RadialMenuItem item1 in item.Items)
                    {
                        yield return item1;
                    }
                }
                yield return item;
            }
        }

        private int GetTopItemCount(IEnumerable children)
        {
            var count = 0;
            foreach (RadialMenuItem item in children)
            {
                if (string.IsNullOrEmpty(item.ParentName))
                    count++;
            }
            return count;
        }

        private void DrawChildren(double midX, double midY, IEnumerable children, int hyp = 0)
        {
            int angle = 0;
            int topItemCount = GetTopItemCount(children);
            Dictionary<int, int> angles = new Dictionary<int, int>();
            angles.Add(children.GetHashCode(), angle);
            hyp += 15 * (topItemCount / 4 + 1);
            bool largeArc = topItemCount == 1 ? true : false;
            int angleDisp = 360 / topItemCount;
            foreach (UIElement child in children)
            {
                var menuItem = child as RadialMenuItem;
                if (menuItem.Parent is ContextMenu)
                {
                    ((ContextMenu)menuItem.Parent).Opened += new RoutedEventHandler(RoundPanel_Opened);
                }
                menuItem.PreviewMouseLeftButtonDown += MenuItem_PreviewMouseLeftButtonDown;
                menuItem.SubmenuOpened += MenuItem_SubmenuOpened;

                menuItem.IsLargeArc = largeArc;
                int endAngle = angle + angleDisp;
                double x = midX + Math.Cos(DegToRad(endAngle)) * hyp;
                double y = midY + Math.Sin(DegToRad(endAngle)) * hyp;
                menuItem.X = x;
                menuItem.Y = y;

                angle += angleDisp;
                child.Arrange(new Rect(new Point(0, 0), child.DesiredSize));
                if (angle > 359)
                    angle = 0;
            }
        }

        private void MenuItem_SubmenuOpened(object sender, RoutedEventArgs e)
        {
            var menuItem = (RadialMenuItem)sender;
            if (menuItem.Items.Count > 0)
                ((RadialMenuItem)menuItem.Items[0]).IsSubmenuOpen = true;
            var finalSize = new Rect(new Point(0, 0), this.DesiredSize);
            if (menuItem.Items.Count > 0)
                DrawForSingleTrack(finalSize.Width / 2, finalSize.Height / 2, menuItem.Items);
        }

        private void MenuItem_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {

        }


        void RoundPanel_Opened(object sender, RoutedEventArgs e)
        {
            ContextMenu menu = (ContextMenu)sender;
            menu.VerticalOffset = -(menu.ActualHeight / 2) + 4;
            menu.HorizontalOffset = -(menu.ActualWidth / 2) + 4;
        }

        private double DegToRad(int deg)
        {
            return deg * Math.PI / 180;
        }
    }
}

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace RoundMenu
{
    public class MouseEventArgs : EventArgs
    {
        public bool IsMouseEntered { get; set; }
        public bool IsMouseLeft { get; set; }
        public bool IsMouseDown { get; set; }
    }


    public class RadialMenuItem : MenuItem
    {

        public event EventHandler<MouseEventArgs> MouseEvent;
        protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            ((ContextMenu)this.Parent).IsOpen = false;
            this.RaiseEvent(new RoutedEventArgs(MenuItem.ClickEvent));
        }

        protected override void OnMouseEnter(System.Windows.Input.MouseEventArgs e)
        {
            base.OnMouseEnter(e);
            this.IsMouseOver = true;
            this.InvalidateVisual();
            if (this.MouseEvent != null)
                this.MouseEvent(this, new MouseEventArgs() { IsMouseEntered = true });
        }

        protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e)
        {
            base.OnMouseLeave(e);
            this.IsMouseOver = false;
            this.InvalidateVisual();
            if (this.MouseEvent != null)
                this.MouseEvent(this, new MouseEventArgs() { IsMouseLeft = true });
        }

        public string ParentName { get; set; }
        public bool IsMouseOver { get; set; }
        public int StartAngle { get; set; }
        public int EndAngle { get; set; }

        public double StartX { get; set; }
        public double StartY { get; set; }

        public double X { get; set; }
        public double Y { get; set; }

        public double MidX { get; set; }
        public double MidY { get; set; }

        public double Hyp { get; set; }

        public bool IsLargeArc { get; set; }

        protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            if (Icon != null)
                DrawIcon(this, drawingContext);
        }

        private void DrawIcon(MenuItem menuItem, DrawingContext drawingContext)
        {

            ImageSourceConverter imgConv = new ImageSourceConverter();
            ImageSource imageSource = (ImageSource)imgConv.ConvertFromString(menuItem.Icon.ToString());
            drawingContext.DrawImage(imageSource, new Rect(X, Y, imageSource.Width, imageSource.Height));

            if (menuItem.Items.Count > 0)
                ((RadialMenuItem)menuItem.Items[0]).IsSubmenuOpen = true;

            System.Diagnostics.Debug.WriteLine(menuItem.Name + " " + menuItem.Visibility + " " + menuItem.IsVisible);
        }


        private double DegToRad(int deg)
        {
            return deg * Math.PI / 180;
        }
    }
}

0 个答案:

没有答案