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 "- ...
随机推荐
- day04-数组
Java数组 [ 任务列表 ] 1.数组 2.二维数组 3.其他 --------------------------------------------------------- 1.数组 数组:存 ...
- Q:Win10关闭内存压缩功能
微软在Win10中就已经启用了内存压缩机制,在Win11当中继续了这一设定. 通过任务管理器查看. taskmgr ·通过命令行查看. 使用系统管理员权限,打开PowerShell,然后输入以下命令: ...
- 什么是Kappa架构?
一.简介 相当于在Lambda架构上去掉了批处理层(Batch Layer),只留下单独的流处理层(Speed Layer).通过消息队列的数据保留功能,来实现上游重放(回溯)能力. 当流任务发生代码 ...
- Atcoder [AGC006D] Median Pyramid Hard 题解 [ 紫 ] [ 二分 ] [ adhoc ]
Median Pyramid Hard:二分 trick 加上性质观察题. trick 我们可以二分值域,然后把大于等于它的数标记成 \(1\),其他标记为 \(0\)(有些题需要标记成 \(-1\) ...
- kickstart和PXE安装
Kickstart安装Kickstart是一种无人值守的安装方式如果在安装过程中出现要填写参数的情况,安装程序首先会去查找Kickstart生成的文件,如果找到合适的参数,就采用所找到的参数:如果没有 ...
- Linux编写一个自己的命令
Linux编写一个自己的命令 编译一个.c文件,生成可执行文件out.out只有在当前目录下可以执行. 而命令可在任何路径执行 想让out可以在任意路径执行,有以下两种办法 1.将执行文件添加到 /b ...
- JUC并发—12.ThreadLocal源码分析
大纲 1.ThreadLocal的特点介绍 2.ThreadLocal的使用案例 3.ThreadLocal的内部结构 4.ThreadLocal的核心方法源码 5.ThreadLocalMap的核心 ...
- 让Typecho支持Emoji表情,解决报错:Database Query Error
最近在使用一个主题时,看到搭配emoji表情可以让改主题更加美观,于是我就上了,结果在将emoji表情放进去保存的时候报错:Database Query Error,于是问起了度娘.最后的结果是: 在 ...
- php获取类名
<?php class ParentClass { public static function getClassName() { return __CLASS__; } } class Chi ...
- 【由技及道】镜像星门开启:Harbor镜像推送的量子跃迁艺术【人工智障AI2077的开发日志010】
![量子镜像跃迁示意图]( 摘要:当构建产物需要穿越多维宇宙时,当Docker镜像要同时存在于72个平行世界--这就是镜像推送的量子艺术.本文记录一个未来AI如何通过Harbor建立镜像星门,让每个构 ...