【问题标题】:Is there a better way to update UI Element with Dispatcher?有没有更好的方法来使用 Dispatcher 更新 UI 元素?
【发布时间】:2023-03-06 15:26:01
【问题描述】:

我是 WCF 服务的新手,我正在尝试找出在异步调用 WCF 服务时是否有更好的方法来更新 WPF UI 元素(如标签控件)。

这是一段代码:

    private void button1_Click(object sender, RoutedEventArgs e)
    {

        int result;
        CalculatorServiceClient proxy = new CalculatorServiceClient();
        AsyncCallback addOperation = (async_result) =>
        {
            result = proxy.EndAdd(async_result);

            Dispatcher.Invoke(DispatcherPriority.Normal,
                  new Action(
                      delegate()
                      {
                        label1.Content = result.ToString();
                      }
                  )
            );
            proxy.Close();
        };
        proxy.BeginAdd(Convert.ToInt32(txtNumber1.Text), Convert.ToInt32(txtNumber2.Text), addOperation, null);

    }

如您所见,我正在使用 AsyncCallback 获得的异步结果更新 label1.Content。

我的问题是,有没有更好或更正确的方法来刷新这个异步回调操作中的 UI 控件?

提前致谢!

【问题讨论】:

    标签: wpf wcf asynchronous dispatcher


    【解决方案1】:

    Dispatcher.Invoke 是更新 UI 的好方法。 UI 动作必须由 UI 线程执行,由 Dispatcher 负责。

    您可以使用 Lambda 表达式使代码更短:

    Dispatcher.Invoke((Action<string>) ((data) => { label1.Content = data; }));
    

    我还建议将代理放在 Using 语句中,这样就可以了:

    int result = 0;
    using (CalculatorServiceClient proxy = new CalculatorServiceClient()) {
        AsyncCallback callback = new AsyncCallback((asyncResult) => {
            result = proxy.EndAdd(asyncResult);
            Dispatcher.Invoke((Action<string>) ((data) => { label1.Content = data; }));
        });
    
        proxy.BeginAdd(Convert.ToInt32(txtNumber1.Text), Convert.ToInt32(txtNumber2.Text), callback, null);
    }
    

    或者如果你喜欢简短:

    int result = 0;
    using (CalculatorServiceClient proxy = new CalculatorServiceClient())
        proxy.BeginAdd(Convert.ToInt32(txtNumber1.Text), Convert.ToInt32(txtNumber2.Text), new AsyncCallback((asyncResult) => {
            result = proxy.EndAdd(asyncResult);
            Dispatcher.Invoke((Action<string>) ((data) => { label1.Content = data; }));
        }), null);
    

    【讨论】:

    • 实际上,WCF 是我知道的一种情况,你应该实现using 块。请参阅weblogs.asp.net/jezell/archive/2008/07/02/… 上的“Indisposable:WCF Gotcha #1 - Jesse Ezell 博客”
    • 哇,但我是否理解正确,您根本不应该使用 Close 方法,而是使用 Abort() 方法?这实际上并不是一个特定的使用问题,而是微软的一个奇怪的实现。
    • 没有。关闭没问题。问题是:“使用”中抛出异常1; Dispose 被调用; Dispose 调用“关闭”;关闭抛出异常 2。异常 1 丢失。我发布的博客上的模式有效。而且,是的,这是微软糟糕的设计。他们错过了一个。我找到了 Don Box 的一句话,他被问到在这种情况下调用 Close 时是否可以抛出异常,他认为可以,因为您已经在处理异常。他错过了我们正在处理一个不同的异常。
    • 谢谢大家的回答!!你们是否将 using 语句用于在 WPF 应用程序中调用 WCF 服务?在这种情况下,我没有抛出任何异常。我现在不应该把这个该死的 using 语句放在我的代码中吗?
    【解决方案2】:

    可以做得更短:

    using (var proxy = new CalculatorServiceClient())
    {
        proxy.BeginAdd(
             Convert.ToInt32(txtNumber1.Text),
             Convert.ToInt32(txtNumber2.Text),
             (asyncResult) =>
                 {
                     int result = proxy.EndAdd(asyncResult);
                     Dispatcher.BeginInvoke(
                       (Action) () => { label1.Content = result; });
                 },
             null);
    }
    

    诀窍是,Invoke/BeginInvoke 并不意味着您必须显式传递参数,编译器无论如何都会解决它。

    而且你不想使用 Invoke,因为它不需要后台线程。

    【讨论】:

      猜你喜欢
      • 2019-09-03
      • 1970-01-01
      • 2016-04-02
      • 2019-03-22
      • 2018-01-30
      • 2019-01-12
      • 2021-09-26
      • 1970-01-01
      相关资源
      最近更新 更多