我有一个带有网格的滚动查看器作为孩子.我正在更改网格的宽度和高度属性以显示不同的“缩放”级别.网格包含2行,其中包含许多图像列,大小相同.
但是,我希望滚动条的相对位置保持不变.在更改网格大小后,屏幕中心的任何内容仍应位于屏幕中央.
默认“放大”视图:
private void SizeGrid() { grid1.Width = (scrollViewer1.ViewportWidth / 2) * grid1.ColumnDefinitions.Count; grid1.Height = (scrollViewer1.ViewportHeight / 2) * grid1.RowDefinitions.Count; }
“缩小”观点:
private void scrollViewer1_KeyDown(object sender,KeyEventArgs e) { if (e.KeyboardDevice.IsKeyDown(Key.Insert)) { grid1.Width = (scrollViewer1.ViewportWidth / 2) * grid1.ColumnDefinitions.Count / 5; grid1.Height = (scrollViewer1.ViewportHeight / 2) * grid1.RowDefinitions.Count / 3; } }
我做了什么……
如果我知道哪个列是关注的(我不想知道这个):
double shiftAmount = (scrollViewer1.ScrollableWidth / (grid1.ColumnDefinitions.Count - columnsOnScreen)); scrollViewer1.ScrollToHorizontalOffset(column * shiftAmount);
如果我不确切知道他们正在看什么列,但我只是想保持相对位置……
double prevIoUsScrollRatio = scrollViewer1.HorizontalOffset / scrollViewer1.ScrollableWidth; //resize grid... scrollViewer1.ScrollToHorizontalOffset(prevIoUsScrollRatio * scrollViewer1.ScrollableWidth);
两种方法都不奏效.如果我以滚动条居中缩小,则滚动条将转到最右侧.任何的想法?
可以在上面找到一个最小的代码示例here加上scroll_KeyDown方法.
默认缩放的屏幕截图:
缩小后的屏幕截图,不正确(海军蓝和粉红色方块远离屏幕):
缩小后的屏幕截图,它应该是什么样子:
解决方法
这是一种在放大或缩小时将内容保持在中心的解决方案
//variables to store the offset values double relX; double relY; void scrollViewer1_ScrollChanged(object sender,ScrollChangedEventArgs e) { ScrollViewer scroll = sender as ScrollViewer; //see if the content size is changed if (e.ExtentWidthChange != 0 || e.ExtentHeightChange != 0) { //calculate and set accordingly scroll.ScrollToHorizontalOffset(CalculateOffset(e.ExtentWidth,e.ViewportWidth,scroll.ScrollableWidth,relX)); scroll.ScrollToVerticalOffset(CalculateOffset(e.ExtentHeight,e.ViewportHeight,scroll.ScrollableHeight,relY)); } else { //store the relative values if normal scroll relX = (e.HorizontalOffset + 0.5 * e.ViewportWidth) / e.ExtentWidth; relY = (e.VerticalOffset + 0.5 * e.ViewportHeight) / e.ExtentHeight; } } private static double CalculateOffset(double extent,double viewPort,double scrollWidth,double relBefore) { //calculate the new offset double offset = relBefore * extent - 0.5 * viewPort; //see if it is negative because of initial values if (offset < 0) { //center the content //this can be set to 0 if center by default is not needed offset = 0.5 * scrollWidth; } return offset; }
后面的想法是存储最后一个滚动位置,并在每次更改内容大小时使用它计算新的偏移量,这将使范围发生变化.
只需将ScrollViewer的事件ScrollChanged附加到构造函数等中的此事件处理程序,并将其余部分保留给它.
例如
scrollViewer1.ScrollChanged += scrollViewer1_ScrollChanged;
上述解决方案将确保将电网保持在中心位置,即使是第一次负载也是如此
中心内容的样本
放大了
缩小了
额外
我也尝试为同一个创建一个可附加的行为,因此您不需要连接事件,只需设置属性将启用或禁用该行为
namespace CSharpWPF { public class AdvancedZooming : DependencyObject { public static bool GetKeepInCenter(DependencyObject obj) { return (bool)obj.GetValue(KeepInCenterProperty); } public static void SetKeepInCenter(DependencyObject obj,bool value) { obj.SetValue(KeepInCenterProperty,value); } // Using a DependencyProperty as the backing store for KeepInCenter. This enables animation,styling,binding,etc... public static readonly DependencyProperty KeepInCenterProperty = DependencyProperty.RegisterAttached("KeepInCenter",typeof(bool),typeof(AdvancedZooming),new PropertyMetadata(false,OnKeepInCenterChanged)); // Using a DependencyProperty as the backing store for Behavior. This enables animation,etc... public static readonly DependencyProperty BehaviorProperty = DependencyProperty.RegisterAttached("Behavior",new PropertyMetadata(null)); private static void OnKeepInCenterChanged(DependencyObject d,DependencyPropertyChangedEventArgs e) { ScrollViewer scroll = d as ScrollViewer; if ((bool)e.NewValue) { //attach the behavior AdvancedZooming behavior = new AdvancedZooming(); scroll.ScrollChanged += behavior.scroll_ScrollChanged; scroll.SetValue(BehaviorProperty,behavior); } else { //dettach the behavior AdvancedZooming behavior = scroll.GetValue(BehaviorProperty) as AdvancedZooming; if (behavior != null) scroll.ScrollChanged -= behavior.scroll_ScrollChanged; scroll.SetValue(BehaviorProperty,null); } } //variables to store the offset values double relX; double relY; void scroll_ScrollChanged(object sender,ScrollChangedEventArgs e) { ScrollViewer scroll = sender as ScrollViewer; //see if the content size is changed if (e.ExtentWidthChange != 0 || e.ExtentHeightChange != 0) { //calculate and set accordingly scroll.ScrollToHorizontalOffset(CalculateOffset(e.ExtentWidth,relX)); scroll.ScrollToVerticalOffset(CalculateOffset(e.ExtentHeight,relY)); } else { //store the relative values if normal scroll relX = (e.HorizontalOffset + 0.5 * e.ViewportWidth) / e.ExtentWidth; relY = (e.VerticalOffset + 0.5 * e.ViewportHeight) / e.ExtentHeight; } } private static double CalculateOffset(double extent,double relBefore) { //calculate the new offset double offset = relBefore * extent - 0.5 * viewPort; //see if it is negative because of initial values if (offset < 0) { //center the content //this can be set to 0 if center by default is not needed offset = 0.5 * scrollWidth; } return offset; } } }
启用行为
通过xaml
<ScrollViewer l:AdvancedZooming.KeepInCenter="True">
要么
<Style TargetType="ScrollViewer" x:Key="zoomCenter"> <Setter Property="l:AdvancedZooming.KeepInCenter" Value="True" /> </Style>
或者通过像
scrollViewer1.SetValue(AdvancedZooming.KeepInCenterProperty,true);
要么
AdvancedZooming.SetKeepInCenter(scrollViewer1,true);
通过样式或以编程方式设置属性l:AdvancedZooming.KeepInCenter =“True”内联,以便在任何scrollviewer上启用行为
l:在本例中指的是AdvancedZooming类的命名空间xmlns:l =“clr-namespace:CSharpWPF”