Canvas上批量创建可视对象(DrawingVisual)管理,获取鼠标悬浮图形状态,并控制鼠标右键快捷菜单等...

近期公司有个新的定制,先简要说明下:

窗口上有个播放区域,区域上悬浮了很多可视对象(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>
UI前段代码
  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 }
UI后端代码
  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 }
CustomCanvas 扩展类

有需要的朋友,也可以移步下载:点击下载

原文地址:https://www.cnblogs.com/xuling-297769461/p/12523103.html