我创建了一个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>
在门外,控件在启动时看起来像这样:
如果用户需要在工作中浏览其他SQL Server实例,用户将单击&#34;单击此处搜索本地网络&#34;按钮。单击此按钮将生成一个单独的线程,该线程将执行搜索,同时保持UI响应。在此期间,控件显示动画旋转&#34;等待&#34; gif,看起来像这样:
当控件完成搜索后,用户将能够点击任何已发现的服务器,就像在&#34; Local&#34;和&#34;最近&#34;部分:
但是,根据本地网络环境和其他条件,搜索并不总能找到所有可用的服务器。发生这种情况时,用户可以单击超链接按钮:&#34;手动将服务器添加到此列表&#34;。单击此超链接按钮时,DataTrigger会折叠超链接并将TextBox放在其位置,以便用户手动输入服务器名称:
出现文本框,允许用户输入用户名。 &#34; KeyDown&#34;有一个处理程序。监听ENTER键的文本框上的事件。当按下ENTER键时,服务器被添加到列表中,文本框被折叠,并且&#34; ScrollToBottom()&#34;调用主ScrollViewer控件上的方法,并选择新添加的项目。
以下是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通常不会像往常一样滚动到底部。根据控件的大小和列表中的服务器数量,它通常在中间或向下滚动(但不是一直),如下所示:
为什么ScrollViewer一直滚动到底部???
答案 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每次都正确滚动。