15

我希望有人可以在这里帮助我。我正在构建一个 WPF 成像应用程序,该应用程序从相机获取实时图像,允许用户查看图像,然后突出显示该图像上的感兴趣区域 (ROI)。然后将有关 ROI 的信息(宽度、高度、相对于图像上某个点的位置等)发送回相机,实际上是告诉/训练相机固件在哪里寻找条形码、文本、液位、转弯等信息图像上的螺钉等)。所需的功能是能够平移和缩放图像及其 ROI,以及在图像缩放到大于查看区域时进行滚动。ROI 的 StrokeThickness 和 FontSize 需要保持原始比例,但是 ROI 内形状的宽度和高度需要随图像缩放(这对于捕获精确的像素位置以传输到相机至关重要)。除了滚动和其他一些问题外,我已经解决了大部分问题。我关心的两个方面是:

  1. 当我引入 ScrollViewer 时,我没有得到任何滚动行为。据我了解,我需要引入一个 LayoutTransform 以获得正确的 ScrollViewer 行为。但是,当我这样做时,其他区域开始崩溃(例如,ROI 没有在图像上保持正确的位置,或者鼠标指针在平移时开始远离图像上的选定点,或者我的图像的左角弹跳到 MouseDown 上的当前鼠标位置。)

  2. 我不能完全按照我需要的方式来扩展我的投资回报率。我有这个工作,但它并不理想。我所拥有的没有保留确切的笔画粗细,而且我没有考虑忽略文本块上的比例。希望您会在代码示例中看到我在做什么。

我确定我的问题与我对变换及其与 WPF 布局系统的关系缺乏了解有关。希望展示我到目前为止所完成的代码的再现会有所帮助(见下文)。

仅供参考,如果建议使用装饰器,那在我的场景中可能不起作用,因为我最终可能会得到比支持更多的装饰器(谣言 144 装饰器是事情开始崩溃的时候)。

首先,下面是显示具有 ROI(文本和形状)的图像的屏幕截图。矩形、椭圆和文本需要跟随图像上的区域进行缩放和旋转,但它们不应该在粗细或字体大小上缩放。

显示带有 ROI 的示例图像的屏幕截图

这是显示上图的 XAML,以及用于缩放的滑块(鼠标滚轮缩放稍后会出现)

<Window x:Class="PanZoomStackOverflow.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    mc:Ignorable="d"
    Title="MainWindow" Height="768" Width="1024">

<DockPanel>
  <Slider x:Name="_ImageZoomSlider" DockPanel.Dock="Bottom"
          Value="2"
          HorizontalAlignment="Center" Margin="6,0,0,0" 
          Width="143" Minimum=".5" Maximum="20" SmallChange=".1" 
          LargeChange=".2" TickFrequency="2" 
          TickPlacement="BottomRight" Padding="0" Height="23"/>

  <!-- This resides in a user control in my solution -->
  <Grid x:Name="LayoutRoot">
    <ScrollViewer Name="border" HorizontalScrollBarVisibility="Auto" 
                  VerticalScrollBarVisibility="Auto">
      <Grid x:Name="_ImageDisplayGrid">
        <Image x:Name="_DisplayImage" Margin="2" Stretch="None"
               Source="Untitled.bmp"
               RenderTransformOrigin ="0.5,0.5"
               RenderOptions.BitmapScalingMode="NearestNeighbor"
               MouseLeftButtonDown="ImageScrollArea_MouseLeftButtonDown"
               MouseLeftButtonUp="ImageScrollArea_MouseLeftButtonUp"
               MouseMove="ImageScrollArea_MouseMove">                            
           <Image.LayoutTransform>
             <TransformGroup>
               <ScaleTransform />
               <TranslateTransform />
             </TransformGroup>
           </Image.LayoutTransform>
         </Image>
         <AdornerDecorator> <!-- Using this Adorner Decorator for Move, Resize and Rotation and feedback adornernments -->
           <Canvas x:Name="_ROICollectionCanvas"
                   Width="{Binding ElementName=_DisplayImage, Path=ActualWidth, Mode=OneWay}"
                   Height="{Binding ElementName=_DisplayImage, Path=ActualHeight, Mode=OneWay}"
                   Margin="{Binding ElementName=_DisplayImage, Path=Margin, Mode=OneWay}">

             <!-- This is a user control in my solution -->
             <Grid IsHitTestVisible="False" Canvas.Left="138" Canvas.Top="58" Height="25" Width="186">
               <TextBlock Text="Rectangle ROI" HorizontalAlignment="Center" VerticalAlignment="Top" 
                          Foreground="Orange" FontWeight="Bold" Margin="0,-15,0,0"/>
                 <Rectangle StrokeThickness="2" Stroke="Orange"/>
             </Grid>

             <!-- This is a user control in my solution -->
             <Grid IsHitTestVisible="False" Canvas.Left="176" Canvas.Top="154" Height="65" Width="69">
               <TextBlock Text="Ellipse ROI" HorizontalAlignment="Center" VerticalAlignment="Top" 
                          Foreground="Orange" FontWeight="Bold" Margin="0,-15,0,0"/>
               <Ellipse StrokeThickness="2" Stroke="Orange"/>
             </Grid>
           </Canvas>
         </AdornerDecorator>
       </Grid>
     </ScrollViewer>
  </Grid>
</DockPanel>

这是管理平移和缩放的 C#。

public partial class MainWindow : Window
{
private Point origin;
private Point start;
private Slider _slider;

public MainWindow()
{
    this.InitializeComponent();

    //Setup a transform group that we'll use to manage panning of the image area
    TransformGroup group = new TransformGroup();
    ScaleTransform st = new ScaleTransform();
    group.Children.Add(st);
    TranslateTransform tt = new TranslateTransform();
    group.Children.Add(tt);
    //Wire up the slider to the image for zooming
    _slider = _ImageZoomSlider;
    _slider.ValueChanged += _ImageZoomSlider_ValueChanged;
    st.ScaleX = _slider.Value;
    st.ScaleY = _slider.Value;
    //_ImageScrollArea.RenderTransformOrigin = new Point(0.5, 0.5);
    //_ImageScrollArea.LayoutTransform = group;
    _DisplayImage.RenderTransformOrigin = new Point(0.5, 0.5);
    _DisplayImage.RenderTransform = group;
    _ROICollectionCanvas.RenderTransformOrigin = new Point(0.5, 0.5);
    _ROICollectionCanvas.RenderTransform = group;
}

//Captures the mouse to prepare for panning the scrollable image area
private void ImageScrollArea_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    _DisplayImage.ReleaseMouseCapture();
}

