ListView не обновляется после изменения ObservableCollection - PullRequest
1 голос
/ 11 июня 2019

Я несколько часов пытался заставить мой ListView работать с ObservableCollection. Однако не повезло. Я также прочитал некоторые посты здесь и там и пытаюсь соответствовать, и все еще бесполезно. Пожалуйста, дайте мне указатель на то, где это пошло не так.

По сути, я пытаюсь разделить логику от VM до класса Helper. И логика этого класса обновит данные, но виртуальная машина не знает об этом.

Моя проблема заключается в копировании файловой функции, статус задания не меняет представление данных. Я попытался Messenger.Default.Send (от помощника) и Messenger зарегистрироваться (в ВМ), чтобы принять изменения, все же не повезло.

Я использую MVVM Light, 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;
        }
    }
}

Вот мой код для ВМ.

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");
    }
}

А вот и мой 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 не будет отражать изменения в View. Итак, я следую решению этих сообщений (чтобы использовать уведомление об изменении в классе модели), не работает для меня.

Для меня мой индивидуальный файл имеет большой размер, поэтому я вижу, что на моей виртуальной машине нет обновлений.
Если вы тестируете файл небольшого размера, вы не увидите разницы.

Я пытался использовать метод Messenger, он также не обновлялся при просмотре, но моя виртуальная машина может принимать входящие сообщения без проблем.

Ответы [ 2 ]

1 голос
/ 11 июня 2019

Нельзя одновременно обновлять пользовательский интерфейс и копировать файлы в одном потоке.

Вам следует либо выполнить копирование в фоновом потоке, либо использовать асинхронный 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;
    }
}

Обратите внимание, что вы должны изменить сигнатуру вашего метода, чтобы иметь возможность использовать ключевые слова async / await, которые были введены в .NET Framework 4.5 и C # 5.

Вам также следует подождатьасинхронные методы "полностью" :

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;
    }
}
0 голосов
/ 12 июня 2019

Благодаря Клеменсу и mm8 мне удалось изменить его на асинхронный и дождаться обновления пользовательского интерфейса.Я предполагаю, что мой способ реализации все еще разумен (сравните с mm8).

    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;
        }
    }
...