Ограничьте количество панорамирования, чтобы изображение не выходило за пределы окна.

ВСТУПЛЕНИЕ:

Я экспортировал SVG как XAMLи превратился в холст с огромным количеством дорожек.

После создания этого основного содержимого Canvas в простом автоматически сгенерированном окне изображение было обрезано, поскольку оно было больше, чем клиентский прямоугольник главного окна.

Я решил эту проблему с помощью панорамирования.

Так как я планирую возиться с масштабированием позже, я добавил манекен ScaleTransform в XAML холста и изменили его RenderTransformOrigin на 0.5 , 0.5 (так что я могу увеличить изображение вокруг его центра).

Я хотел, чтобы изображение было центрировано при загрузке окна, поэтому я поместил этот Canvas внутри Viewbox, который, кажется, работает нормально, после настройки нескольких свойств.

ПРОБЛЕМА:

Поскольку я изменил RenderTransformOrigin для Canvas, я не могу понять математику, которая позволит мне ограничить количество панорамирования.

Несмотря на то, что Viewbox является содержимым главного окна, я не могу получить размеры клиентской области главного окна (Viewbox изменяет размеры в соответствии с его содержимым). Это делает мою задачу еще более сложной.

МОИ УСИЛИЯ ДЛЯ РЕШЕНИЯ ЭТОГО:

Кажется, нет другого способа получить прямоугольник клиента главного окна, кроме как с использованием P/Invoke а также GetClientRect WinAPI звонок.

Был еще один хак с запросом SystemParametersInfo для не-клиентских метрик, таких как границы и заголовок, но это оценка, которая может потерпеть неудачу из-за примененных тем и подобных.

Я не пытался использовать P/Invoke потому что я просто отказываюсь сделать это в данный момент. Должно быть лучшее решение, чем это! Этот путь должен быть моим последним выбором, и тот, который я выберу из чистого отчаяния.

Я пытался найти альтернативный подход самостоятельно, но безуспешно.

Я пытался использовать TransformToAncestor(...).Transformtobounds(..) для моих расчетов, но это тоже не удалось.

ВОПРОС:

Как ограничить панорамирование, чтобы изображение не исчезало из главного окна, когда пользователь перетаскивает слишком много?

Я приму альтернативные решения. Код, который был представлен, был попыткой неопытного начинающего, самоучки, поэтому я принимаю конструктивную критику и советы.

РЕЛЕВАНТНАЯ ИНФОРМАЦИЯ:

Чтобы этот пост был как можно более коротким, я даю ниже только соответствующие фрагменты XAML и "код позади".

Полный код наклеен на Pastebin, и его можно найти в самом низу этого поста.

Соответствующие фрагменты XAML:

<Window  x:Class="TestZaMapu.MainWindow" 
        Name="GlavniProzor" 
        WindowStartupLocation="CenterScreen"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" 
        Height="350" 
        Width="525" 
        MouseLeftButtonDown="GlavniProzor_MouseLeftButtonDown" 
        MouseLeftButtonUp="GlavniProzor_MouseLeftButtonUp" 
        MouseMove="GlavniProzor_MouseMove" 
        LostMouseCapture="GlavniProzor_LostMouseCapture">
    <Viewbox 
        Name="surface"
        Stretch="UniformToFill" 
        HorizontalAlignment="Center"
        VerticalAlignment="Center">
        <Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                Background="Blue" 
                x:Name="svg4306" 
                Width="494.44705" 
                Height="510.55356"
                <!-- needed in the future, for zooming around center-->
                RenderTransformOrigin="0.5, 0.5">
            <!--Unknown tag: metadata-->
            <Canvas.RenderTransform>
                <TransformGroup>
                    <!-- I intend to experiment with scaling in the future, so I added the below part -->
                    <ScaleTransform ScaleX="1" ScaleY="1"/>
                    <!-- I have used dependency properties for translation -->
                    <TranslateTransform X="{Binding TranslationFactorX, ElementName=GlavniProzor, Mode=TwoWay}" Y="{Binding TranslationFactorY, ElementName=GlavniProzor, Mode=TwoWay}"/>
                </TransformGroup>
            </Canvas.RenderTransform>
        <!-- Bunch of Path objects, omitted for brevity-->
        </Canvas>
    </Viewbox>
