【问题标题】:How to keep area of canvas centered in a ScrollViewer when Zoomed in or out, and not everything can be displayed in the viewing window放大或缩小时如何在 ScrollViewer 中保持画布区域居中,并且并非所有内容都可以显示在查看窗口中
【发布时间】:2011-09-20 18:51:29
【问题描述】:

大家,

我有一个 WPF 应用程序,该应用程序有一个我包裹在滚动查看器中的画布。我在状态栏中有一个滑块,允许用户放大和缩小(就像 Win 7 的 mspaint 一样)。

这里是一些 XAML:

<ScrollViewer Name="Map"
              VerticalScrollBarVisibility="Auto"
              HorizontalScrollBarVisibility="Auto">
    <Canvas x:Name="WallsCanvas" Height="800" Width="1000" ClipToBounds="True">
        <Canvas.LayoutTransform>
            <ScaleTransform x:Name="WallsCanvasScale"
                            ScaleX="1" ScaleY="1" />
        </Canvas.LayoutTransform>
    </Canvas>
</ScrollViewer>

当我放大并且滚动条可见时,滚动条无论设置在哪里,都会跳到中间。

就像滚动条的值保持不变但最大值增加一样。

我可以做些什么来让他们……比如说,如果他们在右下角,在放大或缩小后留在右下角?

顺便说一句,这是我的放大和缩小代码:

private void SliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    var scales = new []{.125, .25, .5, 1, 2, 4, 8};
    var scale = scales[(int)((Slider) sender).Value];

    ScaleChanged(scale, WallsCanvasScale);
}

private static void ScaleChanged(double scale, ScaleTransform st)
{
    st.ScaleX = scale;
    st.ScaleY = scale;
}

所以,我的代码中没有火箭科学,但是......

更新想法:如果我可以访问滚动条的值和最大值,我可以得到两者之间的百分比,然后在缩放(缩放)之后我可以重新应用滚动条的值作为百分比最大值???但是可用的值和最大值在哪里?

任何帮助将不胜感激。我不能认为我是唯一遇到此问题的人,因为 MSPaint(Windows 7 版本)可以正常工作,并且我认为它是一个 XAML 应用程序。

这是一个link (http://www.leesaunders.net/examples/zoomexample/zoomexample.zip) 到一个最小工作示例项目 (VS 2010)。当你运行它时,只需移动滚动条然后放大一个级别,你就会立即看到问题。

【问题讨论】:

  • 中心属性没有设置相对百分比值,目前你的中心是向右半像素,从左上角向下半像素。
  • 好的,所以从技术上讲,我什至不需要定义 CenterX 和 CenterY。谢谢你的小糟糕。要是原来的问题这么容易解决就好了。 :-)
  • (H.B.) 我从示例和源代码中删除了 Center 属性。再次感谢。
  • 你能不能展示完整的代码/上传一些可靠地证明这种行为的项目?滚动条不应正常跳转。
  • (H.B.) 我在问题末尾添加了一个链接,其中包含一个最小工作示例项目(VS 2010)

标签: c# wpf xaml zooming


【解决方案1】:

您只需要抵消由比例引起的偏移,因为它从 (0,0) 缩放。这有点复杂,但这里是您示例中的方法的示意图:

private void SliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    var slider = (Slider)sender;
    if (!slider.IsLoaded)
    {
        slider.Loaded += (s, le) => SliderValueChanged(sender, e);
        return;
    }

    var scales = new[] { .125, .25, .5, 1, 2, 4, 8 };
    var scale = scales[(int)((Slider)sender).Value];

    // The "+20" are there to account for the scrollbars... i think. Not perfectly accurate.
    var relativeMiddle = new Point((Map.ActualWidth + 20) / 2, (Map.ActualHeight + 20) / 2);
    var oldLocation = CanvasScale.Transform(TemplateCanvas.PointFromScreen(relativeMiddle));

    ScaleChanged(scale, CanvasScale);

    var newLocation = CanvasScale.Transform(TemplateCanvas.PointFromScreen(relativeMiddle));

    var shift = newLocation - oldLocation;

    Map.ScrollToVerticalOffset(Map.VerticalOffset + shift.Y);
    Map.ScrollToHorizontalOffset(Map.HorizontalOffset + shift.X);

    lblScale.Content = scale.ToString("P1").Replace(".0", string.Empty);
}

应该是不言自明的;在缩放之前和之后测量中心的位置以计算该点的偏移量,然后将其添加到当前滚动位置。

【讨论】:

  • (HB) 刚刚尝试让您的代码正常工作,但是...这部分代码失败 -> TemplateCanvas.PointFromScreen(relativeMiddle) 显示“此 Visual 未连接到 PresentationSource。 "我需要做什么才能克服这个问题?
  • @saunderl:您需要检查控件是否已加载,如果不延迟处理程序的执行。我添加了一些代码。
  • (H.B.) 谢谢先生!如果 TemplateCanvas 不是 Visual 的一部分,我也只是通过退出函数来使其工作: if (PresentationSource.FromVisual(TemplateCanvas) =​​= null) return;
  • @saunderl:刚退出的问题是如果默认值不适合默认比例,初始显示可能会关闭。
  • (H.B.) 非常正确,但作为唯一的开发者,我可以保证。再次感谢您为我解决了这个问题。
猜你喜欢
  • 1970-01-01
  • 2019-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-19
  • 2015-05-24
  • 2011-10-10
  • 1970-01-01
相关资源
最近更新 更多