DataGrid 在其 ItemSource 更新时不创建行 & 有时重复条目
DataGrid not creating Rows when its ItemSource is updated & Sometimes Duplicate entries
我们正在制作一个 WPF 应用程序,它使用 FileSystemWatcher 来监视对用户选择的目录的更改并将更改输出到 DataGrid。
在我的 MainWindow() 构造函数中,我将我的 DataGrid 绑定到我通过 ItemSource 调用 _eventList 的列表。
当 OnChanged 或 OnRenamed FileSystemWatcher 事件发生时,我的应用成功地将四组字符串(更改类型、受影响的文件、路径、上次修改日期)写入我调用的数组 _event。这构成一个事件。然后,在创建这个 _event 数组之后,我通过调用一个名为 SetThreadSafe().
的单独方法来尝试处理我的 "The calling thread cannot access this object because a different thread owns it" 跨线程异常。
我认为这就是我崩溃的地方。在我的 SetThreadSafe 方法中发生的不需要的行为是我的 DataGrid 显示它包含在它的项目 属性 中,条目对应于每个事件。 DataGrid.Items 中的每个索引都绑定到我的 _eventList 的同义词(因为它应该),并且在 List/Items 属性 的每个索引中是数组中每个索引中的正确值。
所以,第一,我的 DataGrid 显示它在我的调试器中的项目 属性 中包含正确的数据,但这些行不会填充到我的 DataGrid 中。
有两个,有时,不是全部,SetThreadSafe 方法触发它的 IF 和 ELSE 条件,因此在我的 DataGrid 中添加一个重复条目。Items/_eventList。我不再收到跨线程异常,但我觉得这里明显遗漏了一些东西。
[更新:感谢 ASh,我的问题得到了解决。我重新编辑了我的代码以反映正确的调整。现在一切正常。]
C#:
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
using WinForms = System.Windows.Forms;
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 string[] _event;
private ObservableCollection<string[]> _eventList;
private bool _on;
#endregion
#region class Constructor
public MainWindow()
{
InitializeComponent();
_eventList = new ObservableCollection<string[]>();
DataGrid_DisplayOutput.ItemsSource = _eventList;
}
#endregion
#region FileSystemWatcher Methods
private void OnChanged(object source, FileSystemEventArgs e)
{
_event = new string[4];
_event[0] = e.ChangeType.ToString();
_event[1] = e.Name;
_event[2] = e.FullPath;
_event[3] = DateTime.Now.ToString();
SetThreadSafe();
}
private void OnRenamed(object source, RenamedEventArgs e)
{
_event = new string[4];
_event[0] = e.ChangeType.ToString();
_event[1] = e.Name;
_event[2] = e.OldFullPath;
_event[3] = DateTime.Now.ToString();
SetThreadSafe();
}
private delegate void SetCallback();
private void SetThreadSafe()
{
if (!CheckAccess())
{
SetCallback d = new SetCallback(SetThreadSafe);
Dispatcher.Invoke(d);
}
else
{
_eventList.Add(_event);
}
}
#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">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding [0]}" Header="Change" Width="89" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding [1]}" Header="Change" Width="89" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding [2]}" Header="Change" Width="89" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding [3]}" Header="Change" Width="89" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
代码中有多个地方需要修复
将 List<string[]>
更改为 ObservableCollection<string[]>
以修复 "rows won't populate in my DataGrid." 问题。与 List 不同,ObservableCollection 可以在项目 added/deleted
时通知 UI
_event
在构造函数中只创建一次 _event = new string[4];
。这意味着您只有 1 个数组并多次添加它并在执行 change/rename 时擦除值。我想你需要在 OnChanged
/OnRenamed
中创建新数组
DataGridTextColumn 的 none 配置了 "Binding" 属性。由于 DataGrid 中的每一行都显示一个数组,因此绑定应使用索引:
<DataGridTextColumn Binding="{Binding [0]}" Header="Change" Width="89" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding [1]}" Header="File" Width="89" IsReadOnly="True"/>
我们正在制作一个 WPF 应用程序,它使用 FileSystemWatcher 来监视对用户选择的目录的更改并将更改输出到 DataGrid。 在我的 MainWindow() 构造函数中,我将我的 DataGrid 绑定到我通过 ItemSource 调用 _eventList 的列表。
当 OnChanged 或 OnRenamed FileSystemWatcher 事件发生时,我的应用成功地将四组字符串(更改类型、受影响的文件、路径、上次修改日期)写入我调用的数组 _event。这构成一个事件。然后,在创建这个 _event 数组之后,我通过调用一个名为 SetThreadSafe().
的单独方法来尝试处理我的 "The calling thread cannot access this object because a different thread owns it" 跨线程异常。我认为这就是我崩溃的地方。在我的 SetThreadSafe 方法中发生的不需要的行为是我的 DataGrid 显示它包含在它的项目 属性 中,条目对应于每个事件。 DataGrid.Items 中的每个索引都绑定到我的 _eventList 的同义词(因为它应该),并且在 List/Items 属性 的每个索引中是数组中每个索引中的正确值。
所以,第一,我的 DataGrid 显示它在我的调试器中的项目 属性 中包含正确的数据,但这些行不会填充到我的 DataGrid 中。
有两个,有时,不是全部,SetThreadSafe 方法触发它的 IF 和 ELSE 条件,因此在我的 DataGrid 中添加一个重复条目。Items/_eventList。我不再收到跨线程异常,但我觉得这里明显遗漏了一些东西。
[更新:感谢 ASh,我的问题得到了解决。我重新编辑了我的代码以反映正确的调整。现在一切正常。]
C#:
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
using WinForms = System.Windows.Forms;
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 string[] _event;
private ObservableCollection<string[]> _eventList;
private bool _on;
#endregion
#region class Constructor
public MainWindow()
{
InitializeComponent();
_eventList = new ObservableCollection<string[]>();
DataGrid_DisplayOutput.ItemsSource = _eventList;
}
#endregion
#region FileSystemWatcher Methods
private void OnChanged(object source, FileSystemEventArgs e)
{
_event = new string[4];
_event[0] = e.ChangeType.ToString();
_event[1] = e.Name;
_event[2] = e.FullPath;
_event[3] = DateTime.Now.ToString();
SetThreadSafe();
}
private void OnRenamed(object source, RenamedEventArgs e)
{
_event = new string[4];
_event[0] = e.ChangeType.ToString();
_event[1] = e.Name;
_event[2] = e.OldFullPath;
_event[3] = DateTime.Now.ToString();
SetThreadSafe();
}
private delegate void SetCallback();
private void SetThreadSafe()
{
if (!CheckAccess())
{
SetCallback d = new SetCallback(SetThreadSafe);
Dispatcher.Invoke(d);
}
else
{
_eventList.Add(_event);
}
}
#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">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding [0]}" Header="Change" Width="89" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding [1]}" Header="Change" Width="89" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding [2]}" Header="Change" Width="89" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding [3]}" Header="Change" Width="89" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
代码中有多个地方需要修复
将 List<string[]>
更改为 ObservableCollection<string[]>
以修复 "rows won't populate in my DataGrid." 问题。与 List 不同,ObservableCollection 可以在项目 added/deleted
_event
在构造函数中只创建一次 _event = new string[4];
。这意味着您只有 1 个数组并多次添加它并在执行 change/rename 时擦除值。我想你需要在 OnChanged
/OnRenamed
none 配置了 "Binding" 属性。由于 DataGrid 中的每一行都显示一个数组,因此绑定应使用索引:
<DataGridTextColumn Binding="{Binding [0]}" Header="Change" Width="89" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding [1]}" Header="File" Width="89" IsReadOnly="True"/>