//Moves/Pans the scrollable image area  assuming mouse is captured.
private void ImageScrollArea_MouseMove(object sender, MouseEventArgs e)
{
    if (!_DisplayImage.IsMouseCaptured) return;

    var tt = (TranslateTransform)((TransformGroup)_DisplayImage.RenderTransform).Children.First(tr => tr is TranslateTransform);

    Vector v = start - e.GetPosition(border);
    tt.X = origin.X - v.X;
    tt.Y = origin.Y - v.Y;
}

//Cleanup for Move/Pan when mouse is released
private void ImageScrollArea_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    _DisplayImage.CaptureMouse();
    var tt = (TranslateTransform)((TransformGroup)_DisplayImage.RenderTransform).Children.First(tr => tr is TranslateTransform);
    start = e.GetPosition(border);
    origin = new Point(tt.X, tt.Y);
}

//Zoom according to the slider changes
private void _ImageZoomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    //Panel panel = _ImageScrollArea;
    Image panel = _DisplayImage;

    //Set the scale coordinates on the ScaleTransform from the slider
    ScaleTransform transform = (ScaleTransform)((TransformGroup)panel.RenderTransform).Children.First(tr => tr is ScaleTransform);
    transform.ScaleX = _slider.Value;
    transform.ScaleY = _slider.Value;


    //Set the zoom (this will affect rotate too) origin to the center of the panel
    panel.RenderTransformOrigin = new Point(0.5, 0.5);

    foreach (UIElement child in _ROICollectionCanvas.Children)
    {
        //Assume all shapes are contained in a panel
        Panel childPanel = child as Panel;

        var x = childPanel.Children;

        //Shape width and heigh should scale, but not StrokeThickness
        foreach (var shape in childPanel.Children.OfType<Shape>())
        {
            if (shape.Tag == null)
            {
                //Hack: This is be a property on a usercontrol in my solution
                shape.Tag = shape.StrokeThickness;
            }
            double orignalStrokeThickness = (double)shape.Tag;

            //Attempt to keep the underlying shape border/stroke from thickening as well
            double newThickness = shape.StrokeThickness - (orignalStrokeThickness / transform.ScaleX);

            shape.StrokeThickness -= newThickness;
        }
    }
}
}

假设没有剪切/粘贴错误,代码应该在 .NET 4.0 或 4.5 项目和解决方案中工作。

有什么想法吗?欢迎提出建议。

4

3 回答 3

23

好的。这是我对你所描述的看法。

它看起来像这样:

在此处输入图像描述

  • 由于我没有应用任何RenderTransformsScrollbar / ScrollViewer 功能,因此我得到了所需的功能。
  • MVVM,这是进入 WPF 的方式。UI 和数据是独立的,因此 DataItems 仅具有X、Y、宽度、高度doubleint属性,您可以将其用于任何目的,甚至将它们存储在数据库中。
  • 我在 a 中添加了所有东西Thumb来处理平移。您仍然需要对通过 ResizerControl 拖动/调整 ROI 大小时发生的平移做一些事情。我想你可以检查Mouse.DirectlyOver一下什么的。
  • 我实际上使用 aListBox来处理 ROI,以便您在任何给定时间都可以选择 1 个 ROI。这将切换调整大小功能。因此,如果您单击 ROI,您将看到调整大小。
  • 缩放是在 ViewModel 级别处理的,因此不需要自定义Panels或类似的东西(尽管@Clemens 的解决方案也很好)
  • 我正在使用一个Enum和一些DataTriggers来定义形状。见DataTemplate DataType={x:Type local:ROI}部分。
  • WPF 摇滚。只需将我的代码复制并粘贴到 a 中File -> New Project -> WPF Application,然后自己查看结果。

    <Window x:Class="MiscSamples.PanZoomStackOverflow_MVVM"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:MiscSamples"
            Title="PanZoomStackOverflow_MVVM" Height="300" Width="300">
       <Window.Resources>
        <DataTemplate DataType="{x:Type local:ROI}">
            <Grid Background="#01FFFFFF">
                <Path x:Name="Path" StrokeThickness="2" Stroke="Black"
                      Stretch="Fill"/>
                <local:ResizerControl Visibility="Collapsed" Background="#30FFFFFF"
                                      X="{Binding X}" Y="{Binding Y}"
                                      ItemWidth="{Binding Width}"
                                      ItemHeight="{Binding Height}"
                                      x:Name="Resizer"/>
            </Grid>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="True">
                    <Setter TargetName="Resizer" Property="Visibility" Value="Visible"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding Shape}" Value="{x:Static local:Shapes.Square}">
                    <Setter TargetName="Path" Property="Data">
                        <Setter.Value>
                            <RectangleGeometry Rect="0,0,10,10"/>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
    
                <DataTrigger Binding="{Binding Shape}" Value="{x:Static local:Shapes.Round}">
                    <Setter TargetName="Path" Property="Data">
                        <Setter.Value>
                            <EllipseGeometry RadiusX="10" RadiusY="10"/>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    
        <Style TargetType="ListBox" x:Key="ROIListBoxStyle">
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <Canvas/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <ItemsPresenter/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="ListBoxItem" x:Key="ROIItemStyle">
            <Setter Property="Canvas.Left" Value="{Binding ActualX}"/>
            <Setter Property="Canvas.Top" Value="{Binding ActualY}"/>
            <Setter Property="Height" Value="{Binding ActualHeight}"/>
            <Setter Property="Width" Value="{Binding ActualWidth}"/>
    
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <ContentPresenter ContentSource="Content"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    
    </Window.Resources>
    
    <DockPanel>
        <Slider VerticalAlignment="Center" 
                Maximum="2" Minimum="0" Value="{Binding ScaleFactor}" SmallChange=".1"
                DockPanel.Dock="Bottom"/>
    
        <ScrollViewer VerticalScrollBarVisibility="Visible"
                      HorizontalScrollBarVisibility="Visible" x:Name="scr"
                      ScrollChanged="ScrollChanged">
            <Thumb DragDelta="Thumb_DragDelta">
                <Thumb.Template>
                    <ControlTemplate>
                        <Grid>
                            <Image Source="/Images/Homer.jpg" Stretch="None" x:Name="Img"
                                    VerticalAlignment="Top" HorizontalAlignment="Left">
                                <Image.LayoutTransform>
                                    <TransformGroup>
                                        <ScaleTransform ScaleX="{Binding ScaleFactor}" ScaleY="{Binding ScaleFactor}"/>
                                    </TransformGroup>
                                </Image.LayoutTransform>
                            </Image>
    
                            <ListBox ItemsSource="{Binding ROIs}"
                                     Width="{Binding ActualWidth, ElementName=Img}"
                                     Height="{Binding ActualHeight,ElementName=Img}"
                                     VerticalAlignment="Top" HorizontalAlignment="Left"
                                     Style="{StaticResource ROIListBoxStyle}"
                                     ItemContainerStyle="{StaticResource ROIItemStyle}"/>
                        </Grid>
                    </ControlTemplate>
                </Thumb.Template>
            </Thumb>
        </ScrollViewer>
    </DockPanel>
    

