最近的一個項目有個需求,就是點擊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了。