我正在开发一个非常简单的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
}
}