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 "- ...
随机推荐
- uni-app中使用uView组件库
01通过npm的方式安装uView组件库 uView依赖SCSS,您必须要安装此插件,否则无法正常运行. 如果您的项目是由HBuilder X创建的, 相信已经安装scss插件, 如果没有,请在HX菜 ...
- java中的HsahMap
HsahMap HashMap 是 Java 中最常用的集合类之一,它实现了 Map 接口,基于哈希表存储键值对 HashMap的存储是无顺序的 HashMap存储的是键值对(key-value)其中 ...
- 从理房间到移动零:一道考察数组操作的经典题目|LeetCode 283 移动零
LeetCode 283 移动零 点此看全部题解 LeetCode必刷100题:一份来自面试官的算法地图(题解持续更新中) 生活中的算法 你有没有整理过房间?常常会发现一些要丢掉的东西,但又不想立刻处 ...
- 浅谈基于SASE的安全云服务
本文分享自天翼云开发者社区<浅谈基于SASE的安全云服务>,作者:姚****亮 SASE(secure access service edge安全访问服务边缘):是一种安全框架,结合了软件 ...
- 天翼云重磅升级边缘WAF能力,助力企业高效应对Web安全威胁!
"2022年,网络高危漏洞数量同比增长了13%:Q2遭受攻击的API数量月均超过了25万:物联网的普及大大降低了DDoS的攻击成本,大流量攻击指数显著提升:恶意Bot流量仍在持续增长,202 ...
- galaxy特色胡思乱想
有没有什么办法,让我不伤害任何人,什么也不破坏,被判死刑.我觉得这样比我紫砂要好的多. 我所可怜的是神不能紫砂.--芥川龙之介<某傻子的一生>
- Q:oracle备份表语句
oracle备份还原表语句 方法1.sql语句(同一数据库服务器) 备份 create table xxx_temp as select * from xxx; 还原 truncate table x ...
- [MQ] Kafka
概述: Kafka 安装指南 安装 on Windows Step1 安装 JDK JDK 安装后: 在"系统变量"中,找到 JAVA_HOME,如果没有则新建,将其值设置为 JD ...
- Ubuntu修改密码和用户名
Ubuntu是一个Linux操作系统,修改密码和用户名是有危险的动作,请谨慎修改. 一.Ubuntu修改密码和用户名 Ubuntu更改密码步骤:1.进入Ubuntu,打开一个终端,输入 sudo su ...
- C# async/await使用举例
1.async/await几点总结 a.被async标记的方法,返回值类型只能为void.Task.Task<T>. b.被async标记的方法,内部可以有await修饰符,表明内部逻辑某 ...