2011-07-08 17 views
5

Hammockフレームワークを使用して、SilverlightアプリケーションからRestサービスへの非同期サービス呼び出しを行っています。 'completed'コールバックでは、ビューのコンボボックスにバインドされているObservableCollectionを更新しています。Silverlightアプリケーションでクロススレッドアクセスが無効です

'OnPropertyChanged'イベントハンドラで「無効なクロススレッドアクセス」例外がスローされています。

これは、ハンモックがUIスレッドでコールバックを実行していないためですか?そうでない場合は、どうしてですか?それは、フレームワークが扱うべき機能性のようです。何か不足していますか?完成した各ハンドラで自分自身でUIスレッドの呼び出しを処理したくないのは確かです。

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

おかげで

答えて

8

のようなものです。プロパティは以前に変更できますが、UIはOnPropertyChangedが呼び出されるまでバインディングを変更しません。

私たちが作成したViewModelBaseは、以下のようにヘルパーSendPropertyChangedを実装しています(Cross-Threadingについて心配する必要はありません)。

すべてのnotifyプロパティは、OnPropertyChangedを直接呼び出すのではなく、それを呼び出します。あなたはあなたにMVVM、a)の謝罪およびb)恥を使用していない場合

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

:):あなたはUIスレッド上で任意のコードを実行できるよう

はまた、一般的に有用なOnUiThreadメソッドを公開します

+0

私はMVVMを使用しています。私は、UIスレッド上にある必要があるコードはOnPropertyChangedであることを知っていました。しかし私はそこにBeginInvokeを入れることを考えなかった。素晴らしい提案。 –

3

ハンモックは、バックグラウンドスレッドでリクエストを実行している、とあなたは絶対にそこにそれを実行して戻って自分自身をスレッドUIへの切り替えを処理します。そうしないと、UIスレッドがブロックされ、アプリケーションが応答しなくなります。

UIトレッドに戻すには、ディスパッチャーのハンドルが必要です。それを得るための最も簡単な方法は、それが唯一のUIスレッド上にある必要がありOnPropertyChangedをあるコレクション(と、観察コレクションにない子供)ですバウンドプロパティとプロパティのためにそう

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

のようでした完了したハンドラはそれぞれn個です。私はWebサービスの実行がバックグラウンドスレッドで行われる必要があることを理解していますが、なぜHammockはUIスレッドでコールバックを呼び出さず、コールバックごとに明示的にコールバックを呼び出すことができますか? –

+0

WinFormsには、UIスレッドからのアクセスUI要素についてのみ、同じ規則があります。私の経験では、.NETフレームワークを使用して従来のASP SOAP Webサービスを呼び出している非同期Webサービスを呼び出す際に、この問題に対処する必要はありませんでした。フレームワークがUIスレッドでコールバックを呼び出していたため、私はいつもそう思いました(危険です、私は知っています)。私はハンモックが同じように動作することを期待していました。 –

+0

@Jimしかし、応答コンテンツの処理はUIスレッド上に行われます。したがって、それをXMLまたはJSONパーサにロードして、すべてUIスレッドを縛り付けている場合はそうです。私は彼らが正しい方法でそれをすると思う。 –

0

私は私がやったと私は私は確信してUIの呼び出しは、自分自身に私のスレッドに処理したくない」と言ったときに私が何を意味するのか。つまり、以下の

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


    } 

} 
関連する問題