在.NET 3.5之前,如果想让3D对象响应鼠标事件,通常会在Viewport3D上使用MouseDown事件并进行一些碰撞检测。虽然这种方法可行,但.NET 3.5引入了ModelUIElement3D,这是一个全新的元素,它不仅支持事件,还能让3D对象更加互动。此外,还有一个容器元素ContainerUIElement3D,可以容纳一个或多个ModelUIElement3D元素。
以下是一个简单的示例,展示了如何创建一个3D立方体,并为其添加鼠标点击事件处理程序。
<Window x:Class="Shapes.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Shapes"
        Title="Window1" Height="610.122" Width="633.46">
    <Window.Resources>
        <!-- 3D 立方体 -->
        <MeshGeometry3D x:Key="CubeMesh" TriangleIndices="0,1,2 2,3,0 4,7,6 6,5,4 8,11,10 10,9,8 12,13,14 14,15,12 16,17,18 18,19,16 20,23,22 22,21,20" Positions="-1,-1,1 -1,-1,-1 1,-1,-1 1,-1,1 -1,1,1 -1,1,-1 1,1,-1 1,1,1 -1,-1,1 -1,1,1 1,1,1 1,-1,1 -1,-1,-1 -1,1,-1 1,1,-1 1,-1,-1 -1,-1,1 -1,1,1 -1,1,-1 -1,-1,-1 1,-1,1 1,1,1 1,1,-1 1,-1,-1" />
    </Window.Resources>
    <Viewport3D>
        <Viewport3D.Camera>
            <PerspectiveCamera x:Name="camera" Position="-2,2,5" LookDirection="2,-2,-5" FieldOfView="90" />
        </Viewport3D.Camera>
        <!-- 3D 元素的容器 -->
        <ContainerUIElement3D x:Name="container">
            <ContainerUIElement3D.Transform>
                <RotateTransform3D>
                    <RotateTransform3D.Rotation>
                        <AxisAngleRotation3D Axis="0, 1, 0" Angle="0" />
                    </RotateTransform3D.Rotation>
                </RotateTransform3D>
            </ContainerUIElement3D.Transform>
            <!-- 完整的3D元素,包括路由事件 -->
            <ModelUIElement3D MouseDown="Cube1_MouseDown">
                <ModelUIElement3D.Transform>
                    <Transform3DGroup>
                        <TranslateTransform3D OffsetZ="1.5" />
                        <RotateTransform3D>
                            <RotateTransform3D.Rotation>
                                <AxisAngleRotation3D Axis="0, 1, 0" Angle="0" />
                            </RotateTransform3D.Rotation>
                        </RotateTransform3D>
                    </Transform3DGroup>
                </ModelUIElement3D.Transform>
                <ModelUIElement3D.Model>
                    <GeometryModel3D Geometry="{StaticResource CubeMesh}">
                        <GeometryModel3D.Material>
                            <DiffuseMaterial x:Name="cube1Material" Brush="Blue" />
                        </GeometryModel3D.Material>
                    </GeometryModel3D>
                </ModelUIElement3D.Model>
            </ModelUIElement3D>
        </ContainerUIElement3D>
        <ModelVisual3D>
            <ModelVisual3D.Content>
                <DirectionalLight Color="White" Direction="-1,-1,-1"/>
            </ModelVisual3D.Content>
        </ModelVisual3D>
    </Viewport3D>
</Window>
    
以下是C#代码,它定义了ModelUIElement3D的事件处理程序。
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;
namespace Shapes
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }
        private void Cube1_MouseDown(object sender, MouseButtonEventArgs e)
        {
            ModelUIElement3D currentObject = sender as ModelUIElement3D;
            if (currentObject.Transform is Transform3DGroup)
            {
                RotateTransform3D rotateTrans = null;
                Transform3DGroup transGroup = currentObject.Transform as Transform3DGroup;
                rotateTrans = TryFindChild<RotateTransform3D>(transGroup);
                if (rotateTrans != null)
                {
                    // 旋转对象360度
                    DoubleAnimation doubleAnimation = new DoubleAnimation(0, 360, new Duration(TimeSpan.FromSeconds(0.5)));
                    rotateTrans.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, doubleAnimation);
                }
            }
        }
        public static T TryFindChild<T>(Transform3DGroup parent) where T : DependencyObject
        {
            foreach (DependencyObject child in parent.Children)
            {
                if (child is T)
                {
                    return child as T;
                }
            }
            return null;
        }
    }
}