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 扩展类
有需要的朋友,也可以移步下载:点击下载
Canvas上批量创建可视对象(DrawingVisual)管理,获取鼠标悬浮图形状态,并控制鼠标右键快捷菜单等...的更多相关文章
- Azure上批量创建OS Disk大于30G的Linux VM
Azure上VM的OS盘的大小在创建时是固定的.Windows是127G,Linux是30G.如果需要批量创建的VM的OS Disk有更大的容量.可以考虑用下面的方法实现. 1 创建一台有Data-d ...
- py3.5 telnet的实例(在远程机器上批量创建用户)
import sysimport telnetlibimport time HOST = ["172.18.217.12","172.18.217.13"]#往 ...
- 设计模式-FlyWeight(结构型模式) 针对 需要创建大量对象的情形,被共享的状态作为内部状态,不被共享的状态作为外部状态
以下代码来源: 设计模式精解-GoF 23种设计模式解析附C++实现源码 //Flyweight.h #pragma once #include<string> class FlyWeig ...
- JS,Jquery,ExtJs不同脚本动态创建DOM对象
好久不来写东西了,这段时间太慢了,闲了下来看了几篇文章,觉得很好,同时也许咱们大家都能遇到,所以就把它记录下来... 简单使用JavaScript.JQuery.ExtJs进行DOM对象创建的测试,主 ...
- JS、JQuery和ExtJs动态创建DOM对象
做了个简单使用JavaScript.JQuery.ExtJs进行DOM对象创建的测试,主要是使用JavaScript.JQuery.ExtJs动态创建Table对象.动态Table数据填充.多选控制. ...
- Unit03 - 对象内存管理 、 继承的意义(上)
Unit03 - 对象内存管理 . 继承的意义(上) 1.内存管理:由JVM来管理的 1)堆: 1.1)存储所有new出来的对象(包含成员变量) 1.2)没有任何引用所指向的对象就是垃圾 ...
- 转 Oracle 12C 之 CDB/PDB用户的创建与对象管理
在Oracle 12C中,账号分为两种,一种是公用账号,一种是本地账号(亦可理解为私有账号).共有账号是指在CDB下创建,并在全部PDB中生效的账号,另一种是在PDB中创建的账号. 针对这两种账号的测 ...
- Linux的VMWare中Centos7用户和用户管理三个系统文件(/etc/passwd-shadow-group解读)和批量创建用户user及用户工作环境path
Linux 用户和用户组管理 用户工作环境PATH Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统. 用 ...
- 如何在指定的地址上创建C++对象
如果已经掌握在静态存储区上创建对象的方法,那么可以扩展一下,可以在任意地址上创建C++对象. 解决方案:-在类中重载new/delete操作符-在new的操作符重载函数中返回指定的地址-在delete ...
- 在TerraExplorer中如何批量根据shape多边形对象创建TerrainModify对象?
其实,在Skyline中TerrainModify对象就是一个特殊类型Polygon对象,他们的Geometry是可以直接交换使用的: <!DOCTYPE html PUBLIC "- ...
随机推荐
- 云主机AI服务的性能测试和优化
本文分享自天翼云开发者社区<云主机AI服务的性能测试和优化>,作者:无敌暴龙兽在云计算的时代,越来越多的人选择将AI模型部署在云主机上,以便利用云服务提供商的弹性和可扩展性.然而,仅仅将A ...
- VS Code 开发工具的基本使用
VS Code 开发工具的基本使用 VS Code(Visual Studio Code)是微软开发的一款免费.开源的代码编辑器,它支持多种操作系统,包括Windows.macOS和Linux.VS ...
- [车联网/计算机网络] Autosar 的 `ARXML` 配置数据库文件协议
序: 缘起 ARXML 概述 : Autosar 的 ARXML 配置数据库文件协议 ARXML 文件 ARXML文件: AUTOSAR系统描述文件,后缀*.arxml 实质是一个XML文件,一般通过 ...
- 降阶公式/ARC173F
ARC173F 题意 给定 \(n,A,B\),初始有一个集合 \(S=\{1,2,\dots,A,A +1,A+2,\dots,A+B\}\).进行如下操作 \(n-1\) 次使得剩下 \(n\) ...
- 告别 DeepSeek 系统繁忙,七个 DeepSeek 曲线救国平替入口,官网崩溃也能用!
前言 DeepSeek作为一款备受瞩目的国产大模型,以其强大的功能和卓越的性能赢得了众多用户的青睐.然而,随着用户量的激增,DeepSeek官网近期频繁遭遇服务器繁忙甚至崩溃的问题,给广大用户带来了不 ...
- 正则表达式匹配邮箱,IP地址,URL
参考链接: http://urlregex.com/ 1. 邮箱匹配正则表达式 C# ^(?(")(".+?(?<!\\)"@)|(([0-9a-z]((\.(?! ...
- 泰山派设备控制(RGB)
泰山派设备系统控制(RGB) 1.进入设备系统 cd /sys/class 2.进入RGB灯子系统 cd /sys/class/leds/ 罗列可操作的设备,可以看到三个设备,"rgb-le ...
- 浅谈李飞飞巴黎演讲:如果 AI 资源被少数公司垄断,整个生态系统都会完蛋
在巴黎人工智能峰会开幕式上,斯坦福大学教授.人工智能专家李飞飞发表了主题演讲,揭示了人工智能如何从"观察者"转变为重塑世界的"行动者".她在致辞中,分析了&qu ...
- SpringBoot - [09] Restful风格接口方法&参数
GetMapping.PostMapping.DeleteMapping.PutMapping是SpringBoot中常用的HTTP请求映射注解,它们分别对应HTTP协议中的GET.POST.DELE ...
- Hive - [08] 数据仓库物理模型设计
分区 分区是将表的数据按照某个列的值进行划分和存储的一种方式.通过分区,可以将数据按照特定的维度进行组织,提高查询效率和数据管理的灵活性. 一.分区的优势 提高查询性能:通过分区,可以将数据按照特定的 ...