c# windows-runtime windows-store-apps

c# - Usar Dispatcher correctamente en caso de actualizar la interfaz de usuario



windows-runtime windows-store-apps (1)

El problema está aquí:

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

Usted declaró una función de async void , lo que significa que "cuando la primera await , regrese de la función de inmediato y deje que el resto del trabajo se ejecute de forma asíncrona". Si desea que la persona que llama pueda esperar al finalizar su función, cambie la firma a private async Task ProcessNLS(...) .

Estoy usando despachadores para actualizar una colección enlazada de un evento. Me encontré con un problema desagradable en el que tenía dos despachadores diferentes en el mismo evento y no funcionaba. Al utilizar el depurador, se saltó por completo el código en el primer despachador. Poner todo el evento en un solo despachador lo arregló. Supongo que es por la forma en que el compilador lo maneja, ¿alguien puede confirmar esto? ¿Solo un despachador por evento, al menos cuando se trata de los mismos elementos?

Aquí está el código, cuando llega a la espera después (línea == 0), sale de la función por completo. Más tarde, cuando line! = 0 ejecuta el "menú de estilo antiguo" bien. Si pongo todo el código en un único despachador, todo funciona bien.

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

De esta manera, simplemente saltaría al despachador sin razón aparente. Si pongo todo en un solo despachador, funciona bien.

Una segunda pregunta, si tengo otro evento con un despachador, algo como esto:

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

¿Sería preferible envolver el bucle foreach completo en un despachador en lugar de llamarlo cuando sea necesario en cada iteración?

Dado que mi pregunta original ha cambiado, he hecho una nueva publicación con más detalles y otra posible solución simplemente envolviendo la tarea original de escucha de socket en un despachador

¿Una posible solución para emitir con múltiples despachadores de UI con el mismo método?

*** Actualización:

Creo que Raymond está en el camino correcto, aunque agregar Tarea no lo solucionó, noté que aunque comienza a procesar la línea "0" del menú, antes de configurar el nuevo menú, intenta procesar el siguiente comando de línea "1" que se ignora porque aún no tiene el estado del menú correcto, todavía no ha sido configurado por el comando anterior.

No estoy seguro de cómo solucionarlo, parece que tengo que esperar en un nivel inferior, así que asegúrese de que completa un comando antes de comenzar el siguiente (y no estoy seguro de por qué funciona todo el procesador ProcessNLS en la interfaz de usuario) , es un poco complicado ya que paso por múltiples niveles, pero aquí está el flujo:

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

Va a:

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

Va a

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

para finalmente

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

Mi solución alternativa es poner la llamada a ProcessMessage en WaitForMessage en un despachador de UI

*** Actualización n. ° 2

Creo que esto puede estar funcionando, aquí está el flujo actualizado, tiene que esperar múltiples pasos, usar tareas en lugar de vacías

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

a

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

a

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