我在.NET Framework4.7上的WPF应用程序中有一个<ScrollViewer>。在该ScrollViewer内部是一个<Border>。这个边界并不一定会占据它所获得的所有空间。对其应用ScaleTransform以允许放大和缩小。
使用鼠标滚轮进行放大和缩小时,对于新旧缩放,鼠标光标下的内容位置应保持不变。为此,我需要做一些数学运算来确定鼠标光标在新旧缩放中的位置。
我已经有了ScrollViewer的滚动位置,但是我找不到ScrollViewer的虚拟的、可滚动的或内容的宽/高。如何确定ScrollViewer内容区域的总宽度或总高度?这是不显示滚动条时的最小尺寸。我不能只使用内部边框的实际大小,因为它是受约束的,并且可能不会在两个维度上填充可滚动区域。我需要直接从ScrollViewer或其滚动条(我没有访问权限)中获取该信息。
下面是XAML的一部分:
<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事件处理程序中的当前位置:
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(...);
}具体地说,我正在从滚动查看器中查找不存在的ContentWidth和ContentHeight属性。如何获取这些值?
发布于 2020-06-05 23:28:49
扩展应该以Canvas为目标。通过将Canvas和ScaleTransform.CenterY设置为当前鼠标位置,可以在鼠标光标位置缩放ScaleTransform.CenterX。缩放将触发布局过程,该过程将触发ScrollViewer根据更改的内容进行调整。这需要通过滚动到新位置来重新定位内容。
缩放因子适用于相对于缩放元素的完整坐标系,例如Canvas。这意味着当鼠标位置为P1(5;5)并应用比例因子2时,鼠标位置将移动到P2(10;10)。新的鼠标位置P2描述必须添加到当前ScrollViewer偏移量的偏移量:
PcurrentScrollOffset =鼠标* scaleFactor + Pscroll
为了按像素滚动ScrollViewer (以像素为单位返回鼠标位置),必须将ScrollViewer配置为使用物理滚动单位,而不是逻辑单位。ScrollViewer.CanContentScroll必须设置为false (默认值)。
此外,PreviewMouseWheel事件处理程序还需要能够直接或通过数据绑定访问Canvas的ScaleTransform (或FrameworkElement )。ScrollViewer也是如此。
为了方便起见,我创建了一个附加的行为ZoomBehavior。
此行为将放大和缩小鼠标滚轮上作为FrameworElement输入的每个元素。
要启用缩放,请将ZoomBehavior.IsEnabled设置为true。
可以选择设置缩放因子集ZoomBehavior.ZoomFactor (默认值为0.1)。如果您希望调整滚动位置,可以选择将ScrollViewer绑定或设置为ZoomBehavior.ScrollViewer。请注意,应将ScrollViewer.CanContentScroll设置为false以实现正确的行为。
下面的示例启用对Canvas元素的缩放:
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语句(带有两个标签)。
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;
}
}https://stackoverflow.com/questions/62218312
复制相似问题