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

窗口上有个播放区域,区域上悬浮了很多可视对象(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)管理,获取鼠标悬浮图形状态,并控制鼠标右键快捷菜单等...的更多相关文章

  1. Azure上批量创建OS Disk大于30G的Linux VM

    Azure上VM的OS盘的大小在创建时是固定的.Windows是127G,Linux是30G.如果需要批量创建的VM的OS Disk有更大的容量.可以考虑用下面的方法实现. 1 创建一台有Data-d ...

  2. py3.5 telnet的实例(在远程机器上批量创建用户)

    import sysimport telnetlibimport time HOST = ["172.18.217.12","172.18.217.13"]#往 ...

  3. 设计模式-FlyWeight(结构型模式) 针对 需要创建大量对象的情形,被共享的状态作为内部状态,不被共享的状态作为外部状态

    以下代码来源: 设计模式精解-GoF 23种设计模式解析附C++实现源码 //Flyweight.h #pragma once #include<string> class FlyWeig ...

  4. JS,Jquery,ExtJs不同脚本动态创建DOM对象

    好久不来写东西了,这段时间太慢了,闲了下来看了几篇文章,觉得很好,同时也许咱们大家都能遇到,所以就把它记录下来... 简单使用JavaScript.JQuery.ExtJs进行DOM对象创建的测试,主 ...

  5. JS、JQuery和ExtJs动态创建DOM对象

    做了个简单使用JavaScript.JQuery.ExtJs进行DOM对象创建的测试,主要是使用JavaScript.JQuery.ExtJs动态创建Table对象.动态Table数据填充.多选控制. ...

  6. Unit03 - 对象内存管理 、 继承的意义(上)

    Unit03 - 对象内存管理 . 继承的意义(上) 1.内存管理:由JVM来管理的  1)堆:    1.1)存储所有new出来的对象(包含成员变量)    1.2)没有任何引用所指向的对象就是垃圾 ...

  7. 转 Oracle 12C 之 CDB/PDB用户的创建与对象管理

    在Oracle 12C中,账号分为两种,一种是公用账号,一种是本地账号(亦可理解为私有账号).共有账号是指在CDB下创建,并在全部PDB中生效的账号,另一种是在PDB中创建的账号. 针对这两种账号的测 ...

  8. Linux的VMWare中Centos7用户和用户管理三个系统文件(/etc/passwd-shadow-group解读)和批量创建用户user及用户工作环境path

    Linux 用户和用户组管理 用户工作环境PATH Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统. 用 ...

  9. 如何在指定的地址上创建C++对象

    如果已经掌握在静态存储区上创建对象的方法,那么可以扩展一下,可以在任意地址上创建C++对象. 解决方案:-在类中重载new/delete操作符-在new的操作符重载函数中返回指定的地址-在delete ...

  10. 在TerraExplorer中如何批量根据shape多边形对象创建TerrainModify对象?

    其实,在Skyline中TerrainModify对象就是一个特殊类型Polygon对象,他们的Geometry是可以直接交换使用的: <!DOCTYPE html PUBLIC "- ...

随机推荐

  1. 云主机AI服务的性能测试和优化

    本文分享自天翼云开发者社区<云主机AI服务的性能测试和优化>,作者:无敌暴龙兽在云计算的时代,越来越多的人选择将AI模型部署在云主机上,以便利用云服务提供商的弹性和可扩展性.然而,仅仅将A ...

  2. VS Code 开发工具的基本使用

    VS Code 开发工具的基本使用 VS Code(Visual Studio Code)是微软开发的一款免费.开源的代码编辑器,它支持多种操作系统,包括Windows.macOS和Linux.VS ...

  3. [车联网/计算机网络] Autosar 的 `ARXML` 配置数据库文件协议

    序: 缘起 ARXML 概述 : Autosar 的 ARXML 配置数据库文件协议 ARXML 文件 ARXML文件: AUTOSAR系统描述文件,后缀*.arxml 实质是一个XML文件,一般通过 ...

  4. 降阶公式/ARC173F

    ARC173F 题意 给定 \(n,A,B\),初始有一个集合 \(S=\{1,2,\dots,A,A +1,A+2,\dots,A+B\}\).进行如下操作 \(n-1\) 次使得剩下 \(n\) ...

  5. 告别 DeepSeek 系统繁忙,七个 DeepSeek 曲线救国平替入口,官网崩溃也能用!

    前言 DeepSeek作为一款备受瞩目的国产大模型,以其强大的功能和卓越的性能赢得了众多用户的青睐.然而,随着用户量的激增,DeepSeek官网近期频繁遭遇服务器繁忙甚至崩溃的问题,给广大用户带来了不 ...

  6. 正则表达式匹配邮箱,IP地址,URL

    参考链接: http://urlregex.com/ 1. 邮箱匹配正则表达式 C# ^(?(")(".+?(?<!\\)"@)|(([0-9a-z]((\.(?! ...

  7. 泰山派设备控制(RGB)

    泰山派设备系统控制(RGB) 1.进入设备系统 cd /sys/class 2.进入RGB灯子系统 cd /sys/class/leds/ 罗列可操作的设备,可以看到三个设备,"rgb-le ...

  8. 浅谈李飞飞巴黎演讲:如果 AI 资源被少数公司垄断,整个生态系统都会完蛋

    在巴黎人工智能峰会开幕式上,斯坦福大学教授.人工智能专家李飞飞发表了主题演讲,揭示了人工智能如何从"观察者"转变为重塑世界的"行动者".她在致辞中,分析了&qu ...

  9. SpringBoot - [09] Restful风格接口方法&参数

    GetMapping.PostMapping.DeleteMapping.PutMapping是SpringBoot中常用的HTTP请求映射注解,它们分别对应HTTP协议中的GET.POST.DELE ...

  10. Hive - [08] 数据仓库物理模型设计

    分区 分区是将表的数据按照某个列的值进行划分和存储的一种方式.通过分区,可以将数据按照特定的维度进行组织,提高查询效率和数据管理的灵活性. 一.分区的优势 提高查询性能:通过分区,可以将数据按照特定的 ...