计算ScrollViewer的滚动条偏移,以使对象居中,同时保持缩放比例可变的布局转换 [英] Calculate a ScrollViewer's scrollbar offsets so that an object will be centered, while maintaining a layout transform who's scale is variable

查看:89
本文介绍了计算ScrollViewer的滚动条偏移,以使对象居中,同时保持缩放比例可变的布局转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经和这个人战斗了好几个小时了,但我似乎无法得出一个可以接受的答案。我希望那里的几何技能比我自己强的人能够为我解决这个难题。任何帮助将不胜感激。问题的性质和描述在下面提供的图像中。

I have been fighting with this one for many hours now, and I just can't seem to arrive at an acceptable answer. I am hoping someone out there with much stronger geometry skills than my self can solve this riddle for me. Any help would be greatly appreciated. The nature of my problem, and description is below in the image that I provided.

这是我构建的示例项目,无法正确满足要求。

And here is a sample project that I have built, which does not correctly fulfill the requirements.

XAML:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="Center and Zoom ScrollViewer Test" Height="600" Width="800" WindowStartupLocation="CenterScreen">
<Grid>
    <DockPanel>
        <GroupBox Header="Parameters" DockPanel.Dock="Top" Margin="10">
            <StackPanel Orientation="Horizontal">
                <GroupBox Header="Manually Set ScrollBar Positions" Margin="10">
                    <StackPanel Orientation="Horizontal">
                        <TextBox Name="EditHorz" Width="60" Margin="10" TextChanged="EditHorz_TextChanged" />
                        <Label Content="x" Margin="0 10 0 10" />
                        <TextBox Name="EditVert" Width="60" Margin="10" TextChanged="EditVert_TextChanged" />
                    </StackPanel>
                </GroupBox>

                <GroupBox Header="Scale" Margin="10">
                    <DockPanel>
                        <Label Content="{Binding ElementName=scaleValue, Path=Value}" DockPanel.Dock="Right" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Width="40" />
                        <Slider Name="scaleValue" Minimum="1" Maximum="4" SmallChange="0.05" LargeChange="0.1" Width="200" VerticalAlignment="Center" />
                    </DockPanel>
                </GroupBox>
            </StackPanel>
        </GroupBox>

        <GroupBox Header="Debug Output" Margin="10">
            <TextBox Name="text" FontFamily="Courier New" FontSize="12" DockPanel.Dock="Left" Width="500" TextWrapping="Wrap" AcceptsReturn="True" AcceptsTab="True" Margin="10" />
        </GroupBox>

        <GroupBox Header="Proof" Margin="10">
            <DockPanel>
                <StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" HorizontalAlignment="Center">
                    <Button Width="60" HorizontalAlignment="Left" Content="Center" Click="ButtonCenter_Click" Margin="10" />
                    <Button Width="60" HorizontalAlignment="Left" Content="Reset" Click="ButtonReset_Click" Margin="10" />
                </StackPanel>
                <ScrollViewer Name="scroll" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" Background="Green" Width="100" Height="100" VerticalAlignment="Top" Margin="10">
                    <Canvas Width="200" Height="200" Background="Red">
                        <Canvas.LayoutTransform>
                            <ScaleTransform ScaleX="{Binding ElementName=scaleValue, Path=Value}" ScaleY="{Binding ElementName=scaleValue, Path=Value}" />
                        </Canvas.LayoutTransform>
                        <Rectangle Name="rect" Width="40" Height="40" Canvas.Left="120" Canvas.Top="70" Fill="Blue" />
                    </Canvas>
                </ScrollViewer>
            </DockPanel>
        </GroupBox>
    </DockPanel>
</Grid>

隐藏代码( VB.net)

