Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/332.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# ItemsControl与其items源不一致-使用Dispatcher.Invoke()时出现问题_C#_Wpf_Dispatcher_Itemscontrol - Fatal编程技术网

C# ItemsControl与其items源不一致-使用Dispatcher.Invoke()时出现问题

C# ItemsControl与其items源不一致-使用Dispatcher.Invoke()时出现问题,c#,wpf,dispatcher,itemscontrol,C#,Wpf,Dispatcher,Itemscontrol,我正在编写一个WPF应用程序(MVVM模式,使用mvvmlighttoolkit)来读取和显示我公司使用的一组内部日志文件。目标是从多个文件中读取,从每一行中提取内容,将它们放在一个类对象中,并将所述对象添加到一个observateCollection。我已将我的GUI上的DataGrid的ItemsSource设置到此列表,以便它以整齐的行和列显示数据。我在第二个窗口中有一个ProgressBar控件,在文件读取和显示过程中,该控件将更新进度 安装程序 请注意,所有这些方法都被简化为基本方法,

我正在编写一个
WPF
应用程序(
MVVM
模式,使用
mvvmlighttoolkit
)来读取和显示我公司使用的一组内部日志文件。目标是从多个文件中读取,从每一行中提取内容,将它们放在一个类对象中,并将所述对象添加到一个
observateCollection
。我已将我的
GUI
上的
DataGrid
ItemsSource
设置到此列表,以便它以整齐的行和列显示数据。我在第二个窗口中有一个
ProgressBar
控件,在文件读取和显示过程中,该控件将更新进度

安装程序 请注意,所有这些方法都被简化为基本方法,删除了所有不相关的代码位

加载按钮

当用户选择包含日志文件的目录并单击此按钮时,过程开始。此时,我打开包含
ProgressBar
的窗口。我使用
BackgroundWorker
进行此过程

public void LoadButtonClicked()
{
    _dialogService = new DialogService();
    BackgroundWorker worker = new BackgroundWorker
    {
        WorkerReportsProgress = true
    };
    worker.DoWork += ProcessFiles;
    worker.ProgressChanged += Worker_ProgressChanged;
    worker.RunWorkerAsync();
}
ProcessFiles()方法

这将读取所选目录中的所有文件,并逐一进行处理。在这里,当启动进度条窗口时,我使用的是
Dispatcher.Invoke()

现在,这同样可以很好地工作,但问题是这会大大降低处理时间。以前,一个包含10000行的日志文件大约需要1s,而现在可能需要5-10倍的时间

我做错了什么,还是这是意料之中的事?有没有更好的方法来处理这个问题

public object SyncLock = new object();
在构造函数中:

BindingOperations.EnableCollectionSynchronization(LogLineList, SyncLock);
然后在您的函数中:

if (logLine.IsRobotLog)
{
    lock(SyncLock)
    {
        LogLineList.Add(logLine);
    }                               
}

这将使集合保持同步,无论您从哪个线程更新它。

可观察的集合不是线程安全的。因此,它以第二种方式工作,因为所有工作都是通过dispatcher在UI线程上完成的

您可以使用异步操作来简化这种类型的流。通过等待结果并更新结果的集合\进度,您将保持UI响应和代码干净

如果不能或不想使用异步操作,请批量更新集合并在UI线程上执行更新

编辑 举个例子

private async void Button_Click(object sender, RoutedEventArgs e)
{
    //dir contents
    var files = new string[4] { "file1", "file2", "file3", "file4" };
    //progress bar for each file
    Pg.Value = 0;
    Pg.Maximum = files.Length;
    foreach(var file in files)
    {                
        await ProcessOneFile(file, entries => 
        {
            foreach(var entry in entries)
            {
                LogEntries.Add(entry);
            }
        });
        Pg.Value++;
    }
}

