托盘应用程序:从后台线程事件处理程序在主线程上创建UI

时间:2013-11-20 17:11:54

标签: c# multithreading winforms

我正在玩托盘应用程序。应用程序仅在系统托盘中运行,并且没有与之关联的Windows窗体。应用程序使用ManagementEventWatcher并在某些情况下显示警报窗口。

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    Application.Run(new AppContext());
}

...

public class AppContext : ApplicationContext
{
    private System.ComponentModel.IContainer _components;
    private NotifyIcon _notifyIcon;
    private ContextMenuStrip _contextMenu;
    private ManagementEventWatcher _regWatcher;


    public AppContext()
    {
        //Initialize context menu & tray icon

        _regWatcher = new ManagementEventWatcher(query);
        _regWatcher.EventArrived += new EventArrivedEventHandler(_regWatcher_EventArrived);
        _regWatcher.Start();
    }

    void _regWatcher_EventArrived(object sender, EventArrivedEventArgs e)
    {
        Alert.Show("Alert!", "My Message", someParam);
    }
}

...

public class Alert
{
    public static void Show(string title, string message, string extraInfo)
    {
        new Alert(title, message, extraInfo).ShowDialog();
    }

    private Alert(string title, string message, string extraInfo)
    {
        InitializeComponent();

        this.Icon = Properties.Resources._default;
        this.Text = title;
        this.label1.Text = message;
        this.linkLabel1.Text = extraInfo;
    }
}

有趣的是,它并没有抱怨没有以线程安全的方式访问UI。我想因为它只存在于这个后台线程中。但是稍后当Form尝试访问剪贴板时,它不起作用,因为它在MTA线程上运行。到目前为止,我发现的所有类似问题都已经有一个调用Invoke的表单,或者可以选择使用BackgroundWorker。在这种情况下,在主线程上创建和显示警报表单的最佳方法是什么?

1 个答案:

答案 0 :(得分:0)

感谢Idle_Mind与Andy Whitfield's blog post的链接,我已经找到了解决方案。我向AppContext类添加了一个私有全局SynchronizationContext。在构造函数中,我将其初始化为WindowsFormsSynchronizationContext的实例。然后,当注册表观察者的事件发生时,我可以将任务发回主线程。

public class AppContext : ApplicationContext
{
    private SynchronizationContext _uiThreadContext;
    ...
    public AppContext()
    {
        //Initialize context menu & tray icon

        _uiThreadContext = new WindowsFormsSynchronizationContext();

        _regWatcher = new ManagementEventWatcher(query);
        _regWatcher.EventArrived += new EventArrivedEventHandler(_regWatcher_EventArrived);
        _regWatcher.Start();

        ...
    }

    private void _regWatcher_EventArrived(object sender, EventArrivedEventArgs e)
    {
        ...
        _uiThreadContext.Post(new SendOrPostCallback(MyEventHandler), parameters) 
    }