2011-07-08 78 views
5

我正在使用Hammock框架將異步服務從Silverlight應用程序調用到Rest服務。在'完成'回調中,我正在更新綁定到視圖上的組合框的ObservableCollection。Silverlight應用程序中的無效跨線程訪問

在'OnPropertyChanged'事件處理程序中引發'無效的跨線程訪問'異常。

這是因爲Hammock沒有在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。屬性可以更早更改,但在調用OnPropertyChanged之前,UI不會更改綁定。

我們所有的ViewModel派生自我們創建的ViewModelBase,它實現了像下面這樣的幫助器SendPropertyChanged(所以我們不必擔心跨線程)。

我們所有的通知屬性調用,而不是直接調用OnPropertyChanged。

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

如果你不使用MVVM,一)道歉和b)不要臉:)

它也暴露了一個普遍有用OnUiThread方法,這樣你就可以在UI線程上執行任意代碼

+0

我正在使用MVVM。我意識到需要在UI線程上的唯一代碼是OnPropertyChanged。但我沒有想到將BeginInvoke放在那裏。偉大的建議。 –

3

吊牀運行在後臺線程你的要求,你絕對要運行它,並處理切換回UI線程自己。否則,您會阻止UI線程,並且您的應用程序將顯示無響應。

要切換回UI界面,您需要Dispatcher上的句柄。得到它最簡單的方法是,像這樣

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

這是我做過什麼,什麼時候我說:「我肯定不想處理UI的調用線程自己我我的意思n每個完成的處理程序。「我知道Web服務執行需要在後臺線程上執行,但爲什麼Hammock不能在UI線程上調用回調,而不是在每個回調中都使用這些回調? –

+0

Winforms只有從UI線程才具有訪問UI元素的相同規則。根據我的經驗,使用.NET框架對傳統ASP SOAP Web服務執行異步Web服務調用時,我從來沒有處理過這個問題。我總是認爲(危險,我知道),這是因爲框架調用了UI線程的回調。我期待Hammock以同樣的方式工作。 –

+0

@Jim但是你的處理的響應內容將在UI線程上。因此,如果您將它加載到XML或JSON解析器中,所有這些都將綁定UI線程。我認爲他們正確的做法。 –

0

我不喜歡下面

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


    } 

} 
相關問題