public async Task ProcessOneFile(string fileName, Action<List<string>> onEntryBatch)
{
    //Get the lines
    var lines = await Task.Run(() => GetRandom());
    //the max amount of lines you want to update at once
    var batchBuffer = new List<string>(100);

    //Process lines
    foreach (string line in lines)
    {
        //Create the line
        if (CreateLogLine(line, out object logLine))
        {
            //do your check
            if (logLine != null)
            {
                //add
                batchBuffer.Add($"{fileName} -{logLine.ToString()}");
                //check if we need to flush
                if (batchBuffer.Count != batchBuffer.Capacity)
                    continue;
                //update\flush
                onEntryBatch(batchBuffer);
                //clear 
                batchBuffer.Clear();
            }
        }
    }

    //One last flush
    if(batchBuffer.Count > 0)
        onEntryBatch(batchBuffer);            
}
private async void按钮\u单击(对象发送方,路由目标)
{
//目录内容
var files=新字符串[4]{“file1”、“file2”、“file3”、“file4”};
//每个文件的进度条
Pg.值=0;
Pg.max=files.Length;
foreach(文件中的var文件)
{                
等待ProcessOneFile(文件,条目=>
{
foreach(分录中的var分录)
{
日志条目。添加(条目);
}
});
Pg.Value++;
}
}
公共异步任务ProcessOneFile(字符串文件名,操作onEntryBatch)
{
//接电话
var lines=wait Task.Run(()=>GetRandom());
//一次要更新的最大行数
var batchBuffer=新列表(100);
//生产线
foreach(行中的字符串行)
{
//创建线
if(CreateLogLine(线,输出对象logLine))
{
//请结账
if(logLine!=null)
{
//加
Add($“{fileName}-{logLine.ToString()}”);
//检查一下我们是否需要冲洗
if(batchBuffer.Count!=batchBuffer.Capacity)
继续;
//更新\刷新
onEntryBatch(批缓冲区);
//清楚的
batchBuffer.Clear();
}
}
}
//最后一次冲水
如果(batchBuffer.Count>0)
onEntryBatch(批缓冲区);
}

通过等待结果并更新集合\结果进度,您将保持UI响应速度快,代码干净。那么,您的意思是我应该
等待
CreateLogLine()
方法并更新集合吗?您希望等待流程文件并使用操作回调成批更新集合。我用一个例子更新了我的答案。这没有帮助。我还是有问题。你是说速度问题还是同步问题?就速度而言,您是否尝试将锁移出循环?不是速度问题,在加载后,每当我尝试滚动时(或任何操作,如最大化窗口,导致重新绘制
DataGrid
),锁仍会继续挂起。如果您可以批量更新而不是实时更新,则创建单独的集合(非obs)每5秒添加一次日志行-将项目源更改为从非obs集合创建的新obs集合LoadButtonClick方法位于查看代码背后?@shat90我将尝试一下,看看是否可行。@SirRufo,不,我使用的是
MVVM
,所以它位于主窗口的
ViewModel
中。该方法附加到按钮的click事件。@那么,您应该在ViewModel中定义ICommand属性,并将其绑定到按钮的Command属性。方法分配不是正确的MVVM绑定方式。仅供参考
BindingOperations.EnableCollectionSynchronization(LogLineList, SyncLock);
if (logLine.IsRobotLog)
{
    lock(SyncLock)
    {
        LogLineList.Add(logLine);
    }                               
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
    //dir contents
    var files = new string[4] { "file1", "file2", "file3", "file4" };
    //progress bar for each file
    Pg.Value = 0;
    Pg.Maximum = files.Length;
    foreach(var file in files)
    {                
        await ProcessOneFile(file, entries => 
        {
            foreach(var entry in entries)
            {
                LogEntries.Add(entry);
            }
        });
        Pg.Value++;
    }
}

public async Task ProcessOneFile(string fileName, Action<List<string>> onEntryBatch)
{
    //Get the lines
    var lines = await Task.Run(() => GetRandom());
    //the max amount of lines you want to update at once
    var batchBuffer = new List<string>(100);

    //Process lines
    foreach (string line in lines)
    {
        //Create the line
        if (CreateLogLine(line, out object logLine))
        {
            //do your check
            if (logLine != null)
            {
                //add
                batchBuffer.Add($"{fileName} -{logLine.ToString()}");
                //check if we need to flush
                if (batchBuffer.Count != batchBuffer.Capacity)
                    continue;
                //update\flush
                onEntryBatch(batchBuffer);
                //clear 
                batchBuffer.Clear();
            }
        }
    }

    //One last flush
    if(batchBuffer.Count > 0)
        onEntryBatch(batchBuffer);            
}