代码背后:

public partial class PanZoomStackOverflow_MVVM : Window
    {
        public PanZoomViewModel ViewModel { get; set; }

        public PanZoomStackOverflow_MVVM()
        {
            InitializeComponent();
            DataContext = ViewModel = new PanZoomViewModel();

            ViewModel.ROIs.Add(new ROI() {ScaleFactor = ViewModel.ScaleFactor, X = 150, Y = 150, Height = 200, Width = 200, Shape = Shapes.Square});

            ViewModel.ROIs.Add(new ROI() { ScaleFactor = ViewModel.ScaleFactor, X = 50, Y = 230, Height = 102, Width = 300, Shape = Shapes.Round });
        }

        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            //TODO: Detect whether a ROI is being resized / dragged and prevent Panning if so.
            IsPanning = true;
            ViewModel.OffsetX = (ViewModel.OffsetX + (((e.HorizontalChange/10) * -1) * ViewModel.ScaleFactor));
            ViewModel.OffsetY = (ViewModel.OffsetY + (((e.VerticalChange/10) * -1) * ViewModel.ScaleFactor));

            scr.ScrollToVerticalOffset(ViewModel.OffsetY);
            scr.ScrollToHorizontalOffset(ViewModel.OffsetX);

            IsPanning = false;
        }

        private bool IsPanning { get; set; }

        private void ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            if (!IsPanning)
            {
                ViewModel.OffsetX = e.HorizontalOffset;
                ViewModel.OffsetY = e.VerticalOffset;
            }
        }
    }

主视图模型:

public class PanZoomViewModel:PropertyChangedBase
{
    private double _offsetX;
    public double OffsetX
    {
        get { return _offsetX; }
        set
        {
            _offsetX = value;
            OnPropertyChanged("OffsetX");
        }
    }

    private double _offsetY;
    public double OffsetY
    {
        get { return _offsetY; }
        set
        {
            _offsetY = value;
            OnPropertyChanged("OffsetY");
        }
    }

    private double _scaleFactor = 1;
    public double ScaleFactor
    {
        get { return _scaleFactor; }
        set
        {
            _scaleFactor = value;
            OnPropertyChanged("ScaleFactor");
            ROIs.ToList().ForEach(x => x.ScaleFactor = value);
        }
    }

    private ObservableCollection<ROI> _rois;
    public ObservableCollection<ROI> ROIs
    {
        get { return _rois ?? (_rois = new ObservableCollection<ROI>()); }
    }
}

投资回报率视图模型:

public class ROI:PropertyChangedBase
{
    private Shapes _shape;
    public Shapes Shape
    {
        get { return _shape; }
        set
        {
            _shape = value;
            OnPropertyChanged("Shape");
        }
    }

    private double _scaleFactor;
    public double ScaleFactor
    {
        get { return _scaleFactor; }
        set
        {
            _scaleFactor = value;
            OnPropertyChanged("ScaleFactor");
            OnPropertyChanged("ActualX");
            OnPropertyChanged("ActualY");
            OnPropertyChanged("ActualHeight");
            OnPropertyChanged("ActualWidth");
        }
    }

    private double _x;
    public double X
    {
        get { return _x; }
        set
        {
            _x = value;
            OnPropertyChanged("X");
            OnPropertyChanged("ActualX");
        }
    }

    private double _y;
    public double Y
    {
        get { return _y; }
        set
        {
            _y = value;
            OnPropertyChanged("Y");
            OnPropertyChanged("ActualY");
        }
    }

    private double _height;
    public double Height
    {
        get { return _height; }
        set
        {
            _height = value;
            OnPropertyChanged("Height");
            OnPropertyChanged("ActualHeight");
        }
    }

    private double _width;
    public double Width
    {
        get { return _width; }
        set
        {
            _width = value;
            OnPropertyChanged("Width");
            OnPropertyChanged("ActualWidth");
        }
    }

    public double ActualX { get { return X*ScaleFactor; }}
    public double ActualY { get { return Y*ScaleFactor; }}
    public double ActualWidth { get { return Width*ScaleFactor; }}
    public double ActualHeight { get { return Height * ScaleFactor; } }
}

形状枚举:

public enum Shapes
{
    Round = 1,
    Square = 2,
    AnyOther
}

