首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >确定ScrollViewer的内部宽度

确定ScrollViewer的内部宽度
EN

Stack Overflow用户
提问于 2020-06-05 22:57:37
回答 1查看 107关注 0票数 0

我在.NET Framework4.7上的WPF应用程序中有一个<ScrollViewer>。在该ScrollViewer内部是一个<Border>。这个边界并不一定会占据它所获得的所有空间。对其应用ScaleTransform以允许放大和缩小。

使用鼠标滚轮进行放大和缩小时,对于新旧缩放,鼠标光标下的内容位置应保持不变。为此,我需要做一些数学运算来确定鼠标光标在新旧缩放中的位置。

我已经有了ScrollViewer的滚动位置,但是我找不到ScrollViewer的虚拟的、可滚动的或内容的宽/高。如何确定ScrollViewer内容区域的总宽度或总高度?这是不显示滚动条时的最小尺寸。我不能只使用内部边框的实际大小,因为它是受约束的,并且可能不会在两个维度上填充可滚动区域。我需要直接从ScrollViewer或其滚动条(我没有访问权限)中获取该信息。

下面是XAML的一部分:

代码语言:javascript
复制
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
              PreviewMouseWheel="ScrollViewer_PreviewMouseWheel">
    <Border HorizontalAlignment="Center" VerticalAlignment="Center" Margin="100">
        <Grid>
            <Grid.LayoutTransform>
                <ScaleTransform .../>
            </Grid.LayoutTransform>
            <dt:DrawingCanvas .../>
        </Grid>
    </Border>
</scrollViewer>

DrawingCanvas是决定大小的对象。它在滚动查看器中水平和垂直居中。如果它在一维或两维上超过了可用屏幕空间,则会出现滚动条。

我使用以下代码来获取ScrollViewer_PreviewMouseWheel事件处理程序中的当前位置:

代码语言:javascript
复制
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs args)
{
    // Determine the mouse cursor position relative to the content
    var sv = (ScrollViewer)sender;
    Point mousePoint = args.GetPosition(sv);
    double x = mousePoint.X + sv.HorizontalOffset;
    double y = mousePoint.Y + sv.VerticalOffset;
    double xFrac = x / sv.ContentWidth;
    double yFrac = y / sv.ContentHeight;

    // Updating scale factor and layout...

    // Move scrollbars to keep the same content under the mouse cursor
    sv.ScrollToHorizontalOffset(...);
    sv.ScrollToVerticalOffset(...);
}

具体地说,我正在从滚动查看器中查找不存在的ContentWidthContentHeight属性。如何获取这些值?

EN

回答 1

Stack Overflow用户

发布于 2020-06-05 23:28:49

扩展应该以Canvas为目标。通过将CanvasScaleTransform.CenterY设置为当前鼠标位置,可以在鼠标光标位置缩放ScaleTransform.CenterX。缩放将触发布局过程,该过程将触发ScrollViewer根据更改的内容进行调整。这需要通过滚动到新位置来重新定位内容。

缩放因子适用于相对于缩放元素的完整坐标系,例如Canvas。这意味着当鼠标位置为P1(5;5)并应用比例因子2时,鼠标位置将移动到P2(10;10)。新的鼠标位置P2描述必须添加到当前ScrollViewer偏移量的偏移量:

PcurrentScrollOffset =鼠标* scaleFactor + Pscroll

为了按像素滚动ScrollViewer (以像素为单位返回鼠标位置),必须将ScrollViewer配置为使用物理滚动单位,而不是逻辑单位。ScrollViewer.CanContentScroll必须设置为false (默认值)。

此外,PreviewMouseWheel事件处理程序还需要能够直接或通过数据绑定访问CanvasScaleTransform (或FrameworkElement )。ScrollViewer也是如此。

为了方便起见,我创建了一个附加的行为ZoomBehavior

此行为将放大和缩小鼠标滚轮上作为FrameworElement输入的每个元素。

要启用缩放,请将ZoomBehavior.IsEnabled设置为true

可以选择设置缩放因子集ZoomBehavior.ZoomFactor (默认值为0.1)。如果您希望调整滚动位置,可以选择将ScrollViewer绑定或设置为ZoomBehavior.ScrollViewer。请注意,应将ScrollViewer.CanContentScroll设置为false以实现正确的行为。

下面的示例启用对Canvas元素的缩放:

代码语言:javascript
复制
ScrollViewer x:Name="ScrollViewer" 
             CanContentScroll="False" 
             Width="500" Height="500"
             VerticalScrollBarVisibility="Auto"
             HorizontalScrollBarVisibility="Auto">
  <Canvas Width="300" Height="300"
          main:ZoomBehavior.IsEnabled="True"
          main:ZoomBehavior.ZoomFactor="0.1"
          main:ZoomBehavior.ScrollViewer="{Binding ElementName=ScrollViewer}"
          Background="DarkGray">
    <Ellipse Fill="DarkOrange" 
             Height="100" Width="100" 
             Canvas.Top="100" Canvas.Left="100" />
  </Canvas>
</ScrollViewer>

ZoomBehavior.cs

该代码使用C# 8.0的一个特性switch expression

