WPF控件在独立的UI线程上?

5
我正在制作一个媒体播放器,现在我已经添加了实时搜索功能来搜索歌曲等(类似于WMP的实时搜索),即在输入关键词时进行搜索。
在搜索过程中,通过BackGroundProcess在另一个线程上访问数据库并加载IEnumerable。通过调用UIElement的分派程序来更新UI。
这个过程非常快,但是当您在文本框中输入搜索关键字时,似乎会有一些小延迟,这是因为Listbox.ItemSource更新需要一些时间。 例如,假设您想搜索"Adele",当您输入"a"时,搜索函数会加载"A"的结果,但是当我们输入整个单词"Adele"时,显示"d" "e" "l" "e"需要一些时间,并且这些字母之间存在轻微的延迟。
在这个搜索过程中,当我停止更新UI时,搜索过程似乎非常平滑,这只意味着对我来说Listbox正在锁定线程,因此当Listbox完成其处理时,播放器的其余UI都被卡住了。
因此,我相信如果我可以将Listbox控件放到另一个UI线程上,无论加载Listbox需要多长时间,都可以保持播放器的流畅性。 FYI:数据虚拟化已经存在,ListBox具有正在工作的UI虚拟化。
如何将ListBox控件放到另一个UI线程上? WPF,C#
提前感谢:)

实际上你不能...至少我所见过的任何可行的方式都不行。 - Servy
使用.NET 4.5?现在绑定上有一个很好的Delay属性,这可能对你来说是一个简单的胜利,基本上将其设置为100毫秒左右,只有当用户停止输入那么长时间(或按下回车键或文本框失去焦点)时才会更新绑定。 - Andy
1
只有一个UI线程。 - Wonko the Sane
2个回答

5

如果您在每次按键时都查询数据库 - 这会导致您在快速(甚至普通)输入时出现一些延迟。

最好对请求进行限流,我们使用这种方法来对调度线程进行限流。

public static class DispatcherExtensions
{
    private static Dictionary<string, DispatcherTimer> timers =
        new Dictionary<string, DispatcherTimer>();
    private static readonly object syncRoot = new object();

    public static string DelayInvoke(this Dispatcher dispatcher, string namedInvocation,
        Action action, TimeSpan delay,
        DispatcherPriority priority = DispatcherPriority.Normal)
    {
        lock (syncRoot)
        {
            if (String.IsNullOrEmpty(namedInvocation))
            {
                namedInvocation = Guid.NewGuid().ToString();
            }
            else
            {
                RemoveTimer(namedInvocation);
            }
            var timer = new DispatcherTimer(delay, priority, (s, e) =>
                {
                    RemoveTimer(namedInvocation);
                    action();
                }, dispatcher);
            timer.Start();
            timers.Add(namedInvocation, timer);
            return namedInvocation;
        }
    }


    public static void CancelNamedInvocation(this Dispatcher dispatcher, string namedInvocation)
    {
        lock (syncRoot)
        {
            RemoveTimer(namedInvocation);
        }
    }

    private static void RemoveTimer(string namedInvocation)
    {
        if (!timers.ContainsKey(namedInvocation)) return;
        timers[namedInvocation].Stop();
        timers.Remove(namedInvocation);
    } 


} 

假设您没有使用MVVM,您可以在按钮单击事件中轻松使用它,如下所示。
Dispatcher.CurrentDispatcher.DelayInvoke("UpdateSearch",
       YourMethodThatStartsBackgroundThread,Timespan.FromSeconds(1));

另外值得一提的是:如果您正在使用4.5,则可以查看绑定中的延迟属性(Delay)

@Xero 你可能会受益于研究微软的响应式扩展 (Rx)。我看到很多教程都强调了如何轻松地限制查询速度。 - Thelonias

1
在ASP.NET中,通常我们使用两种技术:
  • 等待最多3个字符开始搜索。
  • 在用户停止输入并开始搜索之前等待一些毫秒数,因为用户输入速度比某些毫秒数快,您的搜索条件将包含多个字符。这第二个选项是在用户开始输入时启动计时器,并在每次按键时将该计时器设置为零(不停止)。当用户停止输入2000毫秒(例如)时,执行搜索
一个完美的方法是结合这两种技术:只有在搜索条件中有3个或更多字符时才进行搜索,并使用计时器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接