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

WPF中DataGrid绑定ItemSource后不生成行且偶现重复条目问题

解决WPF FileSystemWatcher绑定DataGrid的两个问题:界面不显示行 & 重复条目

问题回顾

我正在开发一款WPF应用,用FileSystemWatcher监控用户指定目录的变更,把变更信息输出到DataGrid中。在MainWindow构造函数里通过ItemSource把DataGrid绑定到ObservableCollection<string[]>类型的_eventList集合。当OnChanged或OnRenamed事件触发时,会把变更类型、受影响文件、路径、当前时间存入_event数组,然后调用SetThreadSafe()处理跨线程异常。

目前遇到两个异常问题:

  • 调试器显示DataGrid的Items属性有正确数据,但界面上不生成行;
  • SetThreadSafe()方法有时会同时触发IF和ELSE分支,导致_eventList/DataGrid.Items出现重复条目。

更新:感谢ASh的帮助,问题已解决,代码已修正,当前运行正常


问题原因分析与核心修复思路

1. 界面不显示行的根源

你的XAML里所有列的Header都重复设置为"Change",这不是直接原因,但用字符串数组作为绑定项存在WPF绑定通知的潜在问题——数组是值类型集合,UI对其属性变更的感知不如强类型实体类可靠。另外,手动定义列时要确保绑定路径完全匹配数据结构,否则UI无法正确渲染。

2. 重复条目的诱因

类级别的_event字段是共享资源,当FileSystemWatcher事件高频触发时,非UI线程可能在Dispatcher.Invoke未执行完成时就覆盖了_event的值;同时SetThreadSafe()的逻辑是在非UI线程Invoke自身到UI线程,UI线程执行时又会走ELSE分支添加数据,加上字段被覆盖,就会出现重复甚至错误的条目。


修正后的完整代码

C# 后端代码(优化版)

我们改用强类型实体类存储变更信息,彻底避免共享字段的线程覆盖问题,同时简化线程安全逻辑:

using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;

namespace EyeInTheSky
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml + backend code for FileSystemWatcher project
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Fields
        private FileSystemWatcher _watcher;
        private ObservableCollection<FileChangeEvent> _eventList;
        private bool _on;
        #endregion

        #region 强类型事件实体
        public class FileChangeEvent
        {
            public string ChangeType { get; set; }
            public string FileName { get; set; }
            public string Path { get; set; }
            public string Time { get; set; }
        }
        #endregion

        #region 构造函数
        public MainWindow()
        {
            InitializeComponent();
            _eventList = new ObservableCollection<FileChangeEvent>();
            DataGrid_DisplayOutput.ItemsSource = _eventList;

            // 示例:初始化FileSystemWatcher(根据需求替换路径)
            _watcher = new FileSystemWatcher(@"C:\Your_Target_Directory");
            _watcher.Changed += OnChanged;
            _watcher.Renamed += OnRenamed;
            _watcher.EnableRaisingEvents = true;
        }
        #endregion

        #region FileSystemWatcher事件处理
        private void OnChanged(object source, FileSystemEventArgs e)
        {
            var newEvent = new FileChangeEvent
            {
                ChangeType = e.ChangeType.ToString(),
                FileName = e.Name,
                Path = e.FullPath,
                Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
            };
            AddEventToUiCollection(newEvent);
        }

        private void OnRenamed(object source, RenamedEventArgs e)
        {
            var newEvent = new FileChangeEvent
            {
                ChangeType = e.ChangeType.ToString(),
                FileName = e.Name,
                Path = e.OldFullPath,
                Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
            };
            AddEventToUiCollection(newEvent);
        }

        private void AddEventToUiCollection(FileChangeEvent eventItem)
        {
            if (!Dispatcher.CheckAccess())
            {
                // 非UI线程时,委托到UI线程执行
                Dispatcher.Invoke(() => AddEventToUiCollection(eventItem));
            }
            else
            {
                // UI线程直接添加,每个事件都是独立对象,不会被覆盖
                _eventList.Add(eventItem);
            }
        }
        #endregion
    }
}

WPF XAML(修正版)

修改列绑定路径为实体类属性,并设置清晰的表头:

<DataGrid x:Name="DataGrid_DisplayOutput" Grid.Column="1" HorizontalAlignment="Left" Margin="22,11,0,0" Grid.RowSpan="4" 
          VerticalAlignment="Top" Width="356" Height="392" IsTextSearchEnabled="True" RowBackground="#FFFFFAE8" 
          AlternatingRowBackground="White" HeadersVisibility="Column" AlternationCount="1" SelectionMode="Single" 
          IsReadOnly="True" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding ChangeType}" Header="Change Type" Width="89" IsReadOnly="True"/>
        <DataGridTextColumn Binding="{Binding FileName}" Header="File Name" Width="89" IsReadOnly="True"/>
        <DataGridTextColumn Binding="{Binding Path}" Header="Path" Width="89" IsReadOnly="True"/>
        <DataGridTextColumn Binding="{Binding Time}" Header="Time" Width="89" IsReadOnly="True"/>
    </DataGrid.Columns>
</DataGrid>

关键优化点说明

  1. 强类型实体替代数组:用FileChangeEvent类存储每个变更的信息,每个事件都是独立对象,彻底解决多线程下共享字段被覆盖的问题,同时让数据绑定更直观可靠。
  2. 移除类级共享字段:在事件处理方法中直接创建局部事件对象,确保每个事件的数据不会被后续事件篡改。
  3. 简化线程安全逻辑:AddEventToUiCollection方法接收具体的事件对象,非UI线程时委托到UI线程执行,避免了原逻辑中重复触发分支的问题。
  4. 修正XAML绑定:每个列绑定到实体类的对应属性,设置清晰的表头,确保UI能正确识别并渲染数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:27:21