UWP adding UI control to list in non-uit thread

0

So as i'm learning more and more stuff of UWP and XAML i bumped into two issues, one is (i think) "navigation" related and the second a threading issue. What i'm trying to achieve is simple. I have two pages, one "home" and one "Settings". On the Home page i show the connected clients as Custom_Buttons. On the Settings page i can change some settings regarding the app and Connected Clients

Navigation Issue On my MainPage is setup all my declarations and object classes i need. When i navigate to a page i pass me (that is the MainPage) through to the page i'm loading so i can use the properties and objects in the that i declared on the MainPage. Then when i load the page i use the page event OnNavigatedTo to handle the passed MainPage and do local stuf with it. When i switch often between the pages the app crashes and opens the page app.g.i.vb and point to the following code:

#If Debug AndAlso Not DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION Then
    AddHandler Me.UnhandledException,
        Sub(sender As Global.System.Object, unhandledExceptionArgs As Global.Windows.UI.Xaml.UnhandledExceptionEventArgs)
            If Global.System.Diagnostics.Debugger.IsAttached Then
             **Here--->>>**   Global.System.Diagnostics.Debugger.Break()
            End If
        End Sub
#End If

And the navigation code:

Private Sub ListBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)

    If Home.IsSelected AndAlso Not ScenarioFrame.CurrentSourcePageType Is GetType(Home) Then
        BackButton.Visibility = Visibility.Collapsed
        ScenarioFrame.Navigate(GetType(Home), Me)
    ElseIf Settings.IsSelected AndAlso Not ScenarioFrame.CurrentSourcePageType Is GetType(Settings) Then
        BackButton.Visibility = Visibility.Visible
        ScenarioFrame.Navigate(GetType(Settings), Me)
    End If
End Sub

Threading Issue On the MainPage I declare a class i wrote called TCP_Server. This class has a StreamSocketListener that uses the event ConnectionReceived to accept new incoming clients. I then simply create a new Object that represents a UI form of the client and pass it the StreamSocket that comes in the Event Args in the sub new. In this way each object can handles it's own Read and Write directly from the StreamSocket Then i add this new object to a ObservableCollection(Of Object) which is held in the TCP_Server Class. This list is bound to the ItemsSource of a Canvas that i use on the HomePage which is not my MainPage.

Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)
    MyBase.OnNavigatedTo(e)

    If ButtonsList.ItemsSource = Nothing Then ButtonsList.ItemsSource = DirectCast(e.Parameter, MainPage).TCP_Server.Clients
End Sub

When i create this new object in the ConnectionReceived i get an error System.Exception: 'The application has called an interface that has been marshalled for another thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD)) '. It only works when i use the Dispatcher.RunAsync

Private Async Sub TCP_Listener_ConnectionReceived(sender As StreamSocketListener, args As StreamSocketListenerConnectionReceivedEventArgs) Handles TCP_Listener.ConnectionReceived

    'Check if the client already excists or not.
    Dim client As Client_Button = Clients.FirstOrDefault(Function(x) x.IPaddr = args.Socket.Information.RemoteAddress.ToString)

    rootPage.NotifyUser("New Client connected! : [" & args.Socket.Information.RemoteAddress.ToString & "] Total Connected clients = " & Clients.Count, NotifyType.Message)

    If client Is Nothing Then
        Await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Function()
                                                                                                         'Create New object
                                                                                                         Dim x As New Client_Button(args.Socket)

                                                                                                         'Create new task that runs Async to process incomming data
                                                                                                         Dim tsk As Task = Task.Run(Sub() x.ProcessClientAsync())

                                                                                                         'Add to the task list so we can stop it later on
                                                                                                         ClientTasks.Add(tsk)

                                                                                                         'Add it to the Clients List so we can work with the objects
                                                                                                         Clients.Add(x)
                                                                                                         Return True
                                                                                                     End Function)
    Else
        Await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Function()
                                                                                                         client = Nothing
                                                                                                         Clients.Remove(client)

                                                                                                         'Create New object
                                                                                                         Dim x As New Client_Button(args.Socket)

                                                                                                         'Create new task that runs Async to process incomming data
                                                                                                         Dim tsk As Task = Task.Run(Sub() x.ProcessClientAsync())

                                                                                                         'Add to the task list so we can stop it later on
                                                                                                         ClientTasks.Add(tsk)

                                                                                                         'Add it to the Clients List so we can work with the objects
                                                                                                         Clients.Add(x)
                                                                                                         Return True
                                                                                                     End Function)
    End If
End Sub
vb.net
multithreading
binding
navigation
uwp
asked on Stack Overflow Jul 11, 2017 by Gforse

1 Answer

1

For "Navigation Issue" you described here, navigation between pages several times it will crash, please try to set NavigationCacheMode of the page to Required or Enabled as follows:

Public Sub New()
    Me.InitializeComponent()
    Me.NavigationCacheMode = NavigationCacheMode.Required
End Sub

Details please reference remarks of Page class. If you still have issues please provide the details about the "UnhandledException" .

For "Threading Issue", using Core​Dispatcher is the correct way and this is by design. ConnectionReceived triggered in a non-UI thread, but you invoked UI thread inside this event handle, so you need Dispatcher.RunAsync. More details you can reference this similar thread.

answered on Stack Overflow Jul 12, 2017 by Sunteen Wu

User contributions licensed under CC BY-SA 3.0