WebClient 이벤트핸들러에서 특정값을 할당하기 위한 방법을 문의드립니다.

WPF 구현시 고민이 되는 부분이지만, 질문 자체는 일반적인 C#과 관련된 질문이라서 이곳에 문의드립니다. 현재 File이라는 클래스를 Collection으로 갖는 SelectedFiles에서 각각의 File의 다운로드를 처리하면서 이를 View 화면에서 각각 다운로드 상태를 보여주려고 합니다.

문제는 DownloadProgressCallback(링크)에서 다운로드의 상황 정보(e.BytesReceived, e.TotalBytesToReceive, e.ProgressPercentage)를 처리합니다. 하지만 각각 File의 StatusFileDownload 에 해당 값을 할당해야 View에서 보여질텐데, 어떻게 접근해야 할지 고민이 됩니다. 좋은 방법이 있으시면 조언을 부탁드립니다.

namespace WPF_ParsingXML.ViewModels.Helper
{
    public class DownloadHelper
    {
        static WindowsVM  vm;
        public DownloadHelper(WindowsVM _vm)
        {
            vm = _vm;
        }       
        public static void DownloadFile(ObservableCollection<File> SelectedFiles, string path)
        {
            foreach (var sw in SelectedFiles)
            {
                string fileName = Path.Combine(path, sw.FileName);
                Uri uri = new Uri(sw.OriginUri);

                using (WebClient client = new WebClient())
                {
                    client.DownloadProgressChanged += new 
                    DownloadProgressChangedEventHandler(DownloadProgressCallback);
                    client.DownloadFileAsync(uri, fileName);
                }
            }
        }

        private static void DownloadProgressCallback(object sender, DownloadProgressChangedEventArgs e)
        {         
            // 각각의 파일의 StatusFileDownload = e.ProgressPercentage;
        }       
    }
}

namespace WPF_ParsingXML.Models
{
  public class File
    {       
        public string FileName { get; set; }       
        public string OriginUri { get; set; }   
        public int StatusFileDownload { get; set; }       
    }
}

읽어주셔서 감사합니다.

좋아요 1

DownloadProgressChanged이벤트에 의해 수신된 값을 어떻게서든 최종적으로 화면에 표시를 해야 합니다. WPF에서 일반적인 접근은 바인딩인데, FileINotifyPropertyChanged인터페이스를 구현해서 속성이 변경되었을 때 화면에 표시되도록 하면 좋습니다. 예를 들어 아래처럼 만들 수 있습니다.

public class FileInfo : INotifyPropertyChanged
{
    private long _bytesReceived;
    private long _totalBytesToReceive;
    private DownloadState _downloadState;

    public event PropertyChangedEventHandler? PropertyChanged;

    public string Filename { get; }
    public string OriginUri { get; }
    public long BytesReceived {
        get => _bytesReceived;
        set
        {
            _bytesReceived = value;
            OnPropertyChanged(nameof(BytesReceived));
        }
    }
    public long TotalBytesToReceive
    {
        get => _totalBytesToReceive;
        set
        {
            _totalBytesToReceive = value;
            OnPropertyChanged(nameof(TotalBytesToReceive));
        }
    }

    public DownloadState DownloadState
    {
        get => _downloadState;
        set
        {
            _downloadState = value;
            OnPropertyChanged(nameof(DownloadState));
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public FileInfo(string filename, string originUri)
    {
        this.Filename = filename;
        this.OriginUri = originUri;
    }
}

public enum DownloadState
{
    Wait,
    Downloading,
    DownloadComplete
}
좋아요 2

파일 다운로드 하는 로직은 예시로 간단히 만들어봤습니다. 완전한 코드가 되려면 예외 처리라던가 취소 처리를
보완해야 합니다.

    public class FileDownloader
    {
        private IEnumerable<FileInfo> _files;

        public FileDownloader(IEnumerable<FileInfo> files)
        {
            _files = files;
        }

        public async Task DownloadAsync()
        {
            foreach (var file in _files)
            {
                if (file.DownloadState is not DownloadState.Wait)
                    continue;

                var c = new WebClient();
                c.DownloadProgressChanged += (s, e) =>
                {
                    if (file.DownloadState is DownloadState.Wait)
                        file.DownloadState = DownloadState.Downloading;

                    file.BytesReceived = e.BytesReceived;
                    file.TotalBytesToReceive = e.TotalBytesToReceive;
                };
                await c.DownloadFileTaskAsync(new Uri(file.OriginUri), file.Filename);
                file.DownloadState = DownloadState.DownloadComplete;
                c.Dispose();
            }
        }
    }

참고로 WebClient는 이제 사용되지 않습니다. HttpClient로 구현하는게 좋습니다.

좋아요 2

@dimohy 와… 정말 감사드립니다… 정말 많이 배우게 되네요!!

좋아요 1

@dimohy 님께서 언급하신 내용으로 HttpClient vs WebClient 관련 정보가 필요하신 분들이 있을 것 같아서 공유드립니다.

좋아요 2