以下代码创建了一个放射状的上下文菜单,该菜单可以很好地显示一级菜单。它通过重写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;
}
}
}