最近的一个项目有个需求,就是点击WPF 3D模型的某个部分并对其后续操作,例如获得这个部分的数据并展示曲线图等等(这部分暂不展开)
先上截图。
我选择了一个方块,将方块显示为蓝色,同时屏幕坐上显示了点击方块后的操作。
整个流程我处理了两个问题:
1. 如何选择模型的这一部分
2. 如何维护你选择的这个部分
问题1:如何选择模型的这一部分
由于WPF 3D模型中只有Viewport3D对象有MouseButtonLeftDown事件,而模型的某个部分是没有这个事件的。因此需要使用首先VisualTreeHelper.HitTest来确定具体点击的是哪个模型,同时这个模型是需要给外部用的,因此我们自定义了一个ModelVisual3DSelectedEventHandler的事件给外部调用。
2 public class Visual3DEventArgs : EventArgs
3 {
4 public ModelVisual3D Visual3D { get; set; }
5
6 public Visual3DEventArgs(ModelVisual3D modelVisual3D)
7 {
8 this.Visual3D = modelVisual3D;
9 }
10 }
11
12 public delegate void ModelVisual3DSelectedEventHandler( object sender, Visual3DEventArgs args);
13 // ....
14
15 // 声明Viewport3D的LeftButton事件
16 ViewPort.MouseLeftButtonDown += ViewPort_MouseLeftButtonDown;
17
18 // Viewport LeftButton的函数
19 void ViewPort_MouseLeftButtonDown( object sender, System.Windows.Input.MouseButtonEventArgs args)
20 {
21 Point mousePos = args.GetPosition(ViewPort);
22 var hitParams = new PointHitTestParameters(mousePos);
23 VisualTreeHelper.HitTest(ViewPort, null, ResultCallback, hitParams);
24 }
25
26 // 根据HitTest选中之后的处理
27 private HitTestResultBehavior ResultCallback(HitTestResult result)
28 {
29 RayHitTestResult rayResult = result as RayHitTestResult;
30
31 if (rayResult != null)
32 {
33 var visual3D = result.VisualHit as ModelVisual3D;
34 if (visual3D != null)
35 {
36 if (OnModelSelected != null)
37 {
38 OnModelSelected( this, new Visual3DEventArgs(visual3D));
39 }
40
41 return HitTestResultBehavior.Stop;
42 }
43
44 }
45 return HitTestResultBehavior.Continue;
46 }
2. 问题2:如何维护你选择部分
我刚刚在Visual3DEventArgs中已经获得了选择的模型了,这时候我就可以对它进行操作了。或许数据库中有个模型叫"block1",查找数据等等.... 但是很悲催的是, 虽然在xaml文件中ModelVisual3D是有x:name的,但是ModelVisual3D本书是没有 ModelVisual3D.Name这个属性的,即使获取了这个模型,也只能对其进行基本的渲染,根本没有办法进行逻辑操作(例如根据数据库通信等)。
只有通过viewport3D.FindName(string name)才能获取对象!
解决办法是:我们自己可以维护一个模型名字表(其实就是个xml文件):
<Sections>
< Name >dg01 </ Name >
</ Section >
< Section >
< Name >dg02 </ Name >
</ Section >
< Section >
< Name >dg04 </ Name >
</ Section >
</ Sections >
然后定义一个返回名字的函数:
2 {
3 if (_context != null && _context.Sections != null && visual3D != null)
4 {
5 var viewPort = _context.Model.ViewPort;
6
7 foreach ( var section in _context.Sections)
8 {
9 if (viewPort.FindName(section.Name) == visual3D)
10 {
11 return section;
12 }
13 }
14 }
15
17 return null;
18 }
我就可以通过GetSelSection(args.Visual3D)获得这个section对象了。对了,这个Section就是刚才XML文件的映射类。和数据库操作就可以使用这个Section了。