【问题标题】:How to apply Render Transform to mouse position如何将渲染变换应用于鼠标位置
【发布时间】:2014-02-02 07:10:19
【问题描述】:

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

<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,以避免手动调整坐标?

【问题讨论】:

    标签: c# wpf xaml wpf-controls


    【解决方案1】:
    1. CenterX=".5" CenterY=".5" 中的ScaleTransform 是不必要的。它所做的只是添加一个微小的平移变换(半像素)。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多