Class MainWindow
''' Calculates the horizontal and vertical scrollbar offsets so that
''' the blue rectangle is centered within the scroll viewer.
Private Sub RecalculateCenter()
    ' the scale we are using
    Dim scale As Double = scaleValue.Value

    ' get the rectangles current position within the canvas
    Dim rectLeft As Double = Canvas.GetLeft(rect)
    Dim rectTop As Double = Canvas.GetTop(rect)

    ' set our point of interest "Rect" equal to the the whole coordinates of the rectangle
    Dim poi As Rect = New Rect(rectLeft, rectTop, rect.Width, rect.Height)

    ' get our view offset
    Dim ofsViewWidth As Double = (scroll.ScrollableWidth - (((scroll.ViewportWidth / 2) - (rect.ActualWidth / 2)) * scale)) / scale
    Dim ofsViewHeight As Double = (scroll.ScrollableHeight - (((scroll.ViewportHeight / 2) - (rect.ActualHeight / 2)) * scale)) / scale

    ' calculate our scroll bar offsets
    Dim verticalOffset As Double = (poi.Top - ofsViewHeight) * scale
    Dim horizontalOffset As Double = (poi.Left - ofsViewWidth) * scale

    ' record the output to the debug output window
    Dim sb As New StringBuilder()
    sb.AppendLine($"Scale      : {scale}")
    sb.AppendLine($"POI        : {poi.ToString()}")
    sb.AppendLine($"Rect       : {rectLeft}x{rectTop}")
    sb.AppendLine($"Extent     : {scroll.ExtentWidth}x{scroll.ExtentHeight}")
    sb.AppendLine($"Scrollable : {scroll.ScrollableWidth}x{scroll.ScrollableHeight}")
    sb.AppendLine($"View Offset: {ofsViewWidth}x{ofsViewHeight}")
    sb.AppendLine($"Horizontal : {horizontalOffset}")
    sb.AppendLine($"Vertical   : {verticalOffset}")

    text.Text = sb.ToString()

    ' set the EditHorz and EditVert text box values, this will trigger the scroll
    ' bar offsets to fire via the TextChanged event handlers
    EditHorz.Text = horizontalOffset.ToString()
    EditVert.Text = verticalOffset.ToString()
End Sub

''' Try and parse the horizontal text box to a double, and set the scroll bar position accordingly
Private Sub SetScrollBarHorizontalOffset()
    Dim ofs As Double = 0
    If Double.TryParse(EditHorz.Text, ofs) Then
        scroll.ScrollToHorizontalOffset(ofs)
    End If
End Sub

''' Try and parse the vertical text box to a double, and set the scroll bar position accordingly
Private Sub SetScrollBarVerticalOffset()
    Dim ofs As Double = 0
    ofs = 0
    If Double.TryParse(EditVert.Text, ofs) Then
        scroll.ScrollToVerticalOffset(ofs)
    End If
End Sub

