0

我们TreeView的应用程序中有以下要求:

添加项目时:

  1. 新添加的项目滚动到视图中
  2. 新添加项的父项也滚动到视图中。
  3. 如果它们太远而无法同时看到,则该项目优先。

这看起来很简单,只需先将父级滚动到视图中,然后再滚动子级。

问题是当你这样称呼它时:

parent.BringIntoView();
child.BringIntoView();

...只有第二个似乎有任何效果。第一个基本被忽略。

然后我尝试将第二个调用包装在BeginInvoke()这样的调用中:

parent.BringIntoView();

Dispatcher.BeginInvoke((Action)(() => {
    child.BringIntoView();
}));

哪个确实有效,但是现在您可以明显地看到TreeView滚动两次;一次是为父母,然后是片刻之后,为孩子,这看起来很糟糕。

那么我怎样才能BringIntoView在没有使用调度程序的双重刷新问题的情况下进行背靠背调用呢?

4

1 回答 1

1

尝试使用Loaded事件而不是调度程序。根据这篇文章,它非常适合这样的情况:

...我们最初实现了 Loaded 事件,以便它会在窗口渲染之后但在处理任何输入之前触发。我们认为,如果它已经为输入做好了充分的准备,那么它就已经为加载时的初始化做好了充分的准备。但后来我们开始触发 Loaded 事件的动画,发现了问题;一瞬间,您会看到没有动画的内容渲染,然后您会看到动画开始。您可能并不总是注意到它,但是当您远程运行应用程序时尤其明显。

所以我们移动了 Loaded ,使它现在在布局和数据绑定有机会运行之后触发,但就在第一次渲染之前。(请注意,如果您在 Loaded 事件处理程序中执行任何使布局无效的操作,则可能需要在渲染之前重新运行它。)

换句话说,在 Loaded 上,您拥有有关元素物理布局的最新信息,但它还没有真正呈现,因此您应该避免任何“屏幕闪烁”问题。

编辑:要在评论中回答您的问题,您可以使用闭包将“本地”事件连接到当前方法,如下所示:

EventHandler handler = null;
handler = (sender, e) => {
    this.LayoutUpdated -= handler;  // only run once
    child.BringIntoView();
};
this.LayoutUpdated += handler;

通过在方法内部定义处理程序,您可以从内部访问方法的局部变量 ( child)。与通话非常相似Dispatcher

实际上,我不确定依赖是否LayoutUpdated是一个好主意。它经常发生,所以它可能会比你需要的更快地发射。Width例如,对于个人和Height设置,它会发生两次。另一个要研究的是ScrollViewer.ScrollChanged。或者您可以完全避免BringIntoView并尝试手动检查元素大小以计算滚动到的位置。

于 2013-07-12T23:54:07.810 回答