2011-07-08 14 views
5

Estoy utilizando el marco de la hamaca para realizar llamadas de servicio asíncronas desde una aplicación de Silverlight a los servicios de descanso. En la devolución de llamada 'completada' estoy actualizando una ObservableCollection que está vinculada a un cuadro combinado en la vista.Acceso de hilos cruzados no válido en la aplicación Silverlight

Se está lanzando una excepción de 'Acceso cruzado no válido' en el controlador de eventos 'OnPropertyChanged'.

¿Esto se debe a que Hammock no está ejecutando la devolución de llamada en el hilo de la interfaz de usuario? ¿Si no, porque no? Eso parecería ser una funcionalidad que el marco debería manejar. ¿Me estoy perdiendo de algo? Estoy seguro de que no quiero manejar la invocación del subproceso de interfaz de usuario en cada controlador completo.

public void LoadMyData() 
{ 
    var request = new RestRequest(); 
    request.Path = "MyRestUrlText"; 

    var callback = new RestCallback(
     (restRequest, restResponse, userState) => 
     { 
     var visibleData = new ObservableCollection<MyDataType>(); 

     var myData = JsonConvert.DeserializeObject<MyDataType[]> restResponse.Content); 

     foreach (var item in myData) 
      visibleData .Add(item); 

     this.MyBoundCollection = visibleData; 
     OnPropertyChanged("MyBoundCollection"); 
    }); 

    var asyncResult = _restClient.BeginRequest(request, callback); 
} 

Gracias

Respuesta

8

Para las propiedades de la envolvente y las propiedades que son colecciones (y no los niños en colecciones observables) es sólo el OnPropertyChanged que tiene que estar en el hilo de interfaz de usuario. Las propiedades pueden cambiar antes, pero la interfaz de usuario no cambiará las vinculaciones hasta que se llame a OnPropertyChanged.

Todos nuestros modelos de vista se derivan de una ViewModelBase que creamos que implementa un ayudante SendPropertyChanged como se muestra a continuación (por lo que nunca tenemos que preocuparnos por el cross-threading).

Todas nuestras propiedades de notificación lo llaman en lugar de llamar directamente a OnPropertyChanged.

También expone un método OnUiThread generalmente útil para que pueda ejecutar código arbitrario en el hilo de interfaz de usuario:

protected delegate void OnUiThreadDelegate(); 

public event PropertyChangedEventHandler PropertyChanged; 

public void SendPropertyChanged(string propertyName) 
{ 
    if (this.PropertyChanged != null) 
    { 
     this.OnUiThread(() => this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName))); 
    } 
} 

protected void OnUiThread(OnUiThreadDelegate onUiThreadDelegate) 
{ 
    if (Deployment.Current.Dispatcher.CheckAccess()) 
    { 
     onUiThreadDelegate(); 
    } 
    else 
    { 
     Deployment.Current.Dispatcher.BeginInvoke(onUiThreadDelegate); 
    } 
} 

Si no está utilizando MVVM, a) disculpas y b) vergüenza en usted :)

+0

Estoy usando MVVM. Sabía que el único código que debe estar en el hilo de la interfaz de usuario es OnPropertyChanged. Pero no pensé en poner BeginInvoke allí. Gran sugerencia –

3

Hamaca está ejecutando su petición en un subproceso en segundo plano, y es absolutamente quieren correr allí y manejar el interruptor de nuevo a la interfaz de usuario de hilo sí mismo. De lo contrario, bloqueará el subproceso de interfaz de usuario y su aplicación no responderá.

Para volver a la banda de rodamiento de la UI, necesita un identificador en el Dispatcher. La manera más fácil de conseguirlo es como tan

Deployment.Current.Dispatcher.BeginInvoke(() => { 
    this.MyBoundCollection = visibleData; 
    OnPropertyChanged("MyBoundCollection"); 
}); 
+0

Eso es lo que hice y lo que me refería cuando dije "Claro que no quiero manejar la invocación de la interfaz de usuario de hilo de mi i n cada manejador completo ". Entiendo que la ejecución del servicio web debe ocurrir en un hilo de fondo, pero ¿por qué Hammock no puede invocar la devolución de llamada en el hilo de la interfaz de usuario en lugar de hacer que usted y yo lo hagamos explícitamente en cada devolución de llamada? –

+0

Winforms tiene las mismas reglas sobre los elementos de la IU de acceso solo desde el hilo de la interfaz de usuario. En mi experiencia haciendo llamadas de servicio web asíncronas a los servicios web ASP SOAP clásicos usando el framework .NET, nunca tuve que lidiar con este problema. Siempre asumí (peligroso, lo sé) que era porque el framework invocaba la devolución de llamada en el hilo de la interfaz de usuario. Esperaba que Hammock funcionara de la misma manera. –

+0

@Jim Pero luego su procesamiento del contenido de respuesta estaría en el hilo de UI. Entonces, si lo está cargando en un analizador XML o JSON, todo eso atará el hilo de la interfaz de usuario. Creo que lo hacen de manera correcta. –

0

lo hice, como a continuación

namespace IdleStateDetection 
{ 

    public partial class App : Application 
    { 

    private bool idle = true; 

    private System.Threading.Timer _sessionTimeOutTimer = null; 

    public App() 
    { 
     this.Startup += this.Application_Startup; 
     this.Exit += this.Application_Exit; 
     this.UnhandledException += this.Application_UnhandledException; 

     Application.Current.RootVisual.MouseMove += new MouseEventHandler(RootVisual_MouseMove); 
     Application.Current.RootVisual.KeyDown += new KeyEventHandler(RootVisual_KeyDown); 

     _sessionTimeOutTimer = new Timer(SessionTimeOutCheck, null, 20000, 60000); 
     InitializeComponent(); 
    } 

    private void Application_Startup(object sender, StartupEventArgs e) 
    { 

     this.RootVisual = new MainPage(); 
    } 


    void RootVisual_KeyDown(object sender, KeyEventArgs e) 
    { 
     idle = false; 

    } 

    void RootVisual_MouseMove(object sender, MouseEventArgs e) 
    { 
     idle = false; 

    } 

    private void SessionTimeOutCheck(object state) 
    { 
     if (Deployment.Current.Dispatcher.CheckAccess()) 
     { 
     ShowMessage(); 
     } 
     else 
     { 
     Deployment.Current.Dispatcher.BeginInvoke(()=>{ShowMessage();}); 
     } 

    } 

    private void ShowMessage() 
    { 
     if (idle == true) 
     { 
     MessageBox.Show("Idle"); 
     } 
    } 

    private void Application_Exit(object sender, EventArgs e) 
    { 

    } 

    private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) 
    { 
     // If the app is running outside of the debugger then report the exception using 
     // the browser's exception mechanism. On IE this will display it a yellow alert 
     // icon in the status bar and Firefox will display a script error. 
     if (!System.Diagnostics.Debugger.IsAttached) 
     { 

     // NOTE: This will allow the application to continue running after an exception has been thrown 
     // but not handled. 
     // For production applications this error handling should be replaced with something that will 
     // report the error to the website and stop the application. 
     e.Handled = true; 
     Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); }); 
     } 
    } 

    private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e) 
    { 
     try 
     { 
     string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace; 
     errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n"); 

     System.Windows.Browser.HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight Application " + errorMsg + "\");"); 
     } 
     catch (Exception) 
     { 
     } 
    } 


    } 

} 
Cuestiones relacionadas