Silverlight 应用程序中的跨线程访问无效

发布于 2024-11-18 23:55:36 字数 906 浏览 6 评论 0原文

我正在使用 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);
}

谢谢

I am using the Hammock framework to make asyncronous service calls from a Silverlight application to Rest services. In the 'completed' callback I am updating an ObservableCollection that is bound to a combobox on the view.

An 'Invalid cross-thread access' exception is being thrown in the 'OnPropertyChanged' event handler.

Is this becasue Hammock is not executing the callback on the UI thread? If not, why not? That would seem to be functionality that the framework should handle. Am I missing something? I sure do not want to handle the invoking of the UI thread myself in each completed handler.

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

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

太阳公公是暖光 2024-11-25 23:55:36

对于绑定属性和集合属性(而不是可观察集合中的子属性),只有 OnPropertyChanged 需要位于 UI 线程上。属性可以提前更改,但在调用 OnPropertyChanged 之前,UI 不会更改绑定。

我们所有的 ViewModel 都派生自我们创建的 ViewModelBase,该 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) 为您感到羞耻:)

For bound properties and properties that are collections (and not children in observable collections) it is only the OnPropertyChanged that needs to be on the UI thread. The properties can change earlier, but the UI will not change bindings until OnPropertyChanged is called.

All our ViewModels derive from a ViewModelBase we created that implements a helper SendPropertyChanged like below (so we never have to worry about cross-threading).

All our notify properties call that instead of calling OnPropertyChanged directly.

It also exposes a generally useful OnUiThread method so you can execute arbitrary code on the UI thread:

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

If you are not using MVVM, a) apologies and b) shame on you :)

卸妝后依然美 2024-11-25 23:55:36

Hammock 正在后台线程上运行您的请求,您绝对希望在那里运行它并自己处理返回 UI 线程的切换。否则,您会阻塞 UI 线程,并且您的应用程序将显得无响应。

要切换回 UI 界面,您需要调度程序上的句柄。最简单的获取方法就像这样

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

Hammock is running your request on a background thread, and you absolutely want to run it there and handle the switch back to the UI thread yourself. Otherwise you block the UI thread and your application will appear unresponsive.

To switch back to the UI tread you need a handle on the Dispatcher. The easiest way to get it is like so

Deployment.Current.Dispatcher.BeginInvoke(() => {
    this.MyBoundCollection = visibleData;
    OnPropertyChanged("MyBoundCollection");
});
看海 2024-11-25 23:55:36

我确实喜欢下面

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


  }

}

I did like below

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


  }

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文