ScrollViewer.ScrollToBottom没有完全滚动

时间:2016-06-16 00:10:37

标签: c# wpf scrollviewer dispatcher

我创建了一个WPF控件,允许我正在开发的应用程序的用户轻松选择要连接的SQL Server。在控件上,有三种不同类型的SQL Server:网络上的本地,最近和更多服务器。

本地:运行应用程序的计算机上的SQL Server实例。

最近:最近用户连接的SQL Server实例。

网络上的更多服务器:通过在本地网络上发送搜索请求而发现的SQL Server实例。 "网络上的更多服务器"不会自动填充,因为它有时需要一段时间才能填充。大多数情况下,用户连接到最近使用的SQL Server实例,并且不需要浏览网络。

简而言之,此控件由几个模板化的ListBox和TextBlock控件组成,它们全部嵌套在主WPF ScrollViewer控件中。 ScrollViewer将增长到适合所有内容所需的任何大小,ScrollViewer将允许用户滚动到该内容的任何部分。由于整个控件的XAML非常冗长,我将其压缩为伪代码以获得重点:

<Grid>
    <ScrollViewer x:name="mainScrollViewer">
        <StackPanel>
           <Grid>
             <Grid.RowDefinitions>
               ...
             <Grid.RowDefinitions />  

              <Textblock Text="Local" />
              <ListBox />
           </Grid>

            <Grid>
             ...
              <Textblock Text="Recent" />
              <ListBox />
           </Grid>

          ...

        <StackPanel>


    </ScrollViewer>


</Grid>

在门外,控件在启动时看起来像这样:

The Control when it is first launched

如果用户需要在工作中浏览其他SQL Server实例,用户将单击&#34;单击此处搜索本地网络&#34;按钮。单击此按钮将生成一个单独的线程,该线程将执行搜索,同时保持UI响应。在此期间,控件显示动画旋转&#34;等待&#34; gif,看起来像这样:

The control while searching the network

当控件完成搜索后,用户将能够点击任何已发现的服务器,就像在&#34; Local&#34;和&#34;最近&#34;部分:

The control populated from SQL Instances discovered on the local network

但是,根据本地网络环境和其他条件,搜索并不总能找到所有可用的服务器。发生这种情况时,用户可以单击超链接按钮:&#34;手动将服务器添加到此列表&#34;。单击此超链接按钮时,DataTrigger会折叠超链接并将TextBox放在其位置,以便用户手动输入服务器名称:

The hyperlink to manually add a server

出现文本框,允许用户输入用户名。 &#34; KeyDown&#34;有一个处理程序。监听ENTER键的文本框上的事件。当按下ENTER键时,服务器被添加到列表中,文本框被折叠,并且&#34; ScrollToBottom()&#34;调用主ScrollViewer控件上的方法,并选择新添加的项目。

The textbox to add a server manually

以下是KeyDown处理程序的代码隐藏:

    private void txtAddServer_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Escape)
        {
            this.AddServerToListIsShown = false;
        }
        else if (e.Key == Key.Enter)
        {
            if (this.MoreServers == null)
                MoreServers = new ObservableCollection<DatabaseServer>();
            if (this.AllSQLServerNames == null)
                this.AllSQLServerNames = new ObservableCollection<string>();

            if (!this.MoreServers.Any(x => string.Compare(txtAddServer.Text, x.Description, true) == 0))
            {
                this.AllSQLServerNames.Add(this.txtAddServer.Text.ToUpper());
                this.MoreServers.Add(new DatabaseServer { Description = this.txtAddServer.Text.ToUpper(), ServerName = this.txtAddServer.Text.ToUpper() });

                var foundItem = this.MoreServers.FirstOrDefault(x => string.Compare(x.Description, this.txtAddServer.Text, true) == 0);
                if (foundItem != null)
                {
                    this.SelectedServer = foundItem;
                }

                this.AddServerToListIsShown = false;

                mainScrollViewer.ScrollToBottom();
            }
        }
    }

问题:

问题是,当手动添加服务器名称时,ScrollViewer通常不会像往常一样滚动到底部。根据控件的大小和列表中的服务器数量,它通常在中间或向下滚动(但不是一直),如下所示:

The ScrollViewer doesn't scroll all the way

为什么ScrollViewer一直滚动到底部???

1 个答案:

答案 0 :(得分:4)

在思考这个问题时,我尝试了一些不同的东西,以便更好地理解为什么ScrollViewer没有按原样滚动到底部。我做的第一件事是添加一个测试按钮,内容为&#34; Scroll&#34;这将调用&#34; ScrollToBotton()&#34;滚动查看器的方法。当我这样做时,ScrollViewer每次尝试时都会完美滚动到底部。然后我发现Dispatcher仍然在同时完成其他UI操作的过程中&#34; ScrollViewer.ScrollToBottom()&#34;被叫了。我更改了调用&#34; ScrollToBottom()&#34;的KeyDown处理程序中的代码。以下内容:

                Dispatcher.Invoke(new Action(() =>
               {
                   mainScrollViewer.ScrollToBottom();
                }), DispatcherPriority.ContextIdle, null);
            }
        }

    }

这指示Dispatcher在尝试执行ScrollToBottom()之前完成其队列中的所有后台优先级项,这会导致我的ScrollViewer每次都正确滚动。