PropertyChangedBase(MVVM Helper 类):

    public class PropertyChangedBase:INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            Application.Current.Dispatcher.BeginInvoke((Action) (() =>
                                                                     {
                                                                         PropertyChangedEventHandler handler = PropertyChanged;
                                                                         if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
                                                                     }));
        }
    }

调整大小控制:

<UserControl x:Class="MiscSamples.ResizerControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Thumb DragDelta="Center_DragDelta" Height="10" Width="10"
               VerticalAlignment="Center" HorizontalAlignment="Center"/>

        <Thumb DragDelta="UpperLeft_DragDelta" Height="10" Width="10"
               VerticalAlignment="Top" HorizontalAlignment="Left"/>

        <Thumb DragDelta="UpperRight_DragDelta" Height="10" Width="10"
               VerticalAlignment="Top" HorizontalAlignment="Right"/>

        <Thumb DragDelta="LowerLeft_DragDelta" Height="10" Width="10"
               VerticalAlignment="Bottom" HorizontalAlignment="Left"/>

        <Thumb DragDelta="LowerRight_DragDelta" Height="10" Width="10"
               VerticalAlignment="Bottom" HorizontalAlignment="Right"/>

    </Grid>
</UserControl>

代码背后:

 public partial class ResizerControl : UserControl
    {
        public static readonly DependencyProperty XProperty = DependencyProperty.Register("X", typeof(double), typeof(ResizerControl), new FrameworkPropertyMetadata(0d,FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
        public static readonly DependencyProperty YProperty = DependencyProperty.Register("Y", typeof(double), typeof(ResizerControl), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
        public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register("ItemWidth", typeof(double), typeof(ResizerControl), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
        public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register("ItemHeight", typeof(double), typeof(ResizerControl), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public double X
        {
            get { return (double) GetValue(XProperty); }
            set { SetValue(XProperty, value); }
        }

        public double Y
        {
            get { return (double)GetValue(YProperty); }
            set { SetValue(YProperty, value); }
        }

        public double ItemHeight
        {
            get { return (double) GetValue(ItemHeightProperty); }
            set { SetValue(ItemHeightProperty, value); }
        }

        public double ItemWidth
        {
            get { return (double) GetValue(ItemWidthProperty); }
            set { SetValue(ItemWidthProperty, value); }
        }

        public ResizerControl()
        {
            InitializeComponent();
        }

        private void UpperLeft_DragDelta(object sender, DragDeltaEventArgs e)
        {
            X = X + e.HorizontalChange;
            Y = Y + e.VerticalChange;

            ItemHeight = ItemHeight + e.VerticalChange * -1;
            ItemWidth = ItemWidth + e.HorizontalChange * -1;
        }

        private void UpperRight_DragDelta(object sender, DragDeltaEventArgs e)
        {
            Y = Y + e.VerticalChange;

            ItemHeight = ItemHeight + e.VerticalChange * -1;
            ItemWidth = ItemWidth + e.HorizontalChange;
        }

        private void LowerLeft_DragDelta(object sender, DragDeltaEventArgs e)
        {
            X = X + e.HorizontalChange;

            ItemHeight = ItemHeight + e.VerticalChange;
            ItemWidth = ItemWidth + e.HorizontalChange * -1;
        }

        private void LowerRight_DragDelta(object sender, DragDeltaEventArgs e)
        {
            ItemHeight = ItemHeight + e.VerticalChange;
            ItemWidth = ItemWidth + e.HorizontalChange;
        }

        private void Center_DragDelta(object sender, DragDeltaEventArgs e)
        {
            X = X + e.HorizontalChange;
            Y = Y + e.VerticalChange;
        }
    }
于 2013-06-05T18:17:57.233 回答
1

为了在不改变笔画粗细的情况下变换形状,您可以使用Path具有变换几何形状的对象。

以下 XAML 将一个图像和两个路径放在一个画布上。图像由 RenderTransform 缩放和平移。相同的变换也用于Transform两个路径的几何属性。

<Canvas>
    <Image Source="C:\Users\Public\Pictures\Sample Pictures\Desert.jpg">
        <Image.RenderTransform>
            <TransformGroup x:Name="transform">
                <ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
                <TranslateTransform X="100" Y="50"/>
            </TransformGroup>
        </Image.RenderTransform>
    </Image>
    <Path Stroke="Orange" StrokeThickness="2">
        <Path.Data>
            <RectangleGeometry Rect="50,100,100,50"
                               Transform="{Binding ElementName=transform}"/>
        </Path.Data>
    </Path>
    <Path Stroke="Orange" StrokeThickness="2">
        <Path.Data>
            <EllipseGeometry Center="250,100" RadiusX="50" RadiusY="50"
                             Transform="{Binding ElementName=transform}"/>
        </Path.Data>
    </Path>
</Canvas>

您的应用程序现在可以简单地更改transform对象以响应诸如 MouseMove 或 MouseWheel 之类的输入事件。

当涉及到转换 TextBlocks 或其他不应缩放但只能移动到适当位置的元素时,事情会变得有点棘手。

您可以创建一个专门的面板,它能够将这种变换应用于其子元素。这样的 Panel 将定义一个控制子元素位置的附加属性,并将转换应用于该位置而不是子元素的RenderTransformor LayoutTransform

这可能会让您了解如何实施这样的面板:

public class TransformPanel : Panel
{
    public static readonly DependencyProperty TransformProperty =
        DependencyProperty.Register(
            "Transform", typeof(Transform), typeof(TransformPanel),
            new FrameworkPropertyMetadata(Transform.Identity,
                FrameworkPropertyMetadataOptions.AffectsArrange));

    public static readonly DependencyProperty PositionProperty =
        DependencyProperty.RegisterAttached(
            "Position", typeof(Point?), typeof(TransformPanel),
            new PropertyMetadata(PositionPropertyChanged));

    public Transform Transform
    {
        get { return (Transform)GetValue(TransformProperty); }
        set { SetValue(TransformProperty, value); }
    }

    public static Point? GetPosition(UIElement element)
    {
        return (Point?)element.GetValue(PositionProperty);
    }

    public static void SetPosition(UIElement element, Point? value)
    {
        element.SetValue(PositionProperty, value);
    }

    protected override Size MeasureOverride(Size availableSize)
    {
        var infiniteSize = new Size(double.PositiveInfinity,
                                    double.PositiveInfinity);

        foreach (UIElement element in InternalChildren)
        {
            element.Measure(infiniteSize);
        }

        return new Size();
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        foreach (UIElement element in InternalChildren)
        {
            ArrangeElement(element, GetPosition(element));
        }

        return finalSize;
    }

    private void ArrangeElement(UIElement element, Point? position)
    {
        var arrangeRect = new Rect(element.DesiredSize);

        if (position.HasValue && Transform != null)
        {
            arrangeRect.Location = Transform.Transform(position.Value);
        }

        element.Arrange(arrangeRect);
    }

    private static void PositionPropertyChanged(
        DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var element = (UIElement)obj;
        var panel = VisualTreeHelper.GetParent(element) as TransformPanel;

        if (panel != null)
        {
            panel.ArrangeElement(element, (Point?)e.NewValue);
        }
    }
}

它将像这样在 XAML 中使用:

<local:TransformPanel>
    <local:TransformPanel.Transform>
        <TransformGroup>
            <ScaleTransform ScaleX="0.5" ScaleY="0.5" x:Name="scale"/>
            <TranslateTransform X="100"/>
        </TransformGroup>
    </local:TransformPanel.Transform>
    <Image Source="C:\Users\Public\Pictures\Sample Pictures\Desert.jpg"
           RenderTransform="{Binding Transform, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:TransformPanel}}"/>
    <Path Stroke="Orange" StrokeThickness="2">
        <Path.Data>
            <RectangleGeometry Rect="50,100,100,50"
                               Transform="{Binding Transform, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:TransformPanel}}"/>
        </Path.Data>
    </Path>
    <Path Stroke="Orange" StrokeThickness="2">
        <Path.Data>
            <EllipseGeometry Center="250,100" RadiusX="50" RadiusY="50"
                             Transform="{Binding Transform, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:TransformPanel}}"/>
        </Path.Data>
    </Path>
    <TextBlock Text="Rectangle" local:TransformPanel.Position="50,150"/>
    <TextBlock Text="Ellipse" local:TransformPanel.Position="200,150"/>
</local:TransformPanel>
于 2013-06-05T11:15:34.250 回答
1

好吧,这个答案并不能真正帮助 OP 解决他更具体的问题,但总的来说,相机平移、放大和缩小以及环顾四周(使用鼠标)非常困难,所以我只想对我的实现方式提供一些见解相机移动到我的视口场景(如 Blender 或 Unity 等)

这是一个名为 CameraPan 的类,其中包含一些变量,您可以自定义这些变量来编辑放大和缩小距离/速度、平移速度和相机外观灵敏度。在类的底部有一些散列的代码,代表任何场景的基本实现。您首先需要创建一个视口并将其分配给一个“边框”(这是一个可以处理鼠标事件的 UI 元素,因为视口不能),并且还需要创建一个相机以及从 CameraPan 访问的其他几个公共变量班级:

public partial class CameraPan
{
    Point TemporaryMousePosition;
    Point3D PreviousCameraPosition;

    Quaternion QuatX;
    Quaternion PreviousQuatX;
    Quaternion QuatY;
    Quaternion PreviousQuatY;

    private readonly float PanSpeed = 4f;
    private readonly float LookSensitivity = 100f;
    private readonly float ZoomInOutDistance = 1f;
    
    private readonly MainWindow mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();

    public Vector3D LookDirection(PerspectiveCamera camera, Point3D pointToLookAt) // Calculates vector direction between two points (LookAt() method)
    {
        Point3D CameraPosition = camera.Position;

        Vector3D VectorDirection = new Vector3D
            (pointToLookAt.X - CameraPosition.X,
            pointToLookAt.Y - CameraPosition.Y,
            pointToLookAt.Z - CameraPosition.Z);

        return VectorDirection;
    }

    public void PanLookAroundViewport_MouseMove(object sender, MouseEventArgs e) // Panning the viewport using the camera
    {
        if (e.MiddleButton == MouseButtonState.Pressed)
        {               
            Point mousePos = e.GetPosition(sender as Border); // Gets the current mouse pos
            Point3D newCamPos = new Point3D(
                ((-mousePos.X + TemporaryMousePosition.X) / mainWindow.Width * PanSpeed) + PreviousCameraPosition.X,
                ((mousePos.Y - TemporaryMousePosition.Y) / mainWindow.Height * PanSpeed) + PreviousCameraPosition.Y,
                mainWindow.MainCamera.Position.Z); // Calculates the proportional distance to move the camera, 
                                                  //can be increased by changing the variable 'PanSpeed'

            if (Keyboard.IsKeyDown(Key.LeftCtrl)) // Pan viewport
            {                
                mainWindow.MainCamera.Position = newCamPos;
            }
            else // Look around viewport
            {
                double RotY = (e.GetPosition(sender as Label).X - TemporaryMousePosition.X) / mainWindow.Width * LookSensitivity; // MousePosX is the Y axis of a rotation
                double RotX = (e.GetPosition(sender as Label).Y - TemporaryMousePosition.Y) / mainWindow.Height * LookSensitivity; // MousePosY is the X axis of a rotation

                QuatX = Quaternion.Multiply(new Quaternion(new Vector3D(1, 0, 0), -RotX), PreviousQuatX);
                QuatY = Quaternion.Multiply(new Quaternion(new Vector3D(0, 1, 0), -RotY), PreviousQuatY);
                Quaternion QuaternionRotation = Quaternion.Multiply(QuatX, QuatY); // Composite Quaternion between the x rotation and the y rotation
                mainWindow.camRotateTransform.Rotation = new QuaternionRotation3D(QuaternionRotation); // MainCamera.Transform = RotateTransform3D 'camRotateTransform'
            }
        }
    }

    public void MiddleMouseButton_MouseDown(object sender, MouseEventArgs e) // Declares some constants when mouse button 3 is first held down
    {
        if (e.MiddleButton == MouseButtonState.Pressed)
        {
            var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
            TemporaryMousePosition = e.GetPosition(sender as Label);
            PreviousCameraPosition = mainWindow.MainCamera.Position;
            PreviousQuatX = QuatX;
            PreviousQuatY = QuatY;
        }
    }

    public void MouseUp(object sender, MouseEventArgs e)
    {
        mainWindow.CameraCenter = new Point3D(
            mainWindow.CameraCenter.X + mainWindow.MainCamera.Position.X - mainWindow.OriginalCamPosition.X,
            mainWindow.CameraCenter.Y + mainWindow.MainCamera.Position.Y - mainWindow.OriginalCamPosition.Y,
            mainWindow.CameraCenter.Z + mainWindow.MainCamera.Position.Z - mainWindow.OriginalCamPosition.Z);
        // Sets the center of rotation of cam to current mouse position
    } // Declares some constants when mouse button 3 is first let go

    public void ZoomInOutViewport_MouseScroll(object sender, MouseWheelEventArgs e)
    {
        var cam = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault().MainCamera;

        if (e.Delta > 0) // Wheel scrolled forwards - Zoom In
        {
            cam.Position = new Point3D(cam.Position.X, cam.Position.Y, cam.Position.Z - ZoomInOutDistance);
        }
        else // Wheel scrolled forwards - Zoom Out
        {
            cam.Position = new Point3D(cam.Position.X, cam.Position.Y, cam.Position.Z + ZoomInOutDistance);
        }
    }

    // -----CODE IN 'public MainWindow()' STRUCT-----
    /*
        public PerspectiveCamera MainCamera = new PerspectiveCamera();
        public AxisAngleRotation3D MainCamAngle;
        public RotateTransform3D camRotateTransform;
        public Point3D CameraCenter = new Point3D(0, 0, 0);
        public Point3D OriginalCamPosition;

        public MainWindow()
        {
            Viewport3D Viewport = new Viewport3D();
            CameraPan cameraPan = new CameraPan(); // Initialises CameraPan class

            MainCamera.Position = new Point3D(0, 2, 10);
            MainCamera.FieldOfView = 60;
            MainCamera.LookDirection = cameraPan.LookDirection(MainCamera, new Point3D(0, 0, 0));
            // Some custom camera settings

            OriginalCamPosition = MainCamera.Position;
            // Saves the MainCamera's first position

            camRotateTransform = new RotateTransform3D() // Rotation of camera
            {
                CenterX = CameraCenter.X,
                CenterY = CameraCenter.Y,
                CenterZ = CameraCenter.Z,            
            };
            MainCamAngle = new AxisAngleRotation3D() // Rotation value of camRotateTransform
            {
                Axis = new Vector3D(1, 0, 0),
                Angle = 0
            };
            camRotateTransform.Rotation = MainCamAngle;
            MainCamera.Transform = camRotateTransform;

            Border viewportHitBG = new Border() { Width = Width, Height = Height, Background = new SolidColorBrush(Colors.White) };
            // UI Element to detect mouse click events

            viewportHitBG.MouseMove += cameraPan.PanLookAroundViewport_MouseMove;
            viewportHitBG.MouseDown += cameraPan.MiddleMouseButton_MouseDown;
            viewportHitBG.MouseWheel += cameraPan.ZoomInOutViewport_MouseScroll;
            viewportHitBG.MouseUp += cameraPan.MouseUp;
            // Mouse Event handlers

            // Assign the camera to the viewport
            Viewport.Camera = MainCamera;
            
            // Assign Viewport as the child of the UI Element that detects mouse events
            viewportHitBG.Child = Viewport;
        }
     */
}

鼠标事件处理程序根据鼠标和按键事件运行指定的相机平移功能。设置类似于 Unity 视口控件(鼠标中键环顾四周,鼠标中键 + CTRL 平移,滚轮缩放)。

如果需要,这是我对相机平移的完整实现。它包括一个绘制红色立方体的场景,并允许您使用相机在场景中平移:

public partial class MainWindow : Window
{      
    private readonly TranslateTransform3D Position;
    private readonly RotateTransform3D Rotation;
    private readonly AxisAngleRotation3D Transform_Rotation;
    private readonly ScaleTransform3D Scale;

    public PerspectiveCamera MainCamera = new PerspectiveCamera();
    public AxisAngleRotation3D MainCamAngle;
    public RotateTransform3D camRotateTransform;
    public Point3D CameraCenter = new Point3D(0, 0, 0);
    public Point3D OriginalCamPosition;

    public MainWindow()
    {
        InitializeComponent();

        Height = SystemParameters.PrimaryScreenHeight;
        Width = SystemParameters.PrimaryScreenWidth;
        WindowState = WindowState.Maximized;

        #region Initialising 3D Scene Objects

        // Declare scene objects.
        Viewport3D Viewport = new Viewport3D();
        Model3DGroup ModelGroup = new Model3DGroup();
        GeometryModel3D Cube = new GeometryModel3D();
        ModelVisual3D CubeModel = new ModelVisual3D();

        #endregion

        #region UI Grid Objects

        Grid grid = new Grid();

        Slider AngleSlider = new Slider()
        {
            Height = 50,
            VerticalAlignment = VerticalAlignment.Top,
        };
        AngleSlider.ValueChanged += AngleSlider_MouseMove;

        grid.Children.Add(AngleSlider);

        #endregion

        #region Camera Stuff
       
        CameraPan cameraPan = new CameraPan();

        MainCamera.Position = new Point3D(0, 2, 10);
        MainCamera.FieldOfView = 60;
        MainCamera.LookDirection = cameraPan.LookDirection(MainCamera, new Point3D(0, 0, 0));
        OriginalCamPosition = MainCamera.Position;

        camRotateTransform = new RotateTransform3D()
        {
            CenterX = CameraCenter.X,
            CenterY = CameraCenter.Y,
            CenterZ = CameraCenter.Z,            
        };
        MainCamAngle = new AxisAngleRotation3D()
        {
            Axis = new Vector3D(1, 0, 0),
            Angle = 0
        };
        camRotateTransform.Rotation = MainCamAngle;
        MainCamera.Transform = camRotateTransform;

        Border viewportHitBG = new Border() { Width = Width, Height = Height, Background = new SolidColorBrush(Colors.White) };

        viewportHitBG.MouseMove += cameraPan.PanLookAroundViewport_MouseMove;
        viewportHitBG.MouseDown += cameraPan.MiddleMouseButton_MouseDown;
        viewportHitBG.MouseWheel += cameraPan.ZoomInOutViewport_MouseScroll;
        viewportHitBG.MouseUp += cameraPan.MouseUp;

        // Asign the camera to the viewport
        Viewport.Camera = MainCamera;

        #endregion

        #region Directional Lighting

        // Define the lights cast in the scene. Without light, the 3D object cannot
        // be seen. Note: to illuminate an object from additional directions, create
        // additional lights.

        AmbientLight ambientLight = new AmbientLight
        {
            Color = Colors.WhiteSmoke,
        };

        ModelGroup.Children.Add(ambientLight);

        #endregion

        #region Mesh Of Object

        Vector3DCollection Normals = new Vector3DCollection
        {
            new Vector3D(0, 0, 1),
            new Vector3D(0, 0, 1),
            new Vector3D(0, 0, 1),
            new Vector3D(0, 0, 1),
            new Vector3D(0, 0, 1),
            new Vector3D(0, 0, 1)
        };

        PointCollection TextureCoordinates = new PointCollection
        {
            new Point(0, 0),
            new Point(1, 0),
            new Point(1, 1),
            new Point(0, 1),
        };

        Point3DCollection Positions = new Point3DCollection
        {
            new Point3D(-0.5, -0.5, 0.5), // BL FRONT 0
            new Point3D(0.5, -0.5, 0.5), // BR FRONT 1
            new Point3D(0.5, 0.5, 0.5), // TR FRONT 2
            new Point3D(-0.5, 0.5, 0.5), // TL FRONT 3
            new Point3D(-0.5, -0.5, -0.5), // BL BACK 4
            new Point3D(0.5, -0.5, -0.5), // BR BACK 5
            new Point3D(0.5, 0.5, -0.5), // TR BACK 6
            new Point3D(-0.5, 0.5, -0.5) // TL BACK 7
        };

        MeshGeometry3D Faces = new MeshGeometry3D()
        {
            Normals = Normals,
            Positions = Positions,
            TextureCoordinates = TextureCoordinates,
            TriangleIndices = new Int32Collection
            {
                0, 1, 2, 2, 3, 0,
                6, 5, 4, 4, 7, 6,
                4, 0, 3, 3, 7, 4,
                2, 1, 5, 5, 6, 2,
                7, 3, 2, 2, 6, 7,
                1, 0, 4, 4, 5, 1
            },
        };

        // Apply the mesh to the geometry model.
        Cube.Geometry = Faces;

        #endregion

        #region Material Of Object

        // The material specifies the material applied to the 3D object.

        // Define material and apply to the mesh geometries.
        Material myMaterial = new DiffuseMaterial(new SolidColorBrush(Color.FromScRgb(255, 255, 0, 0)));
        Cube.Material = myMaterial;

        #endregion

        #region Transform Of Object

        // Apply a transform to the object. In this sample, a rotation transform is applied, rendering the 3D object rotated.
        Transform_Rotation = new AxisAngleRotation3D()
        {
            Angle = 0,
            Axis = new Vector3D(0, 0, 0)
        };
        Position = new TranslateTransform3D
        {
            OffsetX = 0,
            OffsetY = 0,
            OffsetZ = 0
        };
        Scale = new ScaleTransform3D
        {
            ScaleX = 1,
            ScaleY = 1,
            ScaleZ = 1
        };

        Rotation = new RotateTransform3D
        {
            Rotation = Transform_Rotation
        };

        Transform3DGroup transformGroup = new Transform3DGroup();
        transformGroup.Children.Add(Rotation);
        transformGroup.Children.Add(Scale);
        transformGroup.Children.Add(Position);

        Cube.Transform = transformGroup;

        #endregion

        #region Adding Children To Groups And Parents

        // Add the geometry model to the model group.
        ModelGroup.Children.Add(Cube);
        CubeModel.Content = ModelGroup;
        Viewport.Children.Add(CubeModel);
        viewportHitBG.Child = Viewport;
        grid.Children.Add(viewportHitBG);

        #endregion

        Content = grid;
    }

    private void AngleSlider_MouseMove(object sender, RoutedEventArgs e)
    {
        Slider slider = (Slider)sender;
        Transform_Rotation.Angle = slider.Value * 36;
        Transform_Rotation.Axis = new Vector3D(0, 1, 0);
        Scale.ScaleX = slider.Value / 5; Scale.ScaleY = slider.Value / 5; Scale.ScaleZ = slider.Value / 5;
        Position.OffsetX = slider.Value / 5; Position.OffsetY = slider.Value / 5; Position.OffsetZ = slider.Value / 5;
    }
}

public partial class CameraPan
{
    Point TemporaryMousePosition;
    Point3D PreviousCameraPosition;

    Quaternion QuatX;
    Quaternion PreviousQuatX;
    Quaternion QuatY;
    Quaternion PreviousQuatY;

    private readonly float PanSpeed = 4f;
    private readonly float LookSensitivity = 100f;
    private readonly float ZoomInOutDistance = 1f;
    
    private readonly MainWindow mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();

    public Vector3D LookDirection(PerspectiveCamera camera, Point3D pointToLookAt) // Calculates vector direction between two points (LookAt() method)
    {
        Point3D CameraPosition = camera.Position;

        Vector3D VectorDirection = new Vector3D
            (pointToLookAt.X - CameraPosition.X,
            pointToLookAt.Y - CameraPosition.Y,
            pointToLookAt.Z - CameraPosition.Z);

        return VectorDirection;
    }

    public void PanLookAroundViewport_MouseMove(object sender, MouseEventArgs e) // Panning the viewport using the camera
    {
        if (e.MiddleButton == MouseButtonState.Pressed)
        {               
            Point mousePos = e.GetPosition(sender as Border); // Gets the current mouse pos
            Point3D newCamPos = new Point3D(
                ((-mousePos.X + TemporaryMousePosition.X) / mainWindow.Width * PanSpeed) + PreviousCameraPosition.X,
                ((mousePos.Y - TemporaryMousePosition.Y) / mainWindow.Height * PanSpeed) + PreviousCameraPosition.Y,
                mainWindow.MainCamera.Position.Z); // Calculates the proportional distance to move the camera, 
                                                  //can be increased by changing the variable 'PanSpeed'

            if (Keyboard.IsKeyDown(Key.LeftCtrl)) // Pan viewport
            {                
                mainWindow.MainCamera.Position = newCamPos;
            }
            else // Look around viewport
            {
                double RotY = (e.GetPosition(sender as Label).X - TemporaryMousePosition.X) / mainWindow.Width * LookSensitivity; // MousePosX is the Y axis of a rotation
                double RotX = (e.GetPosition(sender as Label).Y - TemporaryMousePosition.Y) / mainWindow.Height * LookSensitivity; // MousePosY is the X axis of a rotation

                QuatX = Quaternion.Multiply(new Quaternion(new Vector3D(1, 0, 0), -RotX), PreviousQuatX);
                QuatY = Quaternion.Multiply(new Quaternion(new Vector3D(0, 1, 0), -RotY), PreviousQuatY);
                Quaternion QuaternionRotation = Quaternion.Multiply(QuatX, QuatY); // Composite Quaternion between the x rotation and the y rotation
                mainWindow.camRotateTransform.Rotation = new QuaternionRotation3D(QuaternionRotation); // MainCamera.Transform = RotateTransform3D 'camRotateTransform'
            }
        }
    }

    public void MiddleMouseButton_MouseDown(object sender, MouseEventArgs e) // Declares some constants when mouse button 3 is first held down
    {
        if (e.MiddleButton == MouseButtonState.Pressed)
        {
            var mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
            TemporaryMousePosition = e.GetPosition(sender as Label);
            PreviousCameraPosition = mainWindow.MainCamera.Position;
            PreviousQuatX = QuatX;
            PreviousQuatY = QuatY;
        }
    }

    public void MouseUp(object sender, MouseEventArgs e)
    {
        mainWindow.CameraCenter = new Point3D(
            mainWindow.CameraCenter.X + mainWindow.MainCamera.Position.X - mainWindow.OriginalCamPosition.X,
            mainWindow.CameraCenter.Y + mainWindow.MainCamera.Position.Y - mainWindow.OriginalCamPosition.Y,
            mainWindow.CameraCenter.Z + mainWindow.MainCamera.Position.Z - mainWindow.OriginalCamPosition.Z);
        // Sets the center of rotation of cam to current mouse position
    } // Declares some constants when mouse button 3 is first let go

    public void ZoomInOutViewport_MouseScroll(object sender, MouseWheelEventArgs e)
    {
        var cam = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault().MainCamera;

        if (e.Delta > 0) // Wheel scrolled forwards - Zoom In
        {
            cam.Position = new Point3D(cam.Position.X, cam.Position.Y, cam.Position.Z - ZoomInOutDistance);
        }
        else // Wheel scrolled forwards - Zoom Out
        {
            cam.Position = new Point3D(cam.Position.X, cam.Position.Y, cam.Position.Z + ZoomInOutDistance);
        }
    }

    // -----CODE IN 'public MainWindow()' STRUCT-----
    /*
        public PerspectiveCamera MainCamera = new PerspectiveCamera();
        public AxisAngleRotation3D MainCamAngle;
        public RotateTransform3D camRotateTransform;
        public Point3D CameraCenter = new Point3D(0, 0, 0);
        public Point3D OriginalCamPosition;

        public MainWindow()
        {
            Viewport3D Viewport = new Viewport3D();
            CameraPan cameraPan = new CameraPan(); // Initialises CameraPan class

            MainCamera.Position = new Point3D(0, 2, 10);
            MainCamera.FieldOfView = 60;
            MainCamera.LookDirection = cameraPan.LookDirection(MainCamera, new Point3D(0, 0, 0));
            // Some custom camera settings

            OriginalCamPosition = MainCamera.Position;
            // Saves the MainCamera's first position

            camRotateTransform = new RotateTransform3D() // Rotation of camera
            {
                CenterX = CameraCenter.X,
                CenterY = CameraCenter.Y,
                CenterZ = CameraCenter.Z,            
            };
            MainCamAngle = new AxisAngleRotation3D() // Rotation value of camRotateTransform
            {
                Axis = new Vector3D(1, 0, 0),
                Angle = 0
            };
            camRotateTransform.Rotation = MainCamAngle;
            MainCamera.Transform = camRotateTransform;

            Border viewportHitBG = new Border() { Width = Width, Height = Height, Background = new SolidColorBrush(Colors.White) };
            // UI Element to detect mouse click events

            viewportHitBG.MouseMove += cameraPan.PanLookAroundViewport_MouseMove;
            viewportHitBG.MouseDown += cameraPan.MiddleMouseButton_MouseDown;
            viewportHitBG.MouseWheel += cameraPan.ZoomInOutViewport_MouseScroll;
            viewportHitBG.MouseUp += cameraPan.MouseUp;
            // Mouse Event handlers

            // Assign the camera to the viewport
            Viewport.Camera = MainCamera;
            
            // Assign Viewport as the child of the UI Element that detects mouse events
            viewportHitBG.Child = Viewport;
        }
     */
}

我希望它可以帮助将来的人!

于 2020-10-17T07:34:19.487 回答