如何将渲染变换应用于鼠标位置

4

我正在制作一个绘图应用程序。为了使它的行为与数学图形相同,我将下面的转换应用于带有数据点的画布:

<UserControl.Resources>
    <TransformGroup x:Key="CanvasTransform">
        <TranslateTransform X="30" Y="30"/>
        <ScaleTransform ScaleX="1" ScaleY="-1" CenterX=".5" CenterY=".5" />
    </TransformGroup>
</UserControl.Resources>

以下是使用的转换方法:

<ListBox>
    <ListBox.ItemsPanel>
         <ItemsPanelTemplate>
             <Canvas>
                <Canvas.RenderTransformOrigin>
                    <Point X="0.5" Y="0.5"/>
                </Canvas.RenderTransformOrigin>
                <Canvas.RenderTransform>
                    <Binding Source="{StaticResource CanvasTransform}"/>
                </Canvas.RenderTransform>
             </Canvas>
         </ItemsPanelTemplate>
    </ListBox.ItemsPanel>

到目前为止一切都很好。问题在于如何向图表中添加点。因为鼠标单击事件返回的是窗口坐标,所以没有用。添加的点会出现在错误的位置,因为它是在添加后进行转换的。
例如,画布高度为400个单位。我在左上角单击鼠标,鼠标位置为[X=10, Y=10],这个点被添加到图表中并呈现出来。然后呈现变换使用[10,10]点并计算它的新位置:[X=40,Y=360](窗口坐标)。
这意味着我在顶部单击,点会出现在底部。这实际上是正确的行为。
我的问题是,如何在存储点之前手动应用渲染变换,使点出现在鼠标下方。
到目前为止,我尝试了以下方法:
var trans = Resources["CanvasTransform"] as TransformGroup;
var mouse = e.GetPosition(this); // mouse position relative to canvas
var newPoint = trans.Transform(mouse);

但是在这次变换后,newPoint的坐标变为[40,-39]。我知道结果为什么会是这样。变换的原点是[0,0],平移量为29,可能是由于四舍五入误差引起的。
现在,我可以取出这个新点并手动更改值-从X坐标中减去30,然后将Canvas.ActualHeight加到Y坐标中,这将修正位置。
但这有什么意义呢?
我的问题是:是否可能以与渲染器相同的方式应用 RenderTransform ,以避免手动处理坐标?
1个回答

1
  1. ScaleTransform 中的 CenterX=".5" CenterY=".5" 是不必要的。 它只会添加一个微小的平移变换(半像素)。

  2. 要从变换后的位置获取源位置,需要使用反向变换(TransformInverse 属性)。这就是 X-30 错误的根源所在。

  3. 要更改变换原点,需要首先减去一半的画布大小,然后进行变换,最后再加上一半的画布大小。

    var origin = new Point(lstItems.ActualWidth / 2, lstItems.ActualHeight / 2);
    var transform = ((TransformGroup)Resources["CanvasTransform"]).Clone();
    transform.Children.Insert(0, new TranslateTransform(-origin.X, -origin.Y));
    transform.Children.Add(new TranslateTransform(origin.X, origin.Y));
    _transform = transform.Inverse;
    

完整示例:

MainWindow.xaml

<Window x:Class="So21501609WpfMouseRenderTransform.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    <Control.Resources>
        <TransformGroup x:Key="CanvasTransform">
            <TranslateTransform X="30" Y="30"/>
            <ScaleTransform ScaleX="1" ScaleY="-1" CenterX=".5" CenterY=".5"/>
        </TransformGroup>
        <Style TargetType="TextBlock">
            <Setter Property="Background" Value="SkyBlue"/>
        </Style>
    </Control.Resources>
    <ItemsControl x:Name="lstItems" MouseDown="LstItems_OnMouseDown" Width="400" Height="400" Background="Transparent">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas>
                    <Canvas.RenderTransformOrigin>
                        <Point X="0.5" Y="0.5"/>
                    </Canvas.RenderTransformOrigin>
                    <Canvas.RenderTransform>
                        <Binding Source="{StaticResource CanvasTransform}"/>
                    </Canvas.RenderTransform>
                </Canvas>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.Items>
            <TextBlock Canvas.Left="10" Canvas.Top="10" Text="10 10"/>
            <TextBlock Canvas.Left="10" Canvas.Top="300" Text="10 300"/>
            <TextBlock Canvas.Left="300" Canvas.Top="300" Text="300 300"/>
            <TextBlock Canvas.Left="300" Canvas.Top="10" Text="300 10"/>
        </ItemsControl.Items>
    </ItemsControl>
</Window>

MainWindow.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace So21501609WpfMouseRenderTransform
{
    public partial class MainWindow
    {
        private GeneralTransform _transform;

        public MainWindow ()
        {
            InitializeComponent();
            Loaded += OnLoaded;
        }

        private void OnLoaded (object sender, RoutedEventArgs routedEventArgs)
        {
            var origin = new Point(lstItems.ActualWidth / 2, lstItems.ActualHeight / 2);
            var transform = ((TransformGroup)Resources["CanvasTransform"]).Clone();
            transform.Children.Insert(0, new TranslateTransform(-origin.X, -origin.Y));
            transform.Children.Add(new TranslateTransform(origin.X, origin.Y));
            _transform = transform.Inverse;
        }

        private void LstItems_OnMouseDown (object sender, MouseButtonEventArgs e)
        {
            Point pos = _transform.Transform(e.GetPosition(lstItems));

            var item = new TextBlock { Text = pos.ToString() };
            Canvas.SetLeft(item, pos.X);
            Canvas.SetTop(item, pos.Y);
            lstItems.Items.Add(item);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接