WPF Scrollviewer не адаптируется к содержанию - PullRequest
0 голосов
/ 03 марта 2020

Мне нужно показать изображение в моем приложении WPF, наложить его на прозрачный холст и позволить пользователю рисовать на нем. Пользователь должен иметь возможность масштабировать изображение (входить и выходить) и «перемещаться» по изображению, в то время как масштабирование выполняется с помощью каких-либо полос прокрутки, прикрепленных к нижней и правой части окна. Чтобы включить масштабирование изображения, я использовал элемент управления ZoomBorder , описанный во втором ответе. Так что это мой код XAML:

<ScrollViewer x:Name="imageScroll" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"  >
  <local:ZoomBorder x:Name="backgroundZoomBorder" ClipToBounds="True" HorizontalAlignment="Center" VerticalAlignment="Center"  Width="{Binding CurrentImageWidth}" Height="{Binding CurrentImageHeight}"  >
     <Grid x:Name="gridDrawing" MouseLeftButtonDown="background_MouseLeftButtonDown" MouseMove="background_MouseMove" MouseLeftButtonUp="background_MouseLeftButtonUp">                           
       <Image  x:Name="mainImage"  Source="{Binding Path=WriteableBmp}" />
       <Canvas x:Name="canvasDrawing"  Background="Transparent" />
     </Grid>
  </local:ZoomBorder>
</ScrollViewer>

Это работает, однако, scollviewer не настраивается на размер контента, он все тот же, если изображение увеличено до максимума или уменьшено до минимума , Он должен исчезнуть, если изображение меньше области окна, или иметь больший диапазон при увеличении изображения. Могу ли я так или иначе связать ScrollViever с преобразованным размером изображения или чем-то еще? Или есть ли лучший контроль для этого вместо ScrollViewer? Спасибо

1 Ответ

2 голосов
/ 03 марта 2020

A ScrollViewer никогда не будет работать с элементом управления ZoomBorder, поскольку он использует RenderTransform. RenderTransform манипулирует тем, что вы видите после того, как прошло через систему макетов. Из-за этого его размер никогда не изменяется, и поэтому ScrollViewer никогда не активируется.

Изменяя ZoomBorder для использования LayoutTransform, вы можете заставить работать функцию масштабирования (колесо прокрутки) и ScrollViewer активируется. Однако получение панорамирования для работы с мышью потребует более значительных изменений.

Вот пример кода, который работает:

ZoomBorder с использованием LayoutTransform

using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace SO
{
    public class ZoomBorder : Border
    {
        private FrameworkElement child;

        private ScaleTransform GetScaleTransform(FrameworkElement element)
        {
            return (ScaleTransform)((TransformGroup)element.LayoutTransform)
              .Children.First(tr => tr is ScaleTransform);
        }

        public override UIElement Child
        {
            get => base.Child;
            set
            {
                if (value != null && value != Child)
                    Initialize(value);
                base.Child = value;
            }
        }

        public void Initialize(UIElement element)
        {
            child = (FrameworkElement)element;

            if (child == null) return;

            var group = new TransformGroup();
            var st = new ScaleTransform();

            @group.Children.Add(st);

            child.LayoutTransform = @group;
            child.RenderTransformOrigin = new Point(0.0, 0.0);

            MouseWheel += child_MouseWheel;
            PreviewMouseRightButtonDown += child_PreviewMouseRightButtonDown;
        }

        public void Reset()
        {
            if (child == null) return;

            // reset zoom
            var st = GetScaleTransform(child);
            st.ScaleX = 1.0;
            st.ScaleY = 1.0;
        }

        #region Child Events
        private void child_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (child != null)
            {
                var st = GetScaleTransform(child);

                var zoom = e.Delta > 0 ? .2 : -.2;

                if (!(e.Delta > 0) && (st.ScaleX < .4 || st.ScaleY < .4)) return;

                st.ScaleX += zoom;
                st.ScaleY += zoom;
            }
        }

        void child_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            Reset();
        }
        #endregion
    }
}

ScrollViewerPanBehavior A поведение который управляет панорамированием, перетаскивая мышью.

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace SO
{
    public class ScrollViewerPanBehavior : Behavior<ScrollViewer>
    {
        private UIElement content;
        private Point scrollMousePoint;
        private double scrollHorizontalOffset;
        private double scrollVerticalOffset;

        public ScrollViewerPanBehavior()
        {
        }

        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            content = (UIElement)AssociatedObject.Content;
            content.MouseLeftButtonDown += OnMouseLeftButtonDown;
            content.MouseMove += OnMouseMove;
            content.MouseLeftButtonUp += OnMouseLeftButtonUp;
        }

        private void OnMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            content.CaptureMouse();
            AssociatedObject.Cursor = Cursors.Hand;
            scrollMousePoint = e.GetPosition(AssociatedObject);
            scrollHorizontalOffset = AssociatedObject.HorizontalOffset;
            scrollVerticalOffset = AssociatedObject.VerticalOffset;
        }

        private void OnMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            if (content.IsMouseCaptured)
            {
                var newVerticalOffset = scrollVerticalOffset + (scrollMousePoint.Y - e.GetPosition(AssociatedObject).Y);
                var newHorizontalOffset = scrollHorizontalOffset + (scrollMousePoint.X - e.GetPosition(AssociatedObject).X);

                AssociatedObject.ScrollToVerticalOffset(newVerticalOffset);
                AssociatedObject.ScrollToHorizontalOffset(newHorizontalOffset);
            }
        }

        private void OnMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            content.ReleaseMouseCapture();
            AssociatedObject.Cursor = Cursors.Arrow;
        }
    }
}

Пример использования:

<ScrollViewer
    HorizontalScrollBarVisibility="Auto"
    VerticalScrollBarVisibility="Auto">
    <i:Interaction.Behaviors>
        <local:ScrollViewerPanBehavior />
    </i:Interaction.Behaviors>
    <local:ZoomBorder
        HorizontalAlignment="Center"
        VerticalAlignment="Center">
        <Grid>
            <Image Source="ThumbsUp.png" />
        </Grid>
    </local:ZoomBorder>
</ScrollViewer>

Работать над этим было весело!

...