如何在Timer_Elapsed事件上运行RunWorkerAsync?

时间:2018-12-25 14:26:49

标签: c# wpf xaml

我正在尝试创建一个应定期连接到服务器并从中请求字符串的应用程序。这应该在后台完成,以免在尝试连接时冻结UI。为了实现这一点,我试图使用System.Timers.Timer类。但是,从不调用BackgroundConnect_DoWork。

为了测试BackgroundWorker的概念,我向应用程序添加了一个按钮,该按钮可以正常工作。按下时将触发BackgroundConnect_DoWork。

public partial class MainWindow : Window
{
    private BackgroundWorker backgroundConnect = new BackgroundWorker();

    public MainWindow()
    {
        InitializeComponent();            
        backgroundConnect = ((BackgroundWorker)this.FindResource("BackgroundConnect"));
        Timer reconnectTimer = new Timer();
        reconnectTimer.Interval = 500;                  //Twice per second
        reconnectTimer.Elapsed += ReconnectTimer_Elapsed;
        reconnectTimer.Enabled = true;
    }

    private void ReconnectTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        /*IPInput and PortInput are names of corresponding textboxes*/
        string InputHost = IPInput.Text;
        int InputPort = int.Parse(PortInput.Text);            
        IPEndPoint InputIPEndpoit = IpTools.GetIPEndPointFromHostName(InputHost, InputPort);    //That's working OK when tested separately
        backgroundConnect.DoWork += BackgroundConnect_DoWork;
        backgroundConnect.RunWorkerCompleted += BackgroundConnect_RunWorkerCompleted;
        backgroundConnect.RunWorkerAsync(InputIPEndpoit);            
    }        

    /* Handler for clicking of ConnectNow button */
    private void ConnectNow_MouseUp(object sender, MouseButtonEventArgs e)
    {      
        string InputHost = IPInput.Text;
        int InputPort = int.Parse(PortInput.Text);                           
        IPEndPoint InputIPEndpoit = IpTools.GetIPEndPointFromHostName(InputHost, InputPort);                
        backgroundConnect.RunWorkerAsync(InputIPEndpoit);                                   //This one is executed correctly when button is clicked
    }       

    /*Works OK on button click*/
    private void BackgroundConnect_DoWork(object sender, DoWorkEventArgs e)
    {            
        IPEndPoint InputIPEndpoit = (IPEndPoint)e.Argument;
        e.Result = SemTCPCommandClient.SendReceiveCommand(InputIPEndpoit, Settings.Default.StringToMicroscopeTM4000);           
    }

    private void BackgroundConnect_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //Some UI update                       
    }
}   

2 个答案:

答案 0 :(得分:0)

解决方案:

    public partial class MainWindow : Window
    {
     private BackgroundWorker backgroundConnect = new BackgroundWorker();

     public MainWindow()
     {
        InitializeComponent();            
        backgroundConnect = ((BackgroundWorker)this.FindResource("BackgroundConnect"));
        DispatcherTimer reconnectTimer = new DispatcherTimer();
        reconnectTimer.Interval = TimeSpan.FromMilliseconds(500); //Twice per second
        reconnectTimer.Tick += ReconnectTimer_Tick;
        reconnectTimer.Start();
     }

    private void ReconnectTimer_Tick(object sender, EventArgs e)
    {
        /*IPInput and PortInput are names of corresponding textboxes*/
        string InputHost = IPInput.Text;
        int InputPort = int.Parse(PortInput.Text);            
        IPEndPoint InputIPEndpoit = IpTools.GetIPEndPointFromHostName(InputHost, InputPort);    //That's working OK when tested separately
        //If below is not commented, than BackgroundConnect_DoWork is executed multiple times on each Timer_Tick
        //backgroundConnect.DoWork += BackgroundConnect_DoWork;
        //backgroundConnect.RunWorkerCompleted += BackgroundConnect_RunWorkerCompleted;
        if (backgroundConnect.IsBusy == false)                 //In order not to call async twice, or it will throw an exception
            backgroundConnect.RunWorkerAsync(InputIPEndpoit);                
    }        

    /* Handler for clicking of ConnectNow button */
     private void ConnectNow_MouseUp(object sender, MouseButtonEventArgs e)
     {      
        string InputHost = IPInput.Text;
        int InputPort = int.Parse(PortInput.Text);                           
        IPEndPoint InputIPEndpoit = IpTools.GetIPEndPointFromHostName(InputHost, InputPort);                
        backgroundConnect.RunWorkerAsync(InputIPEndpoit);                                   //This one is executed correctly when button is clicked
     }       

    /*Works OK on button click*/
    private void BackgroundConnect_DoWork(object sender, DoWorkEventArgs e)
    {            
        IPEndPoint InputIPEndpoit = (IPEndPoint)e.Argument;
        e.Result = SemTCPCommandClient.SendReceiveCommand(InputIPEndpoit, Settings.Default.StringToMicroscopeTM4000);           
    }

    private void BackgroundConnect_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //Some UI update                       
    }
}

想法是DispatcherTimer可以在UI上运行,而System.Timer.Timer不能。一些讨论可以在这里找到: https://social.msdn.microsoft.com/Forums/en-US/77909245-0ecd-4fbd-b831-d82dd6f6edd1/what-is-difference-between-timer-and-dispatcher-timer-?forum=csharplanguage

答案 1 :(得分:0)

您仍然必须将要注册到DoWorkRunWorkerCompleted事件的行从ReconnectTimer_Tick方法移动到构造函数。

如果您尝试将时间跨度设置为3000,并在BackgroundConnect_RunWorkerCompleted中设置一个断点,则您会看到当前在第一次运行后调用一次,第二次运行后调用两次,依此类推。

backgroundConnect = ((BackgroundWorker)this.FindResource("BackgroundConnect"));行做什么?我想你可以把它省略掉。

如果不是您的MainWindow,则还应该编写一个Close事件处理程序以停止计时器。

实际上,BackgroundWorker是WPF最初的概念。您不再需要使用它,因为现在C#中内置了异步编程的更强大功能。它仍在工作,我只是想指出这一点。