4

I have a Window with a ScrollViewer and inside the ScrollViewer there is a Rectangle. Now I added code to drag the Rectangle which works fine. But I have no idea how to show the Scrollbars when the Rectangle is moved outside the view. I thought this will happen automatically which is not the case?

Here is my XAML:

<Window x:Class="WpfApp4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp4"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <ScrollViewer Name="_scrollViewer" CanContentScroll="True" 
                  HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
        <Rectangle Name="_myRect" Width="100" Height="100" Fill="Blue"/>
    </ScrollViewer>
</Window>

And the code behind:

public partial class MainWindow : Window
{
    private Point _origin;
    private Point _start;
    private ScaleTransform _scaleTransform = new ScaleTransform();
    private TranslateTransform _translateTransform = new TranslateTransform();

    public MainWindow()
    {
        InitializeComponent();

        var group = new TransformGroup();
        group.Children.Add(_scaleTransform);
        group.Children.Add(_translateTransform);  
        _myRect.RenderTransform = group;

        // Hook up events
        _myRect.MouseLeftButtonDown += _myRect_MouseLeftButtonDown;
        _myRect.MouseLeftButtonUp += _myRect_MouseLeftButtonUp;
        _myRect.MouseMove += _myRect_MouseMove;
    }

    private void _myRect_MouseMove(object sender, MouseEventArgs e)
    {
        if (_myRect.IsMouseCaptured)
        {
            Vector v = _start - e.GetPosition(this);
            _translateTransform.X = _origin.X - v.X;
            _translateTransform.Y = _origin.Y - v.Y;
        }
    }

    private void _myRect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        _myRect.ReleaseMouseCapture();
        this.Cursor = Cursors.Arrow;
    }

    private void _myRect_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        _start = e.GetPosition(this);
        _origin = new Point(_translateTransform.X, _translateTransform.Y);
        Cursor = Cursors.Hand;
        _myRect.CaptureMouse();
    }
}

[UPDATED]: So based on the input I got I change XAML and the code behind to the following - but still no scrollbars?

<Window x:Class="WpfApp4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp4"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <ScrollViewer Name="_scrollViewer" CanContentScroll="True" 
                  HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
        <Canvas Name="_myCanvas">
            <Rectangle Name="_myRect" Width="100" Height="100" Fill="Blue" Canvas.Left="305" Canvas.Top="129"/>
        </Canvas>
    </ScrollViewer>
</Window>

Code Behind:

public partial class MainWindow : Window
{
    private Point _start;

    public MainWindow()
    {
        InitializeComponent();
        // Hook up events
        _myRect.MouseLeftButtonDown += _myRect_MouseLeftButtonDown;
        _myRect.MouseLeftButtonUp += _myRect_MouseLeftButtonUp;
        _myRect.MouseMove += _myRect_MouseMove;
    }

    private void _myRect_MouseMove(object sender, MouseEventArgs e)
    {
        if (_myRect.IsMouseCaptured)
        {
            var canvasRelativePosition = e.GetPosition(_myCanvas);
            Debug.WriteLine($"New Position: {canvasRelativePosition}");
            Canvas.SetTop(_myRect, canvasRelativePosition.Y - _start.Y);
            Canvas.SetLeft(_myRect, canvasRelativePosition.X - _start.X);
        }
    }

    private void _myRect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        _myRect.ReleaseMouseCapture();
        this.Cursor = Cursors.Arrow;
    }

    private void _myRect_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        _start = e.GetPosition(_myRect);
        Debug.WriteLine($"Start Position: {_start}");
        Cursor = Cursors.Hand;
        _myRect.CaptureMouse();
    }
}
Franz Gsell
  • 1,425
  • 2
  • 13
  • 22
  • Put a Canvas into the ScrollViewer and add the Rectangle to the Canvas. Then move the Rectangle by setting its Canvas.Left and Canvas.Top properties. Scale it by setting its Width and Height. – Clemens Jun 04 '19 at 09:37
  • @Clemens: Updated my code above but still see no scrollbars – Franz Gsell Jun 04 '19 at 12:20
  • You may also want to set the Canvas' Width and Height. – Clemens Jun 04 '19 at 12:25

1 Answers1

2

If you want your transform to affect layout, you have to use LayoutTransofrm. RenderTransform only changes apperance.

Any transformations associated with an elements LayoutTransform property will have an impact on the subsequent Measure and Arrange steps. Whereas a RenderTransform will not have any impact on the layout process and will only effect rendering.

Read more here.

However, LayoutTransform ignores TranslateTransform

LayoutTransform ignores TranslateTransform operations. This is because the layout system behavior for child elements of a FrameworkElement auto-corrects any offsets to the position of a scaled or rotated element into the layout and coordinate system of the parent element.

Read more here

All that means, to achieve moving your element, you can not use Transforms. You could try to change position of your element by hand (Margin, Canvas.Left/Right or other ideas).

Jonatan Dragon
  • 4,675
  • 3
  • 30
  • 38
  • First of all thanks for your answer. Could you give me an example what you mean by "change position of your element by hand"? – Franz Gsell Jun 04 '19 at 09:21
  • You could use canvas like here: https://stackoverflow.com/questions/1123101/changing-position-of-an-element-programmatically-in-wpf In this case, you would also have to remember about changing canvas size, when rectangle is about to be moved outside it's bounds. – Jonatan Dragon Jun 04 '19 at 09:40
  • There were 2 problems to deal with. 1. First thing was moving your object - we managed it. 2. Second one is the size of your Canvas. Even when you move the rectangle, the Canvas size is still the same. Scrolls apperance depends on Canvas size (in this case). You could change it programatically or take a look at this solution with Grid: https://stackoverflow.com/questions/855334/wpf-how-to-make-canvas-auto-resize – Jonatan Dragon Jun 04 '19 at 13:24