in Education by
At first I made an application that downloads a file from the entered link and displays information about progress, speed, etc. When I decided to change the application to download several files at the same time, I ran into a problem. So, in the interface there is a listbox in which there are several objects. When you select one of the objects and enter the link to the file, it should start downloading. When selecting another object, information about the previous object should change to the information of the selected one. I can also enter a link to the file there and then track the downloads of the two files by switching between objects. However, when switching information does not change. How to implement it? Model: public class Model { public WebClient webClient = new WebClient(); public Stopwatch stopWatch = new Stopwatch(); public event Action FileSizeChanged; public event Action DownloadBytesChanged; public event Action ProgressPercentageChanged; public event Action DownloadComplete; public string Name { get; set; } public void DownloadFile(string url, bool openAfterDownload) { if (webClient.IsBusy) throw new Exception("The client is busy"); try { var startDownloading = DateTime.UtcNow; webClient.Proxy = null; if (!SelectFolder(Path.GetFileName(url)+Path.GetExtension(url), out var filePath)) throw DownloadingError(); webClient.DownloadProgressChanged += (o, args) => { ProgressPercentageChanged?.Invoke(args.ProgressPercentage); FileSizeChanged?.Invoke(args.TotalBytesToReceive); DownloadBytesChanged?.Invoke(args.BytesReceived, DateTime.UtcNow - startDownloading); if (args.ProgressPercentage >= 100 && openAfterDownload) Process.Start(filePath); }; webClient.DownloadFileCompleted += (o, args) => DownloadComplete?.Invoke(); stopWatch.Start(); webClient.DownloadFileAsync(new Uri(url), filePath); } catch (Exception e) { throw DownloadingError(); } } public void CancelDownloading() { webClient.CancelAsync(); webClient.Dispose(); DownloadComplete?.Invoke(); } private static Exception DownloadingError() => new Exception("Downloading error!"); private static bool SelectFolder(string fileName, out string filePath) { var saveFileDialog = new SaveFileDialog { InitialDirectory = "c:\\", FileName = fileName, Filter = "All files (*.*)|*.*" }; filePath = ""; if (saveFileDialog.ShowDialog() != true) return false; filePath = saveFileDialog.FileName; return true; } } ViewModel: class MainVM : INotifyPropertyChanged { private string url; private RelayCommand downloadCommand; private RelayCommand cancelCommand; private double progressBarValue; private string bytesReceived; private string bytesTotal; private string speed; private string time; private string error; private long totalBytes; private Model selectedGame; public ObservableCollection Games { get; set; } public MainVM() { Games = new ObservableCollection(); Model Game1 = new Model { Name = "Name1" }; Model Game2 = new Model { Name = "Name2" }; Game1.FileSizeChanged += bytes => BytesTotal = PrettyBytes(totalBytes = bytes); Game1.DownloadBytesChanged += (bytes, time) => { BytesReceived = PrettyBytes(bytes); Speed = DownloadingSpeed(bytes, time); Time = DownloadingTime(bytes, totalBytes, time); }; Game1.ProgressPercentageChanged += percentage => ProgressBarValue = percentage; Game1.DownloadComplete += () => { BytesReceived = ""; BytesTotal = ""; Speed = ""; Time = ""; ProgressBarValue = 0; }; Game2.FileSizeChanged += bytes => BytesTotal = PrettyBytes(totalBytes = bytes); Game2.DownloadBytesChanged += (bytes, time) => { BytesReceived = PrettyBytes(bytes); Speed = DownloadingSpeed(bytes, time); Time = DownloadingTime(bytes, totalBytes, time); }; Game2.ProgressPercentageChanged += percentage => ProgressBarValue = percentage; Game2.DownloadComplete += () => { BytesReceived = ""; BytesTotal = ""; Speed = ""; Time = ""; ProgressBarValue = 0; }; Games.Add(Game1); Games.Add(Game2); } public Model SelectedGame { get => selectedGame; set { if (value == selectedGame) return; selectedGame = value; OnPropertyChanged(nameof(SelectedGame)); } } public string Error { get => error; private set { error = value; OnPropertyChanged(nameof(Error)); } } public string URL { get => url; set { url = value; OnPropertyChanged(nameof(URL)); } } public bool OpenDownloadedFile { get; set; } public double ProgressBarValue { get => progressBarValue; set { progressBarValue = value; OnPropertyChanged(nameof(ProgressBarValue)); } } public string BytesTotal { get => bytesTotal; private set { bytesTotal = value; OnPropertyChanged(nameof(BytesTotal)); } } public string BytesReceived { get => bytesReceived; private set { bytesReceived = value; OnPropertyChanged(nameof(BytesReceived)); } } public string Speed { get => speed; private set { speed = value; OnPropertyChanged(nameof(Speed)); } } public string Time { get => time; private set { time = value; OnPropertyChanged(nameof(Time)); } } public RelayCommand DownloadCommand => downloadCommand ?? (downloadCommand = new RelayCommand(DownloadButton_Click)); public RelayCommand CancelCommand => cancelCommand ?? (cancelCommand = new RelayCommand(CancelButton_Click)); private void DownloadButton_Click(object obj) { if (url == null && url == "") return; try { SelectedGame.DownloadFile(url, OpenDownloadedFile); } catch (Exception e) { Error = e.Message; } } private void CancelButton_Click(object obj) { if (url != null || url != "") SelectedGame.CancelDownloading(); } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string prop = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop)); } private static string PrettyBytes(double bytes) { if (bytes < 1024) return bytes + "Bytes"; if (bytes < Math.Pow(1024, 2)) return (bytes / 1024).ToString("F" + 2) + "Kilobytes"; if (bytes < Math.Pow(1024, 3)) return (bytes / Math.Pow(1024, 2)).ToString("F" + 2) + "Megabytes"; if (bytes < Math.Pow(1024, 4)) return (bytes / Math.Pow(1024, 5)).ToString("F" + 2) + "Gygabytes"; return (bytes / Math.Pow(1024, 4)).ToString("F" + 2) + "terabytes"; } public static string DownloadingSpeed(long received, TimeSpan time) { return ((double)received / 1024 / 1024 / time.TotalSeconds).ToString("F" + 2) + " megabytes/sec"; } public static string DownloadingTime(long received, long total, TimeSpan time) { var receivedD = (double) received; var totalD = (double) total; return ((totalD / (receivedD / time.TotalSeconds)) - time.TotalSeconds).ToString("F" + 1) + "sec"; } } View:

