Search code examples
c#wpfxamlcanvasrendertransform

WPF: Access transformed Canvas coordinates in code behind


Is there a way in WPF to access the ScaleTransform Parameter of a Canvas Panel (which is created in the xaml File) in the Code Behind File?

Use Case Example:

I want to position a list of items inside a scaled Canvas like so:

<ItemsControl MouseMove="Control_MouseMove">
    <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                    <Canvas>
                        <Canvas.RenderTransform>
                              <TransformGroup>
                                   <ScaleTransform>
                                        <ScaleTransform.ScaleX />
                                   </ScaleTransform>
                              </TransformGroup>
                        </Canvas.RenderTransform>
                    </Canvas>
             </ItemsPanelTemplate>
     </ItemsControl.ItemsPanel>
</ItemsControl>

I also want to display the current position of my items inside the Canvas at Mouse over with a Pop-up. I am currently trying it this way:

public void Control_MouseMove(object sender, MouseEventArgs e) {
    if (!this.Popup.IsOpen)
        this.Popup.IsOpen = true;
    var mousePosition = e.GetPosition(this.SwLane);
    this.Popup.HorizontalOffset = mousePosition.X + 10;
    this.Popup.VerticalOffset = mousePosition.Y - 20;
    this.PopupContent.Text = System.Convert.ToString(mousePosition.X);
}

What I get is the canvas X coordinate inside the Popup (which makes sense to me). However, I would like to display the scale transformed "coordinates" of the canvas.

Is there a way to access the ScaleTransform Parameter in Code Behind so that I can visualise my transformed item position? Or should I do it in a different way? Thanks.


Solution

  • You could set an OnInitialized Handler where you build your transform in code-behind, save its constituent ScaleTransform as a private property and then assign it there as well after your window has initialized

    Something crudely like this:

    <Window x:Class="WpfApp4.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Background="White"
            MouseLeftButtonDown="MainWindow_OnMouseLeftButtonDown"
            Initialized="MainWindow_OnInitialized"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <ItemsControl x:Name="MyItems" >
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
    
            <Rectangle Width="100" Height="200" Stroke="Red" StrokeThickness="5"/>
        </Grid>
    </Window>
    
    
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private TransformGroup MyTransform { get; set; }
        private ScaleTransform SceneScale { get; set; }
    
    
        private void MainWindow_OnInitialized(object? sender, EventArgs e)
        {
            MyTransform = new TransformGroup();
            SceneScale = new ScaleTransform(1.0, 1.0, 0, 0);
            MyTransform.Children.Add(SceneScale);
            MyItems.RenderTransform = MyTransform;
        }
    }
    

    Now you could add a MouseLeftButtonDown Handler to scale things, for example:

        private void MainWindow_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SceneScale.ScaleX *= 1.1;
            SceneScale.ScaleY *= 1.1;
        }
    

    For the coordinate thing, expose the current coordinates as properties. Update them when the mouse moves. Bind the UI to them

    This might work better if you packaged it all in a control