ScrollViewer ScrollToHorizo​​ntalOffset奇怪的行为

时间:2015-02-14 21:24:53

标签: c# wpf xaml scrollviewer

我的应用包含ScrollViewerStackPanel

XAML:

<Window x:Class="WpfScrollViewer.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="500" Width="800" Loaded="MainWindow_OnLoaded">
    <Window.Resources>
        <Style TargetType="Rectangle">
            <Setter Property="Fill" Value="Blue" />
            <Setter Property="Margin" Value="10" />
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="50" />
        </Grid.RowDefinitions>
        <ScrollViewer x:Name="MyScrollViewer" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Auto">
            <StackPanel x:Name="MyStackPanel" Orientation="Horizontal" />
        </ScrollViewer>
        <Button Grid.Row="1" Width="50" HorizontalAlignment="Left" Content="left" Click="LeftButton_Click" />
        <Button Grid.Row="1" Width="50" HorizontalAlignment="Right" Content="right" Click="RightButton_Click" />
    </Grid>
</Window>

StackPanel内部我从后面的代码中创建了几个Rectangle,其宽度是根据我的Window的宽度计算出来的。

背后的代码:

using System.Windows;
using System.Windows.Shapes;

namespace WpfScrollViewer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private double _scrollWidth;
        private double _edgeScrollWidth;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void LeftButton_Click(object sender, RoutedEventArgs e)
        {
            var offset = MyScrollViewer.HorizontalOffset;
            if (offset == MyScrollViewer.ScrollableWidth)
            {
                MyScrollViewer.ScrollToHorizontalOffset(offset - _edgeScrollWidth);
            }
            else
            {
                MyScrollViewer.ScrollToHorizontalOffset(offset - _scrollWidth);
            }
        }

        private void RightButton_Click(object sender, RoutedEventArgs e)
        {
            var offset = MyScrollViewer.HorizontalOffset;
            if (offset == 0)
            {
                MyScrollViewer.ScrollToHorizontalOffset(offset + _edgeScrollWidth);
            }
            else
            {
                MyScrollViewer.ScrollToHorizontalOffset(offset + _scrollWidth);
            }
        }

        private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
        {
            _scrollWidth = Width - 60;
            _edgeScrollWidth = Width - 90;
            var itemWidth = Width - 80;

            for (var i = 0; i < 10; i++)
            {
                MyStackPanel.Children.Add(new Rectangle { Width = itemWidth });
            }
        }
    }
}

当我按右Button时,我的矩形看起来像这样: wrong offset 但我想看到两侧矩形的相等部分。我想让我的整个矩形居中,而不是。我真的认为我正确计算了偏移量,所以我真的不知道我错在哪里 我是否遗漏了某些内容,或者使用WPF独立单位进行计算时遇到了一些问题?

1 个答案:

答案 0 :(得分:1)

我的问题源于我在Width事件处理程序中使用Loaded属性。 Width这里是Window类的width属性,该属性的值包含窗口边框的宽度。您真正想要的是窗口内部部分的宽度,或客户区。没有WPF属性,因此您需要使用窗口的子元素ActualWidth(窗口Content)。

另外,我建议摆脱&#34;魔法&#34;数字通过将它们转换为常数。

以下是您的代码的重新编译版本,可以执行您想要的操作:

const double RectangleMarginThickness = 10;
const double RectangleWidthReduction = 80;  // Rect width is client width less this
const double PaddingToCenter = RectangleWidthReduction/2 - RectangleMarginThickness;

double _rectangleWidthIncludingMargin;

private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
    double clientWidth = (Content as FrameworkElement).ActualWidth;
    var itemWidth = clientWidth - RectangleWidthReduction;
    _rectangleWidthIncludingMargin = itemWidth + (RectangleMarginThickness * 2);

    for (var i = 0; i < 10; i++)
    {
        MyStackPanel.Children.Add(new Rectangle { Width = itemWidth });
    }
}

private void LeftButton_Click(object sender, RoutedEventArgs e)
{
    SetNewHorizontalOffset(childOffset: -1);
}

private void RightButton_Click(object sender, RoutedEventArgs e)
{
    SetNewHorizontalOffset(childOffset: 1);
}

private void SetNewHorizontalOffset(int childOffset)
{
    double offset = MyScrollViewer.HorizontalOffset + PaddingToCenter;

    if (_rectangleWidthIncludingMargin > 0)
    {
        int currentChildIndex;
        if (childOffset < 0)
        {
            currentChildIndex =
                (int) Math.Ceiling(offset / _rectangleWidthIncludingMargin);
        }
        else
        {
            currentChildIndex =
                (int) Math.Floor(offset / _rectangleWidthIncludingMargin);
        }

        int newChildIndex = CoerceToRange(currentChildIndex + childOffset,
            0, MyStackPanel.Children.Count - 1);
        offset = newChildIndex * _rectangleWidthIncludingMargin - PaddingToCenter;
    }

    MyScrollViewer.ScrollToHorizontalOffset(offset);
}

private static int CoerceToRange(int value, int minimum, int maximum)
{
    return Math.Max(minimum, Math.Min(value, maximum));
}
相关问题