</Window>

Соответствующий код за фрагментами:

namespace TestZaMapu
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            TranslationFactorX = 0;
            TranslationFactorY = 0;
            isPanning = false;

        }

        // panning variables
        private Point ptOldPosition;
        private bool isPanning ;

        // dependency properties for translation along X and Y axes

        public static readonly DependencyProperty TranslateX =
            DependencyProperty.Register("TranslationFactorX", typeof(double), typeof(MainWindow));

        public static readonly DependencyProperty TranslateY =
            DependencyProperty.Register("TranslationFactorY", typeof(double), typeof(MainWindow));

        public double TranslationFactorX
        {
            get { return (double)GetValue(TranslateX); }
            set { SetValue(TranslateX, value); }
        }

        public double TranslationFactorY
        {
            get { return (double)GetValue(TranslateY); }
            set { SetValue(TranslateY, value); }
        }

        // mouse event handlers for panning

        private void GlavniProzor_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            if (isPanning )
                return;
            isPanning = this.CaptureMouse(); 
            ptOldPosition = e.GetPosition(this);
        }

        private void GlavniProzor_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonUp(e);

            if (this.IsMouseCaptured)
            {
                this.ReleaseMouseCapture();
                isPanning = false;
            }
        }

        private void GlavniProzor_MouseMove(object sender, MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (isPanning )
            {
                Point ptNewPosition = e.GetPosition(this);

                if (ptNewPosition != ptOldPosition)
                {
                    Vector direction = ptOldPosition - ptNewPosition;

                    direction.X = TranslationFactorX - direction.X;
                    direction.Y = TranslationFactorY - direction.Y;

                    TranslationFactorX = direction.X;
                    TranslationFactorY = direction.Y;

                    ptOldPosition = ptNewPosition;
                }
            }
        }

        private void GlavniProzor_LostMouseCapture(object sender, MouseEventArgs e)
        {
            isPanning = false;
            this.OnLostMouseCapture(e);
        }
    }
}

Вот полный XAML, а вот полный "код позади".

2 ответа

Это звучит преступно просто, но если ваша цель состоит в том, чтобы не дать вашему вектору срезаться за пределами назначенного ему пространства, почему бы вам просто не изменить Stretch="UniformToFill" вашего Viewbox для Stretch="Uniform"?

Я действительно не читал полный вопрос (я думаю, что никто не будет), но вот код, который я использую в библиотеке графиков для ограничения панорамирования. Это математика. Теперь добавьте это к вашему делу. Совет: в следующий раз введите только соответствующий код, чтобы мы могли дать лучший ответ.

Я вызываю этот метод, когда панорамирование заканчивается (в моем случае событие "вверх")

Если это помогает, то это библиотека ( https://github.com/beto-rodriguez/Live-Charts), может быть, вы тоже можете помочь вам увидеть исходный код. Расслабьтесь, я думаю, вы делаете это намного сложнее, чем то, что есть.

    private void PreventGraphToBeVisible()
    {
        var tt = Canvas.RenderTransform as TranslateTransform;
        if (tt == null) return;
        var eX = tt.X;
        var eY = tt.Y;
        var xOverflow = -tt.X + ActualWidth - Canvas.Width;
        var yOverflow = -tt.Y + ActualHeight - Canvas.Height;

        if (eX > 0)
        {
            tt.X = 0;
        }

        if (eY > 0)
        {
            tt.Y = 0;
        }

        if (xOverflow > 0)
        {
            tt.X = tt.X + xOverflow;
        }

        if (yOverflow > 0)
        {
            tt.Y = tt.Y + yOverflow;
        }
    }
Другие вопросы по тегам