You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何每隔15秒从数据库拉取通知并在主线程显示弹窗?

解决WinForms子线程显示PopupNotifier的问题

你的核心问题是UI控件必须在创建它们的主线程上操作,而你之前的事件触发和子线程逻辑导致PopupNotifier在子线程中初始化和调用,这违反了WinForms的线程模型。虽然MessageBox可以临时绕过这个限制,但体验很差,我们用标准的跨线程UI更新方式来解决:

问题根源分析

你的ShowNotifications方法启动了一个子线程,在子线程中触发NewNotification事件,事件处理程序OnNewNotification就运行在这个子线程里。而PopupNotifier作为WinForms控件,只能在主线程(也就是创建窗体的线程)上创建和调用其UI方法,否则会出现异常或者显示异常。

解决方案:子线程拉取数据,主线程更新UI

我们把数据拉取的IO操作放在子线程(避免卡主线程),拉取完成后通过Control.Invoke切换到主线程上下文来显示弹窗,这是WinForms跨线程更新UI的标准做法。

修改后的完整代码示例

首先调整窗体的加载逻辑和线程启动方式:

// 负责显示通知的窗体
private Notifications notifications; // 保留Notifications实例引用

private void Student_Details_Load(object sender, EventArgs e)
{
    WinAPI.AnimateWindow(this.Handle, 200, WinAPI.BLEND);
    notifications = new Notifications();
    // 启动通知轮询线程
    StartNotificationPolling();
}

// 启动后台轮询线程的方法
private void StartNotificationPolling()
{
    Thread notificationThread = new Thread(() =>
    {
        while (true)
        {
            // 子线程中执行数据库拉取操作,不会阻塞主线程
            var latestNotifications = notifications.GetNotifications(StudentFound.Student.Person_ID);
            
            // 切换到主线程执行UI操作
            this.Invoke((MethodInvoker)delegate
            {
                ShowPopupNotifications(latestNotifications);
            });
            
            Thread.Sleep(15000); // 按照需求改为15秒间隔
        }
    });
    // 设置为后台线程,主窗体关闭时自动终止
    notificationThread.IsBackground = true;
    notificationThread.Start();
}

// 在主线程中显示弹窗的方法
private void ShowPopupNotifications(List<Notifications> notificationsList)
{
    if (notificationsList == null || notificationsList.Count == 0)
        return;
        
    PopupNotifier popup = new PopupNotifier();
    popup.Image = Properties.Resources.About_104px;
    popup.TitleText = "Smart School System";
    
    foreach (var item in notificationsList)
    {
        popup.ContentText = item.Notification_Text;
        popup.Popup();
        // 可选:添加小延迟避免弹窗堆叠在一起
        System.Threading.Thread.Sleep(1000);
    }
}

// 拉取数据的类方法(保持不变)
public List<Notifications> GetNotifications(string userFound)
{
    List<Notifications> allNotifications = new List<Notifications>();
    DataHandler dataHandler = new DataHandler();
    DataTable dataTable = dataHandler.GetNotifications(userFound);
    foreach (DataRow rowItem in dataTable.Rows)
    {
        allNotifications.Add(new Notifications(
            int.Parse(rowItem["Notification_Id"].ToString()),
            rowItem["Notification_text"].ToString(),
            rowItem["Person_ID"].ToString(),
            bool.Parse(rowItem["Notification_Status"].ToString())
        ));
    }
    return allNotifications;
}

关键改进点

  1. 分离IO操作和UI操作:数据库拉取在子线程执行,保证主线程不卡顿,用户操作流畅。
  2. 使用Control.Invoke切换线程:确保PopupNotifier的创建和Popup()调用都在主线程执行,符合WinForms线程模型。
  3. 后台线程设置:将轮询线程标记为后台线程,主窗体关闭时线程会自动终止,不会残留后台进程。

这样既满足了每隔15秒拉取通知的需求,又保证了弹窗在主线程正常显示,同时不会阻塞程序运行。

内容的提问来源于stack exchange,提问作者Crownpack07

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:20:49