我正在使用Hammock框架从Silverlight应用程序到Rest服务进行异步服务调用。在'完成'回调中,我正在更新一个ObservableCollection,它绑定到视图上的组合框。
“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);
}
由于
答案 0 :(得分:8)
对于作为集合的绑定属性和属性(而不是可观察集合中的子节点),只有OnPropertyChanged需要位于UI线程上。属性可以更早更改,但在调用OnPropertyChanged之前,UI不会更改绑定。
我们所有的ViewModel派生自我们创建的ViewModelBase,它实现了如下所示的助手SendPropertyChanged(所以我们永远不必担心跨线程)。
我们所有的通知属性都会调用它而不是直接调用OnPropertyChanged。
它还公开了一个通常有用的OnUiThread方法,因此您可以在UI线程上执行任意代码:
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,a)道歉并且b)羞辱你:)
答案 1 :(得分:3)
Hammock正在后台线程上运行您的请求,您绝对想在那里运行它并自己切换回UI线程。否则,您将阻止UI线程,您的应用程序将显示无响应。
要切换回用户界面,您需要在Dispatcher上设置一个句柄。获得它的最简单方法就是这样
Deployment.Current.Dispatcher.BeginInvoke(() => {
this.MyBoundCollection = visibleData;
OnPropertyChanged("MyBoundCollection");
});
答案 2 :(得分: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)
{
}
}
}
}