Execute a delegate in the ui thread (using message pump)

。_饼干妹妹 提交于 2019-12-29 07:59:13

问题


I have a background thread that handles communication with an external service. Each time the background thread receives a message I'd like to pass it to the UI thread for further processing (displaying to user).

Currently I've made a thread safe message queue that is pooled periodically in Timer.Tick and filled in the background thread. But this solution is sub optimal.

Do you know how to use message pump to pass events from background thread to ui thread?


回答1:


There are a few techniques.

  1. Control.Invoke() (et al)

    I've found this winforms technique consistently easy to use, but be aware that there are some subtle rules you need to get right. I've tried to capture a general, working implementation that properly handles the rules in a code segment I've posted elsewhere on stackoverflow.

  2. SynchronizationContext

    I haven't needed to use this technique much, so I can't really say anything meaningful about it. You should know that it exists, however. I believe that it's an effective way to ensure something gets called in a particular thread's context, even if that thread is not the ui thread.

  3. DispatcherObject.Dispatcher

    If you're working with WPF, the WPF controls will generally derive from DispatcherObject to supply the Dispatcher object. This is a more feature-rich synchronization technique than the Control.Invoke(), but also more complex. Be sure to read the docs carefully.




回答2:


You can use Control.Invoke and use a delegate. The delegate will be executed on the thread that created the control.

http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx




回答3:


If your GUI thread has blocked and does not process any messages, you could use Application.DoEvents to force the GUI thread to process all awaiting messages on that thread.

To pump messages to the thread of the Control, certainly you can use the Control.BeginInvoke or Control.Invoke methods but note that Control.Invoke will block if the owning thread of the Control is currently blocking.




回答4:


You can use the WPF Dispatcher (class Dispatcher) in WindowsBase.dll too.




回答5:


Here is an example of using the Dispacther object in WPF with MSMQ.

The code behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Messaging;

namespace MSMQGui
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private string queueName = @".\private$\MyFunWithMSMQ";
        private MessageQueue queue = null;

        public MainWindow()
        {
            InitializeComponent();

            if (!MessageQueue.Exists(queueName))
                MessageQueue.Create(queueName,false);


           queue = new MessageQueue(queueName);
           queue.ReceiveCompleted += receiveCompleted;

        }

        private void btnAddMessage_Click(object sender, RoutedEventArgs e)
        {
            string message = txtMessage.Text;
            txtMessage.Text = String.Empty;

            queue.Send(message);

            MessageBox.Show("Message :" + message + " sent");
        }

        private void Populate(object sender, RoutedEventArgs e)
        {
            try
            {
                queue.BeginReceive(TimeSpan.FromSeconds(1)) ;     
            }
            catch (MessageQueueException)
            {
                MessageBox.Show("No message available");
            }
        }

        private void receiveCompleted(object source, ReceiveCompletedEventArgs e)
        {
            try
            {
                var message=queue.EndReceive(e.AsyncResult);



                Action<string> addMessage= (string msg) => {
                     ListViewItem item = new ListViewItem();
                     item.Content = msg;
                     lsvMessages.Items.Add(item);
                };

                this.Dispatcher.Invoke(addMessage, message.Body as string);
            }
            catch (MessageQueueException)
            {
                MessageBox.Show("No message available");
            }
        }
    }
}

The XAML:

<Window x:Class="MSMQGui.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"></ColumnDefinition>
            <ColumnDefinition Width="3*"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="1*"></RowDefinition>
            <RowDefinition Height="9*"></RowDefinition>      
            <RowDefinition Height="1*"></RowDefinition>
        </Grid.RowDefinitions>

        <!-- First row -->
        <Label x:Name="lblMessage" 
               Content="Message:" 
               HorizontalAlignment="Stretch" 
               VerticalAlignment="Top" 
               HorizontalContentAlignment="Right"
               Grid.Column="0" Grid.Row="0"
               ></Label>
        <StackPanel Grid.Column="1" Grid.Row="0" Orientation="Horizontal">
        <TextBox x:Name="txtMessage"  Width="200" HorizontalAlignment="Left" ></TextBox>
        <Button x:Name="btnAddMessage"  Content="Add message" Margin="5,0,0,0" Click="btnAddMessage_Click"></Button>
        </StackPanel>

        <!-- Second row -->
        <ListView x:Name="lsvMessages" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,5,0,0">
        </ListView>

        <!-- Third row-->
        <Button x:Name="btnPopulate" Grid.Column="1" Grid.Row="2" HorizontalAlignment="Right" Click="Populate" Content="Get messages from queque" Margin="5,0,0,0"></Button>

    </Grid>
</Window>


来源:https://stackoverflow.com/questions/1132472/execute-a-delegate-in-the-ui-thread-using-message-pump

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!