1 Answer

0 votes
by
You have to bind to the SelectedGame property. But to fully enable the switching between the downloading items you would have to refactor your code and move download specific attributes (e.g. progress, speed) into a separate class for each download (because SelectedGame doesn't expose all required attributes). This way each game or download item has it's own exposes its own download related information to the view. So I introduced a DownloadItem class that encapsulates donwnload related attributes or data. This class represents your game or download items that you can select in the ListView: class DownloadItem : INotifyPropertyChanged { public DownloadItem() { this.DisplayBytesTotal = string.Empty; this.Url = string.Empty; this.DownloadSpeed = string.Empty; this.ErrorMessage = string.Empty; this.Name = string.Empty; this.ProgressBytesRead = string.Empty; } [NotifyPropertyChangedInvocator] protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; private string name; public string Name { get => this.name; set { if (value == this.name) return; this.name = value; OnPropertyChanged(); } } private string url; public string Url { get => this.url; set { if (value == this.url) return; this.url = value; OnPropertyChanged(); } } private double progress; public double Progress { get => this.progress; set { this.progress = value; OnPropertyChanged(); } } private bool isOpenAfterDownloadEnabled; public bool IsOpenAfterDownloadEnabled { get => this.isOpenAfterDownloadEnabled; set { this.isOpenAfterDownloadEnabled = value; OnPropertyChanged(); } } private string progressBytesRead; public string ProgressBytesRead { get => this.progressBytesRead; set { if (value == this.progressBytesRead) return; this.progressBytesRead = value; OnPropertyChanged(); } } private long bytesTotal; public long BytesTotal { get => this.bytesTotal; set { if (value == this.bytesTotal) return; this.bytesTotal = value; OnPropertyChanged(); } } private string displayBytesTotal; public string DisplayBytesTotal { get => this.displayBytesTotal; set { if (value == this.displayBytesTotal) return; this.displayBytesTotal = value; OnPropertyChanged(); } } private string downloadSpeed; public string DownloadSpeed { get => this.downloadSpeed; set { if (value == this.downloadSpeed) return; this.downloadSpeed = value; OnPropertyChanged(); } } private string timeElapsed; public string TimeElapsed { get => this.timeElapsed; set { if (value == this.timeElapsed) return; this.timeElapsed = value; OnPropertyChanged(); } } private string errorMessage; public string ErrorMessage { get => this.errorMessage; set { if (value == this.errorMessage) return; this.errorMessage = value; OnPropertyChanged(); } } } Then, to encapsulate the download behavior, I modified the your Model class and renamed it to Downloader. Each DownloadItem is associated with one Downloader. Therefore the Downloader now handles the progress of its associated DownloadItem by itself and updates the DownloadItem accordingly: class Downloader { public DownloadItem CurrentDownloadItem { get; set; } public WebClient webClient = new WebClient(); public Stopwatch stopWatch = new Stopwatch(); public event Action FileSizeChanged; public event Action DownloadBytesChanged; public event Action ProgressPercentageChanged; public event Action DownloadComplete; public void DownloadFile(DownloadItem gameToDownload) { this.CurrentDownloadItem = gameToDownload; if (webClient.IsBusy) throw new Exception("The client is busy"); var startDownloading = DateTime.UtcNow; webClient.Proxy = null; if (!SelectFolder( Path.GetFileName(this.CurrentDownloadItem.Url) + Path.GetExtension(this.CurrentDownloadItem.Url), out var filePath)) { DownloadingError(); return; } webClient.DownloadProgressChanged += (o, args) => { UpdateProgressPercentage(args.ProgressPercentage); UpdateFileSize(args.TotalBytesToReceive); UpdateProgressBytesRead(args.BytesReceived, DateTime.UtcNow - startDownloading); if (args.ProgressPercentage >= 100 && this.CurrentDownloadItem.IsOpenAfterDownloadEnabled) Process.Start(filePath); }; webClient.DownloadFileCompleted += OnDownloadCompleted; stopWatch.Start(); webClient.DownloadFileAsync(new Uri(this.CurrentDownloadItem.Url), filePath); } public void CancelDownloading() { webClient.CancelAsync(); webClient.Dispose(); DownloadComplete?.Invoke(); } private string PrettyBytes(double bytes) { if (bytes < 1024) return bytes + "Bytes"; if (bytes < Math.Pow(1024, 2)) return (bytes / 1024).ToString("F" + 2) + "Kilobytes"; if (bytes < Math.Pow(1024, 3)) return (bytes / Math.Pow(1024, 2)).ToString("F" + 2) + "Megabytes"; if (bytes < Math.Pow(1024, 4)) return (bytes / Math.Pow(1024, 5)).ToString("F" + 2) + "Gygabytes"; return (bytes / Math.Pow(1024, 4)).ToString("F" + 2) + "terabytes"; } private string DownloadingSpeed(long received, TimeSpan time) { return ((double) received / 1024 / 1024 / time.TotalSeconds).ToString("F" + 2) + " megabytes/sec"; } private string DownloadingTime(long received, long total, TimeSpan time) { var receivedD = (double) received; var totalD = (double) total; return ((totalD / (receivedD / time.TotalSeconds)) - time.TotalSeconds).ToString("F" + 1) + "sec"; } private void OnDownloadCompleted(object sender, AsyncCompletedEventArgs asyncCompletedEventArgs) { } private void UpdateProgressPercentage(double percentage) { this.CurrentDownloadItem.Progress = percentage; } private void UpdateProgressBytesRead(long bytes, TimeSpan time) { this.CurrentDownloadItem.ProgressBytesRead = PrettyBytes(bytes); this.CurrentDownloadItem.DownloadSpeed = DownloadingSpeed(bytes, time); this.CurrentDownloadItem.TimeElapsed = DownloadingTime(bytes, this.CurrentDownloadItem.BytesTotal, time); } protected virtual void UpdateFileSize(long bytes) { this.CurrentDownloadItem.DisplayBytesTotal = PrettyBytes(bytes); } private void DownloadingError() => this.CurrentDownloadItem.ErrorMessage = "Downloading Error"; private static bool SelectFolder(string fileName, out string filePath) { var saveFileDialog = new SaveFileDialog { InitialDirectory = @"C:\Users\MusicMonkey\Downloads", FileName = fileName, Filter = "All files (*.*)|*.*", }; filePath = ""; if (saveFileDialog.ShowDialog() != true) return false; filePath = saveFileDialog.FileName; return true; } } I highly recommend to move the SaveFileDialog and the interaction to the view. This way you would eliminate view model dependencies to view related operations or logic. The refactored view model would look as followed: class TestViewModel : INotifyPropertyChanged { private RelayCommand downloadCommand; private RelayCommand cancelCommand; private DownloadItem selectedGame; public ObservableCollection Games { get; set; } private Dictionary DownloaderMap { get; set; } public TestViewModel() { this.Games = new ObservableCollection(); this.DownloaderMap = new Dictionary(); var game1 = new DownloadItem() {Name = "Name1"}; this.Games.Add(game1); this.DownloaderMap.Add(game1, new Downloader()); var game2 = new DownloadItem() {Name = "Name2"}; this.Games.Add(game2); this.DownloaderMap.Add(game2, new Downloader()); } public DownloadItem SelectedGame { get => selectedGame; set { if (value == selectedGame) return; selectedGame = value; OnPropertyChanged(nameof(SelectedGame)); } } public RelayCommand DownloadCommand => downloadCommand ?? (downloadCommand = new RelayCommand((param) => DownloadButton_Click(param), (param) => true)); public RelayCommand CancelCommand => cancelCommand ?? (cancelCommand = new RelayCommand((param) => CancelButton_Click(param), (param) => true)); private void DownloadButton_Click(object obj) { if (string.IsNullOrWhiteSpace(this.SelectedGame.Url)) return; if (this.DownloaderMap.TryGetValue(this.SelectedGame, out Downloader downloader)) { downloader.DownloadFile(this.SelectedGame); } } private void CancelButton_Click(object obj) { if (!string.IsNullOrWhiteSpace(this.SelectedGame.Url) && this.DownloaderMap.TryGetValue(this.SelectedGame, out Downloader downloader)) { downloader.CancelDownloading(); } } } Last step I updated the view's bindings to the new properties:

Related questions

0 votes
    I hava a main view, where you have one object and you can change the properties of that object. ... JavaScript Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Mar 4, 2022 in Education by JackTerrance
0 votes
    I am new at using Ansible. I tried to create directories and download files using Ansible. Should I use shell ... module is available? Select the correct answer from above options...
asked Jan 22, 2022 in Education by JackTerrance
0 votes
    I have a series of files on a private ftp file server that I am trying to download using mechanize ... Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Feb 28, 2022 in Education by JackTerrance
0 votes
    I am using Android Data Binding framework I have suppose an EditText for login form with username as below ... Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Jun 18, 2022 in Education by JackTerrance
0 votes
    how would i add multiple buttons to a window in c#? here's what i need to do... i'm getting ... , JavaScript Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Apr 19, 2022 in Education by JackTerrance
0 votes
    I want to change a couple of files at one time, iff I can write to all of them. I'm wondering if I ... to this problem look like? Select the correct answer from above options...
asked Jan 30, 2022 in Education by JackTerrance
0 votes
    How can i apply MVVM pattern in QtQuick applications? Can anybody give me any sample (simple) code? Thanks ... Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Feb 18, 2022 in Education by JackTerrance
0 votes
    What is MVVM architecture in Angular?...
asked Jun 30, 2021 in Technology by JackTerrance
0 votes
    What is the difference between MVP, MVC and MVVM?...
asked Apr 9, 2021 in Education by JackTerrance
0 votes
    How can you explain view and view model in MVVM?...
asked Apr 8, 2021 in Education by JackTerrance
0 votes
    Given my animation scene1. How can I pause it using a button named “Skip” using C# code behind, and play another animation scene2 after....
asked Dec 31, 2021 in Education by JackTerrance
0 votes
    I'm trying to query two JSON files that I put in a src/data directory. I installed gatsby- ... JavaScript Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked May 4, 2022 in Education by JackTerrance
0 votes
    I have huge JSON files in a folder approx 200 each of 300 mbs approx. I need to write Spring ... JavaScript Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked May 4, 2022 in Education by JackTerrance
...