ScrollViewer.ScrollToBottom无法完全滚动 [英] ScrollViewer.ScrollToBottom not completely scrolling

查看:58
本文介绍了ScrollViewer.ScrollToBottom无法完全滚动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个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.RowDefinitions>...< Grid.RowDefinitions/>< Textblock Text ="Local"/>< ListBox/></Grid><网格>...< Textblock Text ="Recent"/>< ListBox/></Grid>...< StackPanel></ScrollViewer></Grid> 

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

如果用户需要浏览工作中的其他SQL Server实例,则用户将单击单击此处以搜索本地网络"按钮.单击此按钮将产生一个单独的线程,该线程将执行搜索,同时保持UI响应.在这段时间内,控件显示一个动画旋转的"wait" gif,看起来像这样:

控件完成搜索后,用户将可以单击任何发现的服务器,就像在本地"和最近"部分中一样:

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

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

下面是KeyDown处理程序的代码:

 私有无效txtAddServer_KeyDown(对象发送者,KeyEventArgs e){如果(e.Key == Key.Escape){this.AddServerToListIsShown = false;}否则if(e.Key == Key.Enter){如果(this.MoreServers == null)MoreServers = new ObservableCollection< DatabaseServer>();如果(this.AllSQLServerNames == null)this.AllSQLServerNames = new ObservableCollection< string>();如果(!this.MoreServers.Any(x => string.Compare(txtAddServer.Text,xDescription,true)== 0)){this.AllSQLServerNames.Add(this.txtAddServer.Text.ToUpper());this.MoreServers.Add(new DatabaseServer {描述= this.txtAddServer.Text.ToUpper(),ServerName = this.txtAddServer.Text.ToUpper()});var foundItem = this.MoreServers.FirstOrDefault(x => string.Compare(x.Description,this.txtAddServer.Text,true)== 0);如果(foundItem!= null){this.SelectedServer = foundItem;}this.AddServerToListIsShown = false;mainScrollViewer.ScrollToBottom();}}} 

问题:

问题是,当手动添加服务器名称时,ScrollViewer通常不会完全滚动到底部.根据控件的大小和列表中的服务器数量,它通常滚动到中间或底部(但并非一直滚动),就像这样:

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

解决方案

在思考问题时,我尝试了几种不同的方法,以便更好地理解ScrollViewer为何未按要求滚动到底部.我做的第一件事是添加一个带有滚动"内容的测试按钮,该按钮将调用滚动查看器的"ScrollToBotton()"方法.当我这样做时,每次尝试滚动查看器时,它都会完美地滚动到底部.然后我想到,在调用"ScrollViewer.ScrollToBottom()"的同时,Dispatcher仍在完成其他UI操作.我将KeyDown处理程序中调用"ScrollToBottom()"的代码更改为以下代码:

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

这指示调度程序在尝试执行ScrollToBottom()之前先完成其队列中的所有后台优先级项目,这将导致ScrollViewer每次正确滚动.

I have created a WPF Control that will allow users of the application I'm developing to easily select a SQL Server in which to connect. On the control, there are three different categories of SQL Servers: Local, Recent, and More Servers on Network.

Local: The SQL Server instance on the machine in which the application is running.

Recent: SQL Servers instances the user has connected to recently.

More Servers on Network: SQL Server instances that are discovered by sending out a search request on the local network. The "More Servers on Network" is not automatically populated since it sometimes takes a while to populate. Most of the time, the users connect to recently used SQL Server instance, and don't need to browse the network.

In short, this control consists of several templated ListBox and TextBlock controls all nested inside of a main WPF ScrollViewer control. The ScrollViewer will grow to whatever size necessary to fit all the content, and the ScrollViewer will allow the user to scroll to any part of that content. Since the XAML for the entire control is quite lengthy, I have compressed it into pseudo code to get the point across:

<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>

Out of the gates, the control looks like this when it is launched:

If the user needs wants to browse other SQL Server instances out on the work, the user will click the "Click here to Search the local network" button. Clicking this button will spawn off a separate thread that will perform the searching, while keeping the UI responsive. During this time, the control display an animated spinning "wait" gif, and will look like this:

When the control has completed searching, the user will be able to click on any of the discovered servers, just like in the "Local" and "Recent" sections:

Depending on local network circumstances and other conditions however, the search does not always find all the available servers. When this happens, the user can click the hyperlink button: "Manually Add Server to this list". When this hyperlink button is clicked, a DataTrigger collapses the hyperlink and puts a TextBox up in its place for the user to manually enter the server name:

The textbox appears, and allows the user to enter the user name. There is an handler for the "KeyDown" event on the textbox that listens for the ENTER key. When the ENTER key is pressed, the server is added to the list, the textbox is collapsed, and the "ScrollToBottom()" method on the main ScrollViewer control is called and the newly added item is selected.

The following is the code-behind for the KeyDown handler:

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

The problem:

The problem is, when a server name is added manually, the ScrollViewer usually don't scroll all the way to the bottom as it should. Depending on the size of the control and how many servers are in the list, it usually scrolls somewhere in the middle or toward the bottom (but not all the way), like so:

Why doesn't the ScrollViewer scroll all the way to the bottom???

解决方案

In thinking through the problem, I tried a few different things to gain a better understanding as to why the ScrollViewer wasn't scrolling to the bottom as it should. The first thing I did was add a test button with the content of "Scroll" that would call the "ScrollToBotton()" method of the scroll viewer. When I did this, the ScrollViewer scrolled to the bottom perfectly every time I tried it. It then occurred to me that the Dispatcher was still in the process of completing other UI operations at the same time "ScrollViewer.ScrollToBottom()" was being called. I changed the code in my KeyDown handler that calls the "ScrollToBottom()" to the following:

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

    }

This instructs the Dispatcher to finish up all background priority items in its queue before attempting to perform the ScrollToBottom(), which causes my ScrollViewer to scroll correctly every time.

这篇关于ScrollViewer.ScrollToBottom无法完全滚动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