在事件中正确使用 Dispatcher 来更新 UI [英] Using Dispatcher correctly in event to update UI

查看:38
本文介绍了在事件中正确使用 Dispatcher 来更新 UI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用调度程序更新事件的绑定集合.我刚遇到一个令人讨厌的问题,我在同一个事件中有两个不同的调度员,但它没有工作.使用调试器,它完全跳过了第一个调度程序中的代码.将整个事件放在单个调度程序中修复了它.我认为这是因为编译器如何处理它,谁能确认这一点 - 每个事件只有一个调度程序,至少在处理相同元素时?

I'm using dispatchers to update a bound collection from an event. I just ran into a nasty issue where I had two different dispatchers in the same event and it wasn't working. Using the debugger it was completely skipping over the code in the first dispatcher. Putting the entire event in a single dispatcher fixed it. I assume it's because of how the compiler handles it, can anyone confirm this - only one dispatcher per event, at least when dealing with the same elements?

这是代码,当它在(line == 0)之后到达等待时,它完全退出该函数.后来,当 line !=0 它运行旧式菜单"时很好.如果我将所有代码放在一个调度程序中,一切正常.

Here is the code, when it gets to the await after (line == 0), it exits the function completely. Later, when line !=0 it runs the "Old style menu" fine. If I put all of the code in a single dispatcher, everything works fine.

    private async void ProcessNLS(string parameters) // NET/USB List Info
    {
        if (parameters.Substring(0, 1) == "A" || (parameters.Substring(0, 1) == "U")) // ASCII x08/2010  Only
        {
            int line = Convert.ToInt32(parameters.Substring(1, 1));
            string text = parameters.Substring(3);

            // New Menu, Clear Old - Use Last Received/Holding Menu: See NLT bug
            if (line == 0)
            {
                await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    State.Menu.ServiceType = State.holdingMenu.ServiceType;
        ...
                    State.Menu.Items.Clear();
                });

                OnMenuTitleInfoChanged(new MenuTitleInfoChangedArgs(State.Menu));

                // Replace Network Top with custom menu
                if (State.Menu.LayerInfo == LayerTypes.NetworkTop)
                {
                    await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        State.Menu.Items.Clear();
                    });

        ...
                }

                // Get 1st Advanced Menu
                if (Device.SupportsAdvancedMenus & State.Menu.LayerInfo != LayerTypes.NetworkTop)
                {
        ...
                }

            }

            // Old style menu
            if (!Device.SupportsAdvancedMenus && State.Menu.LayerInfo != LayerTypes.NetworkTop)
            {
                NetworkMenuItem menuItem = new NetworkMenuItem(line, text);

                await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    State.Menu.Items.Add(menuItem);
                });

                OnMenuLoading(new MenuLoadingArgs(menuItem));
            }
        }

        // C - Track Cursor
        if (parameters.Substring(0,1) == "C")
        {
            if (parameters.Substring(1, 1)== "-")
            {
                // No Cursor
                // Sent when entering player screen
                await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
        ...
                    State.Menu.Items.Clear();

                    OnMenuTitleInfoChanged(new MenuTitleInfoChangedArgs(State.Menu));
                }
            }

        });
    }

像这样它会无缘无故地跳过调度员.如果我将整个内容放在一个调度程序中,它就可以正常工作.

Like this it would just jump over the dispatcher for no apparent reason. If I put the entire thing in a single dispatcher it works fine.

第二个问题,如果我有一个调度员的另一个事件,像这样:

A second question, if I have another event with a dispatcher, something like this:

        foreach (xxx)
        {
            if (xxx == yyy)
            {
                await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
                {
                    State.Menu.Items.Add(menuItem);
                });
            }
        }

是否最好将整个 foreach 循环包装在调度程序中,而不是在每次迭代需要时调用它?

Would it be preferable to instead wrap the entire foreach loop in a dispatcher rather then calling it when needed each iteration?

由于我原来的问题发生了变化,我已经通过将原始套接字侦听器任务包装在调度程序中来发布了一个包含更多细节和另一种可能的解决方案的新帖子

Since my original question has changed I've made a new post with more specifics and another possible solution by just wrapping the original socket listener task in a dispatcher

可能的问题解决方案同一方法中的多个 UI 调度程序?

*** 更新:

我认为 Raymond 是在正确的轨道上,虽然添加 Task 并没有解决它,但我注意到虽然它开始处理菜单的0"行,但在它设置新菜单之前,它会尝试处理下一行"1" 命令因为它没有正确的菜单状态而被忽略,它仍然没有被上一个命令设置.

I think Raymond is on the right track, though adding Task didn't fix it, I noticed although it starts processing line "0" of the menu, before it sets up the new menu it tries to process the next line "1" command which is ignored because it doesn't have the right menu state yet, it still hasn't been set by the previous command yet.

我不确定如何修复它,似乎我必须在较低级别进行等待,因此请确保在开始下一个命令之前它已完成一个命令(并且不确定为什么将整个 ProcessNLS 放在 UI 中调度程序工作),这有点复杂,因为我经历了多个级别,但流程如下:

I'm not sure how to fix it, it seems like I have to do an await at a lower level so be sure sure it full finishes one command before starting the next (and not sure why putting the whole ProcessNLS in UI dispatcher works), it's a little complicated since I go through multiple levels but here is the flow:

        socket = new StreamSocket();
        try
        {
            await socket.ConnectAsync(new HostName(HostName), Port);
            OnConnect(new EventArgs());
            await Task.Factory.StartNew(WaitForMessage);
        }
        catch (Exception ex)
        {
            OnConnectionFail(new EventArgs());
        }

前往:

private async void WaitForMessage() 
{
...

                foreach (var message in messages)
                {
                    if (string.IsNullOrWhiteSpace(message))
                        continue;

                    ProcessMessage(message);
                }

}

前往

    private void ProcessMessage(string message, string optionalFlags = "")
    {
...
            case "NLS": // NET/USB List Info
                ProcessNLS(parameters);
                break;       
    }

到最后

 private async void ProcessNLS(string parameters) // NET/USB List Info

我的替代解决方案是在 UI 调度程序中的 WaitForMessage 下进行 ProcessMessage 调用

My alternate solution is to put to ProcessMessage call under WaitForMessage in a UI dispatcher

*** 更新 #2

我认为这可能有效,这是更新的流程,必须等待多个步骤,使用任务而不是无效

I think this may be working, here is the updated flow, have to await multiple steps, use task instead of void

    private async void WaitForMessage()
    {
 ...
                foreach (var message in messages)
                {
                    if (string.IsNullOrWhiteSpace(message))
                        continue;

                    await ProcessMessage(message);
                }
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine("WaitForMessage Error: " + ex.Message);
            OnDisconnect(new EventArgs());
        }
    }

    private async Task ProcessMessage(string message, string optionalFlags = "")
    {
        ...
           case "NLS": // NET/USB List Info
                await ProcessNLS(parameters);
                break;

        }

    private async Task ProcessNLS(string parameters) // NET/USB List Info

推荐答案

问题出在这里:

private async void ProcessNLS(...)
        ^^^^^^^^^^

您声明了一个 async void 函数,这意味着当第一个 await 发生时,立即从函数返回,让其余的工作异步运行."如果您希望调用者能够等待您的函数完成,请将签名更改为 private async Task ProcessNLS(...).

You declared an async void function, which means "When the first await happens, return from the function immediately, and let the rest of the work run asynchronously." If you want the caller to be able to await on completion of your function, change the signature to private async Task ProcessNLS(...).

这篇关于在事件中正确使用 Dispatcher 来更新 UI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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