Увеличьте положение мыши внутри прокрутки - PullRequest
1 голос
/ 13 марта 2020

Итак, я сделал ZoomControl, который использует границу и изображение

<UserControl x:Class="ImageViewer.Controls.ZoomControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ImageViewer.Controls"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Border x:Name="BorderImage">
        <Image HorizontalAlignment="Left" VerticalAlignment="Top" x:Name="RenderingImage" RenderTransformOrigin="0,0"  Stretch="None" Source="{Binding}" RenderTransform="{Binding}"/>
    </Border>

Этот элемент управления вложен в ScrollViewer

   <ScrollViewer x:Name="ScollViewerImage"  Grid.Column="2" Grid.Row="0" Grid.RowSpan="3" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">            
            <Controls:ZoomControl x:Name="RenderingImage" ViewModel="{Binding}" ClipToBounds="True"></Controls:ZoomControl>
        </ScrollViewer>

Итак, я построил масштабирование для функция позиционирования мыши

private void DoZoom(double deltaZoom, Point mousePosition)
        {
            var scaleTransform = GetScaleTransform();
            var translateTransform = GetTranslateTransform();

            if (!(deltaZoom > 0) && (scaleTransform.ScaleX < .4 || scaleTransform.ScaleY < .4))
                return;

            var mousePositionAfterScaleX = mousePosition.X * deltaZoom;
            var mousePositionAfterScaleY = mousePosition.Y * deltaZoom;

            var newMousePositionX = mousePosition.X - mousePositionAfterScaleX;
            var newMousePositionY = mousePosition.Y - mousePositionAfterScaleY;

            var newTranslateX = newMousePositionX - mousePosition.X;
            var newTranslateY = newMousePositionY - mousePosition.Y;

            var translateX = newTranslateX + translateTransform.X;
            var translateY = newTranslateY + translateTransform.Y;

            scaleTransform.ScaleX += deltaZoom;
            scaleTransform.ScaleY += deltaZoom;

            _currentZoom = scaleTransform.ScaleX;   

            ChangeTranslateTransofrm(translateX - overflowWidth, translateY - overflowHeight);
            UpdateScaleTransfromValue();
        }

Это прекрасно работает, если изображение соответствует размеру ScrollViewer. Но после того, как изображение больше, чем элемент управления ScrollVIewer (он показывает полосы прокрутки), и всякий раз, когда я увеличиваю масштаб, положение моей мыши больше не находится в той же точке. Я уверен, что это как-то связано с тем, что полоса прокрутки видна, но я не могу понять, как заставить мышь придерживаться той же позиции после того, как полоса прокрутки станет видимой.

1 Ответ

1 голос
/ 18 марта 2020

Итак, после 2 дней попыток и ошибок я нашел решение для моей конкретной проблемы c. Прежде всего, есть две основные проблемы:

  1. Полосы прокрутки включены автоматически (они скрываются / отображаются во время выполнения в зависимости от width.height дочернего элемента внутри ScrollViewer ). Из-за этого изображение внутри изменяется, это трансформирующее преобразование.
  2. Положение полосы прокрутки (смещение) не сохраняется на той же позиции после действия увеличения / уменьшения (дочерний элемент становится больше или меньше).

Первая проблема была решена добавлением двух границ в ScrollViewer, которые будут занимать пространство, необходимое для полосы прокрутки, до тех пор, пока полосы прокрутки не видны.

<ScrollViewer x:Name="ScollViewerImage" Grid.Column="2" Grid.Row="0" Grid.RowSpan="3" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" >
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Border Grid.Column="1" Width="{x:Static SystemParameters.VerticalScrollBarWidth}">
                <Border.Style>
                    <Style TargetType="Border">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding ComputedVerticalScrollBarVisibility, ElementName=ScollViewerImage}"
                                         Value="Visible">
                                <Setter Property="Visibility" Value="Collapsed"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
            </Border>
            <Border Grid.Row="1" Width="{x:Static SystemParameters.HorizontalScrollBarHeight}">
                <Border.Style>
                    <Style TargetType="Border">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding ComputedHorizontalScrollBarVisibility, ElementName=ScollViewerImage}"
                                         Value="Visible">
                                <Setter Property="Visibility" Value="Collapsed"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
            </Border>
            <Controls:ZoomControl Grid.Column="0" Grid.Row="0" x:Name="RenderingImage" ViewModel="{Binding}" ClipToBounds="True"></Controls:ZoomControl>
        </Grid>

    </ScrollViewer>

Для второй проблемы, когда выполняется прокрутка, я сохраняю смещение. Я также сохраняю размер масштабирования (ScrollableHeight и Scorllable Width) и когда они меняются (увеличение / уменьшение было сделано). Я перемещаю полосу прокрутки в желаемое положение.

        private void SetScrollbarOffset(ScrollViewer scrollViewer, double verticalChange, double horizontalChange)
        {
            // after each move of the scrollbar we save the current offsets
            _currentVerticalOffset = scrollViewer.VerticalOffset;
            _currentHorizonalOffset = scrollViewer.HorizontalOffset;

            // we check if there was a zoom in/out perfomed
            if (_scrollableHeight != scrollViewer.ScrollableHeight)
            {
                // we save the current zoom in/out scrollable height
                _scrollableHeight = scrollViewer.ScrollableHeight;
                // we move the scrollbar to the position needed to persist the mouse under the same point in the image
                scrollViewer.ScrollToVerticalOffset(_currentVerticalOffset - verticalChange);
            }

            if (_scrollableWidth != scrollViewer.ScrollableWidth)
            {
                _scrollableWidth = scrollViewer.ScrollableWidth;
                scrollViewer.ScrollToHorizontalOffset(_currentHorizonalOffset - horizontalChange);
            }
        }

Этот последний метод вызывается из SizeChangedEvent

        public void SizeChange(ScrollChangedEventArgs e)
        {
            var scrollViewer = (e.OriginalSource as ScrollViewer);

            SetScrollbarOffset(scrollViewer, e.VerticalChange, e.HorizontalOffset);
        }
...