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

WPF跨线程更新DataGrid遇异常:如何实现邮件数据动态同步?

解决WPF跨线程动态更新DataGrid的问题

核心问题分析

你遇到的两个异常根源分别是:

  • 集合无变更通知:Alert.alerts如果是普通List<Alert>,不会触发DataGrid的更新,导致控件和数据源不一致。
  • 跨线程UI访问:后台线程直接修改UI绑定的集合或操作控件,违反WPF的线程模型。

分步解决方案

1. 替换集合类型为ObservableCollection<Alert>

ObservableCollection实现了INotifyCollectionChanged接口,集合元素增删时会自动通知DataGrid更新。修改Alert类的静态集合:

public class Alert
{
    // 保留原Alert属性
    public string MessageId { get; set; }
    public DateTime Date { get; set; }
    public string From { get; set; }
    public string Type { get; set; }
    public string Priority { get; set; }

    // 将List替换为ObservableCollection
    public static ObservableCollection<Alert> alerts = new ObservableCollection<Alert>();

    // 保留原构造函数
    public Alert(string messageId, DateTime date, string from, string type, string priority)
    {
        MessageId = messageId;
        Date = date;
        From = from;
        Type = type;
        Priority = priority;
    }
}

2. 修正AdminWindow的初始化逻辑

直接绑定ObservableCollection,无需手动复制元素:

public AdminWindow()
{
    InitializeComponent();
    // 直接绑定ObservableCollection,后续集合变化会自动同步到DataGrid
    AlertTable.ItemsSource = Alert.alerts;
}

3. 后台线程通过Dispatcher更新UI集合

WPF中只有UI线程能修改UI绑定的集合,后台线程需通过Application.Current.Dispatcher切换到UI线程执行添加操作。同时优化邮件读取逻辑,避免重复添加相同MessageId的邮件:

public void MailKitLib(EmailParser emailParser)
{
    var cancellationTokenSource = new CancellationTokenSource();
    var token = cancellationTokenSource.Token;

    Task.Run(async () =>
    {
        while (!token.IsCancellationRequested)
        {
            using (var client = new ImapClient())
            {
                try
                {
                    await client.ConnectAsync(emailParser.ServerName, emailParser.Port, emailParser.isSSLuse, token);
                    await client.AuthenticateAsync(emailParser.Username, emailParser.Password, token);

                    var inbox = client.Inbox;
                    await inbox.OpenAsync(FolderAccess.ReadOnly, token);

                    Console.WriteLine("Total messages: {0}", inbox.Count);
                    Console.WriteLine("Recent messages: {0}", inbox.Unread);

                    // 记录已添加的MessageId,避免重复
                    var existingIds = Alert.alerts.Select(a => a.MessageId).ToHashSet();

                    for (int i = 0; i < inbox.Count; i++)
                    {
                        var message = await inbox.GetMessageAsync(i, token);
                        Console.WriteLine("ID:" + message.MessageId);

                        if (message.MessageId != null && !existingIds.Contains(message.MessageId))
                        {
                            Alert alert = new Alert(message.MessageId, message.Date.DateTime, message.From.ToString(), "PING", "LOW");
                            
                            // 切换到UI线程添加元素
                            Application.Current.Dispatcher.Invoke(() =>
                            {
                                Alert.alerts.Add(alert);
                            });
                        }
                    }

                    await client.DisconnectAsync(true, token);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("邮件读取异常: " + ex.Message);
                }

                // 每500毫秒轮询一次
                await Task.Delay(500, token);
            }
        }
    }, token);
}

4. 修正EmailParser构造函数(可选)

原Timer逻辑未启动,直接调用MailKitLib即可:

public EmailParser(bool isSSLuse, string username, string password)
{
    ServerName = "imap.outlook.com";
    Port = 993;
    IsSSLuse = isSSLuse;
    Username = username;
    Password = password;
    MailKitLib(this);
}

关键说明

  • Dispatcher的作用:确保集合的修改操作在UI线程执行,避免跨线程访问异常。
  • ObservableCollection的必要性:自动触发DataGrid的更新,无需手动刷新控件。
  • 去重逻辑:避免每次轮询都重复添加已读取的邮件,提升性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 01:37:01