为什么 ScrollViewer 在移除 Item 时滚动到 ItemsControl?

Why do ScrollViewer scroll to ItemsControl when an Item is removed?

ScrollViewer 似乎有一个默认行为,当此 ItemsControl 的 Items 丢失一个元素时,它会滚动到 ItemsControl。

举个例子:

<ScrollViewer>
    <ItemsControl>
        <TextBlock Text="Something"/>
        <TextBlock Text="Something"/>
        <TextBlock Text="Something"/>
        <TextBlock Text="Something"/>
        <TextBlock Text="Something"/>
        <TextBlock Text="Something"/>
        <TextBlock Text="Something"/>
        <TextBlock Text="Something"/>
        <TextBlock Text="Something"/>
        <TextBlock Text="Something"/>
        <ItemsControl>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
            <Button Click="ButtonBase_OnClick">
                <TextBlock Text="Some item"/>
            </Button>
        </ItemsControl>
    </ItemsControl>
</ScrollViewer>

ButtonBase_OnClick 定义如下:

private void ButtonBase_OnClick(object sender, RoutedEventArgs e) {
    ((sender as Button).Parent as ItemsControl).Items.Remove(sender);
}

在执行任何操作之前,它看起来像这样:

但是,如果我单击一个按钮(它将从 ItemsControl 中删除):

如果我添加一项而不是删除一项,则不会发生这种情况。我怎样才能防止这种行为?

编辑

如果我添加这段代码就不会发生:

Loaded += (sender, args) => {
    new Thread(() => {
        Thread.Sleep(1500);
        Dispatcher.Invoke(new Action(() => {

            MyItemsControl.Items.Remove(MyItemsControl.Items[1]);
        }));
    }).Start();
};

但是,如果我添加:

(MyItemsControl.Items[1] as FrameworkElement).Focus();

就在我删除它之前,出现了这种行为。

所以它不是在任何项目被删除时,而是只有在项目具有焦点时。因此,解决此行为的一种方法是在删除 Item 之前将 Focus 从 Item 中移除。

有没有更方便的方法阻止它发生?

编辑 n°2

我没有指定我正在使用 .Net v3.5 框架。我在@Joseph 的评论后用 4.5.1 试了一下,确实这在 4.5.1 中没有发生。

这个问题真的很棘手,我不确定下面是否是确切的解决方案,但它确实是一种解决方法。

我所做的唯一更改是在单击事件中删除项目后添加一行(即将按钮或项目控件的焦点设置到父级或 Window)。

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        ((sender as Button).Parent as ItemsControl).Items.Remove(sender);

        this.Focus();
    }

我 运行 完成了一个具有上述要求的简单测试,如果这不是您要找的,请告诉我?

似乎(很明显)触发了一些额外的滚动事件,我试图检查 ScrollView 收到的所有事件,但我仍然没有想出解决这个错误的正确方法!

并且由于您需要在此处使用 .net 3.5,这是一个 Old School hack,我希望它能有所帮助

首先 添加一个 ScrollChanged 事件处理程序并命名您的 ScrollViewer

       <ScrollViewer x:Name="sc" ScrollChanged="Sc_OnScrollChanged">            
        <ItemsControl >              
            <TextBlock Text="Something"/>
            <TextBlock Text="Something"/>

其次将您的代码隐藏更改为以下内容

 private int _cpt;
    private double _save;
    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        ((sender as Button).Parent as ItemsControl).Items.Remove(sender);
        _cpt = 0;
    }              
    private void Sc_OnScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        if (_cpt < 3)
        {
            if (_cpt == 0)
            {
                _save = sc.VerticalOffset;
            }
            //sc.ScrollToTop();
            sc.ScrollToVerticalOffset(_save);
            _cpt++;
        }

    }

它有点像 Potato 解决方案,但它的效果非常好。