Есть ли лучший способ обновить элемент пользовательского интерфейса с помощью Dispatcher? - PullRequest
1 голос
/ 18 июля 2009

Я новичок со службами WCF, и я пытаюсь выяснить, есть ли лучший способ обновить элемент пользовательского интерфейса WPF (например, элемент управления Label), когда я асинхронно вызываю мою службу WCF.

Вот кусок кода:

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

    }

Как видите, я обновляю label1.Content асинхронным результатом, полученным с помощью AsyncCallback.

У меня вопрос: есть ли лучший или более правильный способ обновления элемента управления пользовательского интерфейса внутри этой асинхронной операции обратного вызова?

Заранее спасибо!

Ответы [ 2 ]

3 голосов
/ 18 июля 2009

Dispatcher.Invoke - это хороший способ обновления вашего пользовательского интерфейса. Действия пользовательского интерфейса должны выполняться потоком пользовательского интерфейса, об этом позаботится диспетчер.

Вы можете сделать свой код немного короче, используя лямбда-выражения:

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);
1 голос
/ 20 июля 2009

Можно сделать еще короче:

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, потому что он связывает фоновый поток без нужды.

...