''' Parse and set scrollbars positions for both Horizontal and Vertical
Private Sub SetScrollBarOffsets()
    SetScrollBarHorizontalOffset()
    SetScrollBarVerticalOffset()
End Sub

Private Sub ButtonCenter_Click(sender As Object, e As RoutedEventArgs)
    RecalculateCenter()
End Sub

Private Sub ButtonReset_Click(sender As Object, e As RoutedEventArgs)
    scroll.ScrollToVerticalOffset(0)
    scroll.ScrollToHorizontalOffset(0)
End Sub

Private Sub EditHorz_TextChanged(sender As Object, e As TextChangedEventArgs)
    SetScrollBarOffsets()
End Sub

Private Sub EditVert_TextChanged(sender As Object, e As TextChangedEventArgs)
    SetScrollBarOffsets()
End Sub

结束类

推荐答案

经过更多的试验和错误,然后将其逐个拆分,我终于可以使此代码起作用。我希望其他人会觉得有用。解决方案如下:

After much more trial and error, and breaking it apart piece by piece, I was able to finally get this code to work. I hope someone else will find this useful. Solution is as follows:

XAML:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="Center and Zoom ScrollViewer Test" Height="600" Width="800" WindowStartupLocation="CenterScreen">
<Grid>
    <DockPanel>
        <GroupBox Header="Parameters" DockPanel.Dock="Top" Margin="10">
            <StackPanel Orientation="Horizontal">
                <GroupBox Header="Manually Set ScrollBar Positions" Margin="10">
                    <StackPanel Orientation="Horizontal">
                        <TextBox Name="EditHorz" Width="60" Margin="10" TextChanged="EditHorz_TextChanged" />
                        <Label Content="x" Margin="0 10 0 10" />
                        <TextBox Name="EditVert" Width="60" Margin="10" TextChanged="EditVert_TextChanged" />
                    </StackPanel>
                </GroupBox>

                <GroupBox Header="Scale" Margin="10">
                    <DockPanel>
                        <Label Content="{Binding ElementName=scaleValue, Path=Value, StringFormat={}{0:F2}}" DockPanel.Dock="Right" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Width="40" />
                        <Slider Name="scaleValue" Minimum="1" Maximum="4" SmallChange="0.05" LargeChange="0.1" Width="200" VerticalAlignment="Center" />
                    </DockPanel>
                </GroupBox>
            </StackPanel>
        </GroupBox>

        <GroupBox Header="Debug Output" Margin="10">
            <TextBox Name="text" FontFamily="Courier New" FontSize="12" DockPanel.Dock="Left" Width="500" TextWrapping="Wrap" AcceptsReturn="True" AcceptsTab="True" Margin="10" />
        </GroupBox>

        <GroupBox Header="Proof" Margin="10">
            <DockPanel>
                <StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" HorizontalAlignment="Center">
                    <Button Width="60" HorizontalAlignment="Left" Content="Center" Click="ButtonCenter_Click" Margin="10" />
                    <Button Width="60" HorizontalAlignment="Left" Content="Reset" Click="ButtonReset_Click" Margin="10" />
                </StackPanel>
                <ScrollViewer Name="scroll" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" Background="Green" Width="100" Height="100" VerticalAlignment="Top" Margin="10">
                    <Canvas Width="200" Height="200" Background="Red">
                        <Canvas.LayoutTransform>
                            <ScaleTransform ScaleX="{Binding ElementName=scaleValue, Path=Value}" ScaleY="{Binding ElementName=scaleValue, Path=Value}" />
                        </Canvas.LayoutTransform>
                        <Rectangle Name="rect" Width="40" Height="40" Canvas.Left="120" Canvas.Top="70" Fill="Blue" />
                    </Canvas>
                </ScrollViewer>
            </DockPanel>
        </GroupBox>
    </DockPanel>
</Grid>

隐藏代码( VB.net):

Class MainWindow
' Calculates the horizontal and vertical scrollbar offsets so that
' the blue rectangle is centered within the scroll viewer.
Private Sub RecalculateCenter()
    ' the scale we are using
    Dim scale As Double = scaleValue.Value

    ' get our rectangles left and top properties
    Dim rectLeft As Double = Canvas.GetLeft(rect) * scale
    Dim rectTop As Double = Canvas.GetTop(rect) * scale
    Dim rectWidth As Double = rect.Width * scale
    Dim rectHeight As Double = rect.Height * scale

    ' set our point of interest "Rect" equal to the the whole coordinates of the rectangle
    Dim poi As Rect = New Rect(rectLeft, rectTop, rectWidth, rectHeight)

    ' get top and left center values
    Dim horizontalCenter As Double = ((scroll.ViewportWidth / 2) - (rectWidth / 2))
    Dim verticalCenter As Double = ((scroll.ViewportHeight / 2) - (rectHeight / 2))

    ' get our center of viewport with relation to the poi
    Dim viewportCenter As New Rect(horizontalCenter, verticalCenter, rectWidth, rectHeight)

    ' calculate our scroll bar offsets
    Dim verticalOffset As Double = (poi.Top) - (viewportCenter.Top)
    Dim horizontalOffset As Double = (poi.Left) - (viewportCenter.Left)

    ' record the output to the debug output window
    Dim sb As New StringBuilder()
    sb.AppendLine($"Scale .............. {scale,0:F2}")
    sb.AppendLine($"rectLeft ........... {rectLeft,0:F0}")
    sb.AppendLine($"rectTop ............ {rectTop,0:F0}")
    sb.AppendLine($"POI ................ {poi.Left,0:F0},{poi.Top,0:F0},{poi.Width,0:F0},{poi.Height,0:F0}")
    sb.AppendLine($"Horz Center ........ {horizontalCenter,0:F0}")
    sb.AppendLine($"Vert Center ........ {verticalCenter,0:F0}")
    sb.AppendLine($"View Center ........ {viewportCenter.Left,0:F0},{viewportCenter.Top,0:F0},{viewportCenter.Width,0:F0},{viewportCenter.Height,0:F0}")
    sb.AppendLine($"Horizontal ......... {horizontalOffset,0:F0}")
    sb.AppendLine($"Vertical ........... {verticalOffset,0:F0}")
    sb.AppendLine($"------------------------------------")
    sb.AppendLine($"ViewPort ........... {scroll.ViewportWidth,0:F0} x {scroll.ViewportHeight,0:F0}")
    sb.AppendLine($"Extent ............. {scroll.ExtentWidth,0:F0} x {scroll.ExtentHeight,0:F0}")
    sb.AppendLine($"Scrollable ......... {scroll.ScrollableWidth,0:F0} x {scroll.ScrollableHeight,0:F0}")

    text.Text = sb.ToString()

    ' set the EditHorz and EditVert text box values, this will trigger the scroll
    ' bar offsets to fire via the TextChanged event handlers
    EditHorz.Text = $"{horizontalOffset,0:F2}"
    EditVert.Text = $"{verticalOffset,0:F2}"
End Sub

' Try and parse the horizontal text box to a double, and set the scroll bar position accordingly
Private Sub SetScrollBarHorizontalOffset()
    Dim ofs As Double = 0
    If Double.TryParse(EditHorz.Text, ofs) Then
        scroll.ScrollToHorizontalOffset(ofs)
    Else
        scroll.ScrollToHome()
    End If
End Sub

' Try and parse the vertical text box to a double, and set the scroll bar position accordingly
Private Sub SetScrollBarVerticalOffset()
    Dim ofs As Double = 0
    ofs = 0
    If Double.TryParse(EditVert.Text, ofs) Then
        scroll.ScrollToVerticalOffset(ofs)
    Else
        scroll.ScrollToHome()
    End If
End Sub

' Parse and set scrollbars positions for both Horizontal and Vertical
Private Sub SetScrollBarOffsets()
    SetScrollBarHorizontalOffset()
    SetScrollBarVerticalOffset()
End Sub

Private Sub ButtonCenter_Click(sender As Object, e As RoutedEventArgs)
    RecalculateCenter()
End Sub

Private Sub ButtonReset_Click(sender As Object, e As RoutedEventArgs)
    EditHorz.Text = String.Empty
    EditVert.Text = String.Empty
End Sub

Private Sub EditHorz_TextChanged(sender As Object, e As TextChangedEventArgs)
    SetScrollBarOffsets()
End Sub

Private Sub EditVert_TextChanged(sender As Object, e As TextChangedEventArgs)
    SetScrollBarOffsets()
End Sub

Private Sub scaleValue_ValueChanged(sender As Object, e As RoutedPropertyChangedEventArgs(Of Double)) Handles scaleValue.ValueChanged
    Dispatcher.BeginInvoke(Sub() RecalculateCenter())
End Sub

最终舱位

这篇关于计算ScrollViewer的滚动条偏移,以使对象居中,同时保持缩放比例可变的布局转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