如果您的环境不支持此语言版本,则需要将表达式转换为经典的switch语句(带有两个标签)。

代码语言:javascript
复制
public class ZoomBehavior : DependencyObject
{
  #region IsEnabled attached property

  // Required
  public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached(
    "IsEnabled", typeof(bool), typeof(ZoomBehavior), new PropertyMetadata(default(bool), OnIsEnabledChanged));

  public static void SetIsEnabled(DependencyObject attachingElement, bool value) => attachingElement.SetValue(ZoomBehavior.IsEnabledProperty, value);

  public static bool GetIsEnabled(DependencyObject attachingElement) => (bool) attachingElement.GetValue(ZoomBehavior.IsEnabledProperty);

  #endregion

  #region ZoomFactor attached property

  // Optional
  public static readonly DependencyProperty ZoomFactorProperty = DependencyProperty.RegisterAttached(
    "ZoomFactor", typeof(double), typeof(ZoomBehavior), new PropertyMetadata(0.1));

  public static void SetZoomFactor(DependencyObject attachingElement, double value) => attachingElement.SetValue(ZoomBehavior.ZoomFactorProperty, value);

  public static double GetZoomFactor(DependencyObject attachingElement) => (double) attachingElement.GetValue(ZoomBehavior.ZoomFactorProperty);

  #endregion

  #region ScrollViewer attached property

  // Optional
  public static readonly DependencyProperty ScrollViewerProperty = DependencyProperty.RegisterAttached(
    "ScrollViewer", typeof(ScrollViewer), typeof(ZoomBehavior), new PropertyMetadata(default(ScrollViewer)));

  public static void SetScrollViewer(DependencyObject attachingElement, ScrollViewer value) => attachingElement.SetValue(ZoomBehavior.ScrollViewerProperty, value);

  public static ScrollViewer GetScrollViewer(DependencyObject attachingElement) => (ScrollViewer) attachingElement.GetValue(ZoomBehavior.ScrollViewerProperty);

  #endregion
  private static void OnIsEnabledChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
  {
    if (!(attachingElement is FrameworkElement frameworkElement))
    {
      throw new ArgumentException("Attaching element must be of type FrameworkElement");
    }

    bool isEnabled = (bool) e.NewValue;
    if (isEnabled)
    {
      frameworkElement.PreviewMouseWheel += ZoomBehavior.Zoom_OnMouseWheel;
      if (ZoomBehavior.TryGetScaleTransform(frameworkElement, out _))
      {
        return;
      }

      if (frameworkElement.LayoutTransform is TransformGroup transformGroup)
      {
        transformGroup.Children.Add(new ScaleTransform());
      }
      else
      {
        frameworkElement.LayoutTransform = new ScaleTransform();
      }
    }
    else
    {
      frameworkElement.PreviewMouseWheel -= ZoomBehavior.Zoom_OnMouseWheel;
    }
  }

  private static void Zoom_OnMouseWheel(object sender, MouseWheelEventArgs e)
  {
    var zoomTargetElement = sender as FrameworkElement;

    Point mouseCanvasPosition = e.GetPosition(zoomTargetElement);
    double scaleFactor = e.Delta > 0
      ? ZoomBehavior.GetZoomFactor(zoomTargetElement)
      : -1 * ZoomBehavior.GetZoomFactor(zoomTargetElement);

    ZoomBehavior.ApplyZoomToAttachedElement(mouseCanvasPosition, scaleFactor, zoomTargetElement);

    ZoomBehavior.AdjustScrollViewer(mouseCanvasPosition, scaleFactor, zoomTargetElement);
  }

  private static void ApplyZoomToAttachedElement(Point mouseCanvasPosition, double scaleFactor, FrameworkElement zoomTargetElement)
  {
    if (!ZoomBehavior.TryGetScaleTransform(zoomTargetElement, out ScaleTransform scaleTransform))
    {
      throw new InvalidOperationException("No ScaleTransform found");
    }

    scaleTransform.CenterX = mouseCanvasPosition.X;
    scaleTransform.CenterY = mouseCanvasPosition.Y;

    scaleTransform.ScaleX = Math.Max(0.1, scaleTransform.ScaleX + scaleFactor);
    scaleTransform.ScaleY = Math.Max(0.1, scaleTransform.ScaleY + scaleFactor);
  }

  private static void AdjustScrollViewer(Point mouseCanvasPosition, double scaleFactor, FrameworkElement zoomTargetElement)
  {
    ScrollViewer scrollViewer = ZoomBehavior.GetScrollViewer(zoomTargetElement);
    if (scrollViewer == null)
    {
      return;
    }

    scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset + mouseCanvasPosition.X * scaleFactor);
    scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + mouseCanvasPosition.Y * scaleFactor);
  }

  private static bool TryGetScaleTransform(FrameworkElement frameworkElement, out ScaleTransform scaleTransform)
  {
    // C# 8.0 Switch Expression
    scaleTransform = frameworkElement.LayoutTransform switch
    {
      TransformGroup transformGroup => transformGroup.Children.OfType<ScaleTransform>().FirstOrDefault(),
      ScaleTransform transform => transform,
      _ => null
    };

    return scaleTransform != null;
  }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62218312

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档