【问题标题】:Why do ScrollViewer scroll to ItemsControl when an Item is removed?为什么在删除 Item 时 ScrollViewer 会滚动到 ItemsControl?
【发布时间】:2015-02-05 07:56:00
【问题描述】:

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();

就在我删除它之前,该行为发生了。

所以这不是在任何项目被删除时,而是只有当项目具有焦点时。因此,解决此问题的一种方法是在删除之前从项目中删除焦点。

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

编辑 n°2

我没有指定我使用的是框架 .Net v3.5。在@Joseph 发表评论后,我在 4.5.1 中进行了尝试,实际上这在 4.5.1 中没有发生。

【问题讨论】:

  • 我试过你的代码,你提到的滚动没有发生!
  • @Joseph 编辑了我的问题:它似乎发生在框架 3.5 但不是 4.5.1
  • 而你必须使用.net 3.5?
  • @Joseph 确实如此。我创建应用程序和组件以集成到管理高达 3.5 的 SCADA 软件中
  • 这是一个非常烦人的行为,即使您手动 ScrollToTop(),如果不添加延迟也将无法正常工作!希望有人可以帮助...ps:标记 .Net3.5

标签: wpf .net-3.5 scrollviewer itemscontrol


【解决方案1】:

似乎(显然)触发了一些额外的滚动事件,我试图检查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++;
        }

    }

这是一种土豆解决方案,但它的作用就像一个魅力。

【讨论】:

    【解决方案2】:

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

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

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

    我用上述方法进行了一个简单的测试,并实现了所需的行为,如果这不是您想要的,请告诉我?

    【讨论】:

    • 我试过你的代码,确实有效。不幸的是,这并不能解决我的问题,因为如果我将 ItemsControl 的 ItemsSource 绑定到 ViewModel 层中的 ObservableCollection,那么我就无法访问该元素来更改其焦点。这个解决方案也有点随机:放置一个睡眠 10 毫秒然后改变焦点的线程,它会在 10 次中工作 9 次。睡眠 20 毫秒,它永远不会工作。似乎正在发生的是,在 Items 集合更改后刷新 UI 之前处理 Focus() 调用。感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2011-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多