C# 在ObservableCollection更改后ListView未更新

C# 在ObservableCollection更改后ListView未更新,c#,wpf,mvvm,mvvm-light,observablecollection,C#,Wpf,Mvvm,Mvvm Light,Observablecollection,几个小时以来,我一直在努力让我的ListView和ObservableCollection正常工作。然而,没有运气。我也在这里和那里读了一些帖子,试图匹配,但仍然没有好结果。请告诉我哪里出了问题 基本上,我要做的是将逻辑从VM拆分为Helper类。该类中的逻辑将更新数据,但VM不知道该数据。 我的问题是在复制文件功能中,作业状态不会更改视图数据。我尝试了Messenger.Default.Send(从帮助程序)和Messenger register(在VM中)来接受更改,但仍然没有成功。 顺便

几个小时以来,我一直在努力让我的ListView和ObservableCollection正常工作。然而,没有运气。我也在这里和那里读了一些帖子,试图匹配,但仍然没有好结果。请告诉我哪里出了问题 基本上,我要做的是将逻辑从VM拆分为Helper类。该类中的逻辑将更新数据,但VM不知道该数据。 我的问题是在复制文件功能中,作业状态不会更改视图数据。我尝试了Messenger.Default.Send(从帮助程序)和Messenger register(在VM中)来接受更改,但仍然没有成功。 顺便说一下,我使用的是MVVM灯光、WPF、C# 这是我的模型代码

public class MyFile : ViewModelBase
{
    public string fullFileName { get; set; }
    public string fileName { get; set; }


    private string _jobStatus;
    public string jobStatus
    {
        get { return _jobStatus; }
        set { Set(ref _jobStatus, value); }
    }
}
这是我的助手代码。

class FileHelper
{
    public List<MyFile> GetFileName(string dir)
    {
        List<MyFile> lstFiles = new List<MyFile>();

        foreach (var file in (new DirectoryInfo(dir).GetFiles()))
        {
            if (file.Name.ToLower().Contains("xls") && !file.Name.Contains("~$"))
                lstFiles.Add(new MyFile() { fullFileName = file.FullName, fileName = file.Name, jobStatus = "-" });
        }

        return lstFiles;
    }

