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

标签 c# wpf xaml wpf-controls

我正在制作绘图应用程序。因为我想要与数学图相同的行为,所以我将以下转换应用于带有数据点的 Canvas :

<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>

到目前为止一切顺利。问题在于向绘图添加点。因为鼠标单击事件返回的是窗口坐标中的位置,所以没有用。该点添加在错误的位置,因为它在添加后发生了转换。

例如 Canvas 高 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. CenterX=".5"CenterY=".5"ScaleTransform 中是不必要的。它所做的只是添加一个微小的平移变换(半像素)。

  2. 要从转换后的位置获取源位置,您需要使用逆变换(TransformInverse 属性)。这就是 X-30 错误的来源。

  3. 要更改变换原点,您需要先减去一半 Canvas 大小,然后进行变换,然后添加一半 Canvas 大小。

    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);
        }
    }
}

关于c# - 如何将渲染变换应用于鼠标位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21501609/

相关文章:

c# - 从选定项目中获取值(value)的麻烦

c# - 如何在两个 MVVM 对之间进行通信?

c# - WPF 中的窗口显示事件?

xaml - Xamarin.Forms : How to set 'GestureRecognizers' in style

wpf - 在 WPF 中实现向导进度控制

c# - 如何在 MailKit 中将邮件标记为已读

c# - WCF 数据服务 5.6 快速入门

c# - 在 C# 中从 Azure 的 keyVault 获取 secret

c# - Encoding.GetEncoding 无法在 UWP 应用程序中工作

c# - 如何绑定(bind)到 View 中但不在 View 模型中的属性?