另一个线程正在运行时,WPF阻止窗口关闭

时间:2015-12-17 23:31:20

标签: c# wpf multithreading

我正在开发一个非常简单的WPF应用程序。但我刚开始学习WPF。所需的行为是应用程序从队列中检索消息,按顺序处理它们,以及作为单位处理,这意味着它不应在处理消息时终止。我非常接近,除非我不知道如何在处理消息时达到阻止窗口关闭的效果。我能得到的最好的是事件处理程序 main_window_closing 。但我知道WPF不会更新UI,因为通过加入工作线程阻止了UI线程。关于在这种情况下如何保持UI响应的任何想法(至少在等待当前消息处理时显示"关闭..."消息)?谢谢。

public partial class MainWindow : Window
{
    //  observable collection bound to the listbox
    private ObservableCollection<DisplayMsg> msgCollection;
    //  seperate thread to run heavy syncing work
    private Thread _thread;
    //  terminate syncing in the middle
    private volatile bool _cancelled = false;

    public MainWindow()
    {
        InitializeComponent();
        //  bind listbox to the collection
        msgCollection = new ObservableCollection<DisplayMsg>();
        listBox.ItemsSource = msgCollection;
        //  bind UI events
        button_Sync.Click += new RoutedEventHandler(button_Sync_Click);
        button_Cancel.Click += new RoutedEventHandler(button_Cancel_Click);
        Closing += new CancelEventHandler(main_window_closing);
    }

    private void main_window_closing(object sender, CancelEventArgs e)
    {
        msgCollection.Add(new DisplayMsg() { Msg = "Closing ..." });
        _cancelled = true;
        button_Cancel.IsEnabled = false;
        button_Sync.IsEnabled = false;
        _thread.Join(TimeSpan.FromMinutes(1));
    }

    private void button_Sync_Click(object sender, RoutedEventArgs e)
    {
        displayMsg("Start syncing accounting data from SigmaMRP to Quickbooks ... ");
        _cancelled = false;
        //  only now we enable Cancel and disable Sync btn
        button_Cancel.IsEnabled = true;
        button_Sync.IsEnabled = false;
        //  start the work on a seperate thread
        work_thread();
    }

    private void button_Cancel_Click(object sender, RoutedEventArgs e)
    {
        displayMsg("Cancelling ...");
        //  mark cancelled and wait the current msg finish processing
        _cancelled = true;
        //  disable Cancel btn until reset
        button_Cancel.IsEnabled = false;
    }

    private void work_thread()
    {
        //  move the heavy work on another thread
        _thread = new Thread(() => postingToQuickbooks());
        _thread.Start();
    }

    private bool isQueueEmpty()
    {
        bool isQueueEmpty = false;
        // connect to a queue.
        var theQueue = new MessageQueue(ExternalPosting_Quickbooks.LQUEUE);
        try
        {
            // set Peek to return immediately.
            theQueue.Peek(new TimeSpan(0));
            // if an IOTimeout was not thrown, there is a message in the queue.
            isQueueEmpty = false;
        }
        catch (MessageQueueException e)
        {
            if (e.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)
            {
                // no message was in the queue.
                isQueueEmpty = true;
            }
            // handle other sources of MessageQueueException.
        }
        // handle other exceptions as necessary.
        // return true if there are no messages in the queue.
        return isQueueEmpty;
    }

    private void postingToQuickbooks()
    {
        try
        {
            var theQueue = new MessageQueue(ExternalPosting_Quickbooks.LQUEUE);
            ((XmlMessageFormatter)theQueue.Formatter).TargetTypeNames = new string[] { "System.String" };

            var queueEmpty = isQueueEmpty();
            while (!queueEmpty && !_cancelled)
            {
                //  retrieve queue msg if not empty and not cancelled
                var theData = theQueue.Receive();
                var theMsg = theData.Body.ToString();
                //  TODO: process message
                displayMsg(theMsg);
                queueEmpty = isQueueEmpty();
                Thread.Sleep(10000);
            }
            if (queueEmpty)
                displayMsg("Syncing finished.");
            if (_cancelled)
                displayMsg("Syncing was cancelled");
            resetBtns();
        }
        catch (Exception ex)
        {
            displayMsg(string.Format("Exception happenes: {0}({1})", ex.Message, ex.InnerException.Message));
            displayMsg("Syncing is terminated");
            return;
        }
    }

    private void displayMsg(string msg)
    {
        //  dispatch UI update from work thread to UI thread through binding of collection
        listBox.Dispatcher.BeginInvoke(new Action(delegate ()
        {
            msgCollection.Add(new DisplayMsg() { Msg = msg });
        }));
    }

    private void resetBtns()
    {
        //  dispatch UI update from work thread to UI thread to reset btn state
        Dispatcher.BeginInvoke(new Action(delegate ()
        {
            button_Cancel.IsEnabled = false;
            button_Sync.IsEnabled = true;
        }));
    }
}

public class DisplayMsg : INotifyPropertyChanged
{
    private string _msg;
    public string Msg
    {
        get { return _msg; }
        set
        {
            _msg = value;
            //  notify UI about data change
            OnPropertyChanged("Msg");
        }
    }

    public DisplayMsg() { }

    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion
}

}

0 个答案:

没有答案