    public bool CopyFiles(string destDir, List<MyFile> lstFiles)
    {
        try
        {
            int counter = 0;

            foreach (MyFile f in lstFiles)
            {
                f.jobStatus = "Copying";
                File.Copy(f.fullFileName, Path.Combine(destDir, f.fileName),true);
                f.jobStatus = "Finished";
                counter += 1;
                Console.WriteLine("M: " + DateTime.Now.ToString("hh:mm:ss") + "    " + counter);
                Messenger.Default.Send(counter, "MODEL");
            }

            return true;
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: " + e.Message);
            return false;
        }
    }
}
类FileHelper
{
公共列表GetFileName(字符串目录)
{
List lstFiles=新列表();
foreach(新目录信息(dir.GetFiles())中的var文件)
{
if(file.Name.ToLower().Contains(“xls”)和&!file.Name.Contains(“~$”)
添加(new MyFile(){fullFileName=file.FullName,fileName=file.Name,jobStatus=“-”});
}
返回文件;
}
公共bool复制文件(字符串destDir,列表lstFiles)
{
尝试
{
int计数器=0;
foreach(lstFiles中的myf文件)
{
f、 jobStatus=“复制”;
File.Copy(f.fullFileName,Path.Combine(destDir,f.fileName),true);
f、 jobStatus=“完成”;
计数器+=1;
Console.WriteLine(“M:+DateTime.Now.ToString”(“hh:mm:ss”)+”+计数器);
Messenger.Default.Send(计数器,“模型”);
}
返回true;
}
捕获(例外e)
{
Console.WriteLine(“错误:+e.Message”);
返回false;
}
}
}
这是我的虚拟机代码

public class MainViewModel : ViewModelBase
{
    public ICommand CmdJob { get; private set; }



    private ObservableCollection<MyFile> fileList;
    public ObservableCollection<MyFile> FileList
    {
        get { return fileList; }
        set { Set(ref fileList, value); }
    }


    private string counter;
    public string Counter
    {
        get { return counter; }
        set { Set(ref counter, value); }
    }


    public MainViewModel()
    {
        Messenger.Default.Register<int>(this, "MODEL", UpdateCounter);

        CmdJob = new RelayCommand<object>(Action_Job);
        Counter = "0";
    }

    private void UpdateCounter(int bgCounter)
    {
        Counter = bgCounter.ToString();
        RaisePropertyChanged("FileList");
        Console.WriteLine("VM: " + DateTime.Now.ToString("hh:mm:ss") + "    " + Counter);
    }

    private void Action_Job(object tag)
    {
        if (tag == null || string.IsNullOrEmpty(tag.ToString()))
            return;

        switch (tag.ToString())
        {
            case "GET": GetFile();  break;
            case "COPY": CopyFile(); break;
        }
    }


    private void GetFile()
    {
        Counter = "0";
        List<MyFile> myFs = new FileHelper().GetFileName(@"C:\Test\Original\");
        FileList = new ObservableCollection<MyFile>(myFs);
    }

    private void CopyFile()
    {
        if (new FileHelper().CopyFiles(@"C:\Test\Destination\", fileList.ToList()))
            Messenger.Default.Send("Files copying finished", "VM");
        else
            Messenger.Default.Send("Files copying failed", "VM");
    }
}
public类MainViewModel:ViewModelBase
{
公共ICommand CmdJob{get;private set;}
私有可观察收集文件列表;
公共可观测集合文件列表
{
获取{返回文件列表;}
set{set(ref fileList,value);}
}
专用字符串计数器;
公共字符串计数器
{
获取{返回计数器;}
set{set(ref计数器,值);}
}
公共主视图模型()
{
Messenger.Default.Register(这个“MODEL”,UpdateCounter);
CmdJob=新的RelayCommand(操作\作业);
计数器=“0”;
}
私有void更新计数器(int-bgCounter)
{
Counter=bgCounter.ToString();
RaisePropertyChanged(“文件列表”);
Console.WriteLine(“VM:+DateTime.Now.ToString”(“hh:mm:ss”)+”+计数器);
}
私有无效操作\u作业(对象标记)
{
if(tag==null | | string.IsNullOrEmpty(tag.ToString()))
返回;
开关(tag.ToString())
{
大小写“GET”:GetFile();break;
case“COPY”:CopyFile();break;
}
}
私有void GetFile()
{
计数器=“0”;
List myFs=new FileHelper().GetFileName(@“C:\Test\Original\”);
FileList=新的ObservableCollection(myFs);
}
私有void CopyFile()
{
if(new FileHelper().CopyFiles(@“C:\Test\Destination\”,fileList.ToList())
Send(“文件复制完成”,“虚拟机”);
其他的
senger.Default.Send(“文件复制失败”,“虚拟机”);
}
}
这是我的XAML。

<ListView Grid.Row="0" Grid.Column="0" ItemsSource="{Binding FileList}" Margin="5,5,0,5" HorizontalAlignment="Left" VerticalAlignment="Stretch" ScrollViewer.VerticalScrollBarVisibility="Visible">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="File Name" Width="170" DisplayMemberBinding="{Binding fileName}" />
                <GridViewColumn Header="Status" Width="170" DisplayMemberBinding="{Binding jobStatus}" >
                </GridViewColumn>
            </GridView>
        </ListView.View>
    </ListView>

    <StackPanel Grid.Row="0" Grid.Column="1" Orientation="Vertical">
        <Button Content="Get file list" Tag="GET" Command="{Binding CmdJob}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" Width="80" Height="25" Margin="0,50,0,50"/>
        <Button Content="Copy file" Tag="COPY" Command="{Binding CmdJob}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Tag}"  Width="80" Height="25" />
        <Label Content="{Binding Counter}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Height="25" FontWeight="Bold" Foreground="Red" Margin="0,50,0,0"/>
    </StackPanel> 

我通读了一些帖子,上面写着“变更”,ObservableCollection不会反映视图的变更。所以,我遵循这些帖子的解决方案(在模型类中使用NotifyChange),不适合我 对我来说,我的个人文件很大,所以我可以看到我的虚拟机上没有更新
如果使用较小的文件大小进行测试,则不会看到差异
我尝试使用Messenger方法,它也不会在视图上更新,但我的VM可以毫无问题地接受传入消息。

您不能同时在同一线程上更新UI和复制文件

您应该在后台线程上执行复制,或者使用异步API,例如:

public async Task<bool> CopyFiles(string destDir, List<MyFile> lstFiles)
{
    try
    {
        int counter = 0;

        foreach (MyFile f in lstFiles)
        {
            f.jobStatus = "Copying";
            using (Stream source = File.Open(f.fullFileName))
            using (Stream destination = System.IO.File.Create(Path.Combine(destDir, f.fileName)))
                await source.CopyToAsync(destination);
            f.jobStatus = "Finished";
            counter += 1;
            Console.WriteLine("M: " + DateTime.Now.ToString("hh:mm:ss") + "    " + counter);
            Messenger.Default.Send(counter, "MODEL");
        }

        return true;
    }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e.Message);
        return false;
    }
}

多亏了Clemens和mm8,我成功地将其更改为async并等待UI更新
我认为我的实现方式仍然是合理的(与mm8相比)。

private async void CopyFile()
{
var fh=new FileHelper();
bool result=wait fh.copyFileAsync(@“C:\Test\Destination\”,fileList.ToList());
如果(结果)
Send(“文件复制完成”,“虚拟机”);
其他的
senger.Default.Send(“文件复制失败”,“虚拟机”);
}
公共异步任务copyFileAsync(字符串destDir,列表lstFiles)
{
尝试
{
int计数器=0;
等待任务。运行(()=>
{
foreach(lstFiles中的myf文件)
{
f、 jobStatus=“复制”;
睡眠(500);
File.Copy(f.fullFileName,Path.Combine(destDir,f.fileName),true);
f、 jobStatus=“完成”;
计数器+=1;
Messenger.Default.Send(计数器,“模型”);
睡眠(500);
}
});
返回true;
}
捕获(例外e)
{
Console.WriteLine(“错误:+e.Message”);
返回false;
}
}

您是否尝试过实现INotifyPropertyChanged接口?@mahlatse,我正在使用MVVM Light,“set{set(ref xxxx})”为我完成任务。我的答案似乎有点不对劲,您是否尝试过此stackoverflow帖子。这是因为您正在UI t中同步设置这些属性
private Task CopyFile()
{
    var fh = new FileHelper();
    if (await fh.CopyFiles(@"C:\Test\Destination\", fileList.ToList()))
        Messenger.Default.Send("Files copying finished", "VM");
    else
        Messenger.Default.Send("Files copying failed", "VM");
}

private async void Action_Job(object tag)
{
    if (tag == null || string.IsNullOrEmpty(tag.ToString()))
        return;

    switch (tag.ToString())
    {
        case "GET": GetFile();  break;
        case "COPY": await CopyFile(); break;
    }
}
    private async void CopyFile()
    {
        var fh = new FileHelper();
        bool result = await fh.CopyFilesAsync(@"C:\Test\Destination\", fileList.ToList());

        if (result)
            Messenger.Default.Send("Files copying finished", "VM");
        else
            Messenger.Default.Send("Files copying failed", "VM");
    }

    public async Task<bool> CopyFilesAsync(string destDir, List<MyFile> lstFiles)
    {
        try
        {
            int counter = 0;

            await Task.Run(() =>
            {
                foreach (MyFile f in lstFiles)
                {
                    f.jobStatus = "Copying";
                    Thread.Sleep(500);
                    File.Copy(f.fullFileName, Path.Combine(destDir, f.fileName), true);
                    f.jobStatus = "Finished";
                    counter += 1;
                    Messenger.Default.Send(counter, "MODEL");
                    Thread.Sleep(500);
                }
            });

            return true;
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: " + e.Message);
            return false;
        }
    }