近期公司有个新的定制,先简要说明下:
窗口上有个播放区域,区域上悬浮了很多可视对象(DrawingVisual),全部是动态生成的....
现在的需求是在这些矩形框上需要添加右键快捷菜单...
需求知道了,懂wpf的都知道,DrawingVisual是极其简约的一个视图对象,是没有属性可以绑定鼠标右键菜单,所以我的思路是,在Canvas上绑定快捷菜单,通过鼠标位置判断当前是否在矩形框里面,如果是,则显示对于的菜单,否则就隐藏起来
好了,需求和解决方案整理完成,那么就开始吧!
先看下整体效果:
1 <Window x:Class="DrawingHelper.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:DrawingHelper" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="450" Width="800"> 9 10 <Window.Resources> 11 <ResourceDictionary> 12 13 <ContextMenu x:Key="right"> 14 <MenuItem Header="默认的" /> 15 <MenuItem Header="单击框" Style="{DynamicResource item}" Click="MenuItem_Click" /> 16 </ContextMenu> 17 18 <Style TargetType="MenuItem" x:Key="item"> 19 <Setter Property="Visibility" Value="Collapsed" /> 20 <Style.Triggers> 21 <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:MainWindow},Path=IsOverRect}" Value="true"> 22 <Setter Property="Visibility" Value="Visible" /> 23 </DataTrigger> 24 </Style.Triggers> 25 </Style> 26 27 </ResourceDictionary> 28 </Window.Resources> 29 <Grid> 30 <Grid x:Name="grid" Margin="10"> 31 <local:CustomCanvas x:Name="canvas" 32 Background="#7d7d7d" 33 ContextMenu="{StaticResource right}" 34 MouseLeftButtonDown="canvas_MouseLeftButtonDown" 35 MouseMove="canvas_MouseMove" 36 MouseLeftButtonUp="canvas_MouseLeftButtonUp"/> 37 </Grid> 38 39 <StackPanel Orientation="Horizontal" VerticalAlignment="Top"> 40 <Button Content="生成" Click="Button_Click" /> 41 </StackPanel> 42 </Grid> 43 </Window>
1 using System; 2 using System.Collections.Generic; 3 using System.Diagnostics; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 using System.Windows; 8 using System.Windows.Controls; 9 using System.Windows.Data; 10 using System.Windows.Documents; 11 using System.Windows.Input; 12 using System.Windows.Media; 13 using System.Windows.Media.Imaging; 14 using System.Windows.Navigation; 15 using System.Windows.Shapes; 16 17 namespace DrawingHelper 18 { 19 /// <summary> 20 /// MainWindow.xaml 的交互逻辑 21 /// </summary> 22 public partial class MainWindow : Window 23 { 24 List<Visual> vsOver = new List<Visual>(); 25 bool ismove = false; 26 Visual slectedVisual; 27 Vector vectorDownOffice; 28 29 public MainWindow() 30 { 31 InitializeComponent(); 32 33 //注册事件 34 //EventManager.RegisterClassHandler(typeof(CustomCanvas), 35 // CustomCanvas.RightContextMenuOpeningEvent, 36 // new RoutedEventHandler(RightContextMenuOpening), true); 37 38 canvas.RightContextMenuOpening += RightContextMenuOpening; 39 } 40 41 /// <summary> 42 /// 是否悬浮在框上 43 /// </summary> 44 public bool IsOverRect 45 { 46 get { return (bool)GetValue(IsOverRectProperty); } 47 set { SetValue(IsOverRectProperty, value); } 48 } 49 50 public static readonly DependencyProperty IsOverRectProperty = 51 DependencyProperty.Register("IsOverRect", typeof(bool), typeof(MainWindow), new PropertyMetadata(false)); 52 53 /// <summary> 54 /// 单击生成 55 /// </summary> 56 /// <param name="sender"></param> 57 /// <param name="e"></param> 58 private void Button_Click(object sender, RoutedEventArgs e) 59 { 60 Rect rect = new Rect(); 61 rect.Size = new Size(100, 100); 62 63 Random r = new Random(DateTime.Now.Millisecond); 64 var x = r.Next(0, (int)(canvas.ActualWidth - rect.Size.Width)); 65 var y = r.Next(0, (int)(canvas.ActualHeight - rect.Size.Width)); 66 67 rect.Location = new Point(x, y); 68 69 canvas.AddVisual(rect); 70 } 71 72 /// <summary> 73 /// 单击鼠标悬浮的框时 74 /// </summary> 75 /// <param name="sender"></param> 76 /// <param name="e"></param> 77 private void MenuItem_Click(object sender, RoutedEventArgs e) 78 { 79 MessageBox.Show($"当前选中:{vsOver.Count}"); 80 } 81 82 /// <summary> 83 /// 左键被按下时 84 /// </summary> 85 /// <param name="sender"></param> 86 /// <param name="e"></param> 87 private void canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 88 { 89 ismove = true; 90 var point = e.GetPosition(canvas); 91 92 var vs = canvas.GetVisualByPoint(point); 93 slectedVisual = vs.FirstOrDefault(); 94 95 if (slectedVisual is DrawingVisual drawing) 96 { 97 vectorDownOffice = point - drawing.Drawing.Bounds.Location; 98 } 99 } 100 101 /// <summary> 102 /// 移动时 103 /// </summary> 104 /// <param name="sender"></param> 105 /// <param name="e"></param> 106 private void canvas_MouseMove(object sender, MouseEventArgs e) 107 { 108 if (ismove && slectedVisual != null) 109 { 110 var point = e.GetPosition(canvas) - vectorDownOffice; 111 112 Rect rect = new Rect(); 113 rect.Size = new Size(100, 100); 114 rect.X = point.X; 115 rect.Y = point.Y; 116 117 canvas.MoveVisual(slectedVisual, rect); 118 } 119 } 120 121 /// <summary> 122 /// 左键被抬起时 123 /// </summary> 124 /// <param name="sender"></param> 125 /// <param name="e"></param> 126 private void canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 127 { 128 ismove = false; 129 slectedVisual = null; 130 } 131 132 /// <summary> 133 /// 快捷菜单打开之前 134 /// </summary> 135 /// <param name="sender"></param> 136 /// <param name="e"></param> 137 void RightContextMenuOpening(object sender, RoutedEventArgs e) 138 { 139 //获取相对面板的位置 140 var point = Mouse.GetPosition(canvas); 141 142 var vs = canvas.GetVisualByPoint(point); 143 vsOver = new List<Visual>(vs); 144 145 IsOverRect = vs.Length > 0; 146 } 147 } 148 149 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Windows; 7 using System.Windows.Controls; 8 using System.Windows.Media; 9 10 namespace DrawingHelper 11 { 12 /// <summary> 13 /// 自定义面板 14 /// </summary> 15 class CustomCanvas : Canvas 16 { 17 /// <summary> 18 /// 透明度 19 /// </summary> 20 double opacity = 0.8; 21 22 public CustomCanvas() 23 { 24 ContextMenuOpening += CustomCanvas_ContextMenuOpening; 25 } 26 27 /// <summary> 28 /// 菜单打开之前 29 /// </summary> 30 /// <param name="sender"></param> 31 /// <param name="e"></param> 32 private void CustomCanvas_ContextMenuOpening(object sender, ContextMenuEventArgs e) 33 { 34 //RaiseEvent(new RoutedEventArgs(RightContextMenuOpeningEvent, e));//事件推送 35 36 RightContextMenuOpening?.Invoke(this, e); 37 } 38 39 /* 40 /// <summary> 41 /// 声明自定义事件 42 /// </summary> 43 public static readonly RoutedEvent RightContextMenuOpeningEvent = 44 EventManager.RegisterRoutedEvent( 45 "RightContextMenuOpeningEvent", 46 RoutingStrategy.Direct, 47 typeof(EventHandler<ContextMenuEventArgs>), 48 typeof(CustomCanvas)); 49 50 /// <summary> 51 /// Raised when the VideoCellContextMenuOpeningEvent changed. 52 /// </summary> 53 public event RoutedEventHandler RightContextMenuOpening 54 { 55 add { AddHandler(RightContextMenuOpeningEvent, value); } 56 remove { RemoveHandler(RightContextMenuOpeningEvent, value); } 57 } 58 */ 59 60 /// <summary> 61 /// 右键菜单打开之前 62 /// </summary> 63 public event EventHandler<ContextMenuEventArgs> RightContextMenuOpening; 64 65 /// <summary> 66 /// 当前所有的图形 67 /// </summary> 68 List<Visual> vs = new List<Visual>(); 69 70 /// <summary> 71 /// 边框画刷 72 /// </summary> 73 Pen pen = new Pen(new SolidColorBrush(Colors.Black), 2); 74 75 /// <summary> 76 /// 添加个图形 77 /// </summary> 78 /// <param name="rect"></param> 79 public void AddVisual(Rect rect) 80 { 81 DrawingVisual dv = new DrawingVisual(); 82 83 using (var drawingContext = dv.RenderOpen()) 84 { 85 drawingContext.PushOpacity(opacity); 86 drawingContext.DrawRectangle(null, pen, rect); 87 } 88 89 vs.Add(dv); 90 91 this.AddVisualChild(dv); 92 this.AddLogicalChild(dv); 93 } 94 95 /// <summary> 96 /// 图形总数 97 /// </summary> 98 protected override int VisualChildrenCount => vs.Count; 99 100 /// <summary> 101 /// 102 /// </summary> 103 /// <param name="index"></param> 104 /// <returns></returns> 105 protected override Visual GetVisualChild(int index) 106 { 107 return vs[index]; 108 } 109 110 /// <summary> 111 /// 根据坐标返回图形 112 /// </summary> 113 /// <param name="point"></param> 114 /// <returns></returns> 115 public Visual[] GetVisualByPoint(Point point) 116 { 117 List<Visual> vis = new List<Visual>(); 118 vs.ForEach(c => 119 { 120 if (c is DrawingVisual dv) 121 { 122 var dr = dv.Drawing; 123 124 var x = dr.Bounds.X; 125 var y = dr.Bounds.Y; 126 var w = dr.Bounds.Width; 127 var h = dr.Bounds.Height; 128 129 if (point.X >= x && point.X <= x + w && point.Y >= y && point.Y <= y + h) 130 { 131 vis.Add(c); 132 } 133 } 134 }); 135 return vis.ToArray(); 136 } 137 138 /// <summary> 139 /// 移动指定的 140 /// </summary> 141 /// <param name="visual"></param> 142 /// <param name="point"></param> 143 public void MoveVisual(Visual visual, Rect rect) 144 { 145 if (visual is DrawingVisual drawing) 146 { 147 using (var dc = drawing.RenderOpen()) 148 { 149 dc.PushOpacity(opacity); 150 dc.DrawRectangle(null, pen, rect); 151 } 152 } 153 } 154 } 155 }
有需要的朋友,也可以移步下载:点击下载