none
How to make multiple processes in a ListView RRS feed

  • Question

  • Hi everyone...

    I'm trying to develop simple download manager like IDM (Internet Download Manager). I want to make each process of download runs in different row of same ListView like IDM does, but I don't know how to realize it. I have studied some download manager applications which I got from CodeProject site but I still didn't get it. Hope someone here can help me.

    Thanks in advance...



    • Edited by The_C Monday, May 30, 2016 6:01 PM
    Monday, May 30, 2016 12:44 PM

Answers

  • Hi,

    if you have multiple threads then you must be careful: You cannot change Windows Forms controls from outside the UI thread! So if you want to update any controls during runtime then you have to call invoke.

    One easy way to do this could be the use of events. So inside your class that is responsible for a download you define a simply event e.g. UpdateState. You can define your own StateEventArgs class which contains all the data required to update the state. And then you fire the event inside your thread whenever you want to update the state.

    When creating the new instance of your download, you register to the event with a anonymous method that simply calls invoke. So image your event handler would be
    OnStateUpdateEvent(object sender, StateEventArgs e) { .... } and your instanze with event would be downloader.StateUpdate then the call to add the listener would be

    downloader.StateUpdate += ((sender, args) => Invoke(new EventHandler<StateEventArgs>(OnStateUpdateEvent), sender, args));

    That as a hint so that you can update your gui without writing a lot of code that checks if an invoke is required and then calls an invoke. 

    With kind regards,

    Konrad

    Tuesday, May 31, 2016 8:15 AM

All replies

  • Hi The_C,
    About multi-thread download, you could refer to the following code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;
    using System.Threading;
    using System.Net;
    
    public class MultiDownload
    {
        #region Variable
        /// <summary>
        /// thread number
        /// </summary>
        private int _threadNum;
        /// <summary>
        /// file Size
        /// </summary>
        private long _fileSize;
        /// <summary>
        /// file Url
        /// </summary>       
        private string _fileUrl;
        /// <summary>
        /// file Name
        /// </summary>
        private string _fileName;
        /// <summary>
        /// save Path
        /// </summary>     
        private string _savePath;
        /// <summary>
        /// finished thread number
        /// </summary>
        private short _threadCompleteNum;   
        /// <summary>
        /// is finished
        /// </summary>
        private bool _isComplete;          
        /// <summary>
        /// downloaded size(dynamic)
        /// </summary>
        private volatile int _downloadSize;
        /// <summary>
        /// thread array
        /// </summary>
        private Thread[] _thread;        
    
        private List<string> _tempFiles = new List<string>();
        private object locker = new object();
    
        #endregion
        #region Properties
        /// <summary>
        /// File Name
        /// </summary>
        public string FileName
        {
            get
            {
                return _fileName;
            }
            set
            {
                _fileName = value;
            }
        }
        /// <summary>
        /// File Size
        /// </summary>
        public long FileSize
        {
            get
            {
                return _fileSize;
            }
        }
        /// <summary>
        /// Download Size(dynamic)
        /// </summary>
        public int DownloadSize
        {
            get
            {
                return _downloadSize;
            }
        }
        /// <summary>
        /// Is Complete
        /// </summary>
        public bool IsComplete
        {
            get
            {
                return _isComplete;
            }
        }
        /// <summary>
        /// Thread Number
        /// </summary>
        public int ThreadNum
        {
            get
            {
                return _threadNum;
            }
        }
        /// <summary>
        /// Save Path
        /// </summary>
        public string SavePath
        {
            get
            {
                return _savePath;
            }
            set
            {
                _savePath = value;
            }
        }
    
        #endregion
    
        /// <summary>
        /// Init
        /// </summary>
        /// <param name="threahNum">threah Num</param>
        /// <param name="fileUrl">file Url</param>
        /// <param name="savePath">save Path</param>
        public MultiDownload(int threahNum, string fileUrl, string savePath)
        {
            this._threadNum = threahNum;
            this._thread = new Thread[threahNum];
            this._fileUrl = fileUrl;
            this._savePath = savePath;
        }
    
        public void Start()
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_fileUrl);
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            _fileSize = response.ContentLength;
            int singelNum = (int)(_fileSize / _threadNum);      
            int remainder = (int)(_fileSize % _threadNum);      
            request.Abort();
            response.Close();
            for (int i = 0; i < _threadNum; i++)
            {
                List<int> range = new List<int>();
                range.Add(i * singelNum);
                if (remainder != 0 && (_threadNum - 1) == i)    
                    range.Add(i * singelNum + singelNum + remainder - 1);
                else
                    range.Add(i * singelNum + singelNum - 1);
                //download file
                int[] ran = new int[] { range[0], range[1] };
                _thread[i] = new Thread(new ParameterizedThreadStart(Download));
                _thread[i].Name = System.IO.Path.GetFileNameWithoutExtension(_fileUrl) + "_{0}".Replace("{0}", Convert.ToString(i + 1));
                _thread[i].Start(ran);
            }
        }
    
    
        private void Download(object obj)
        {
            Stream httpFileStream = null, localFileStram = null;
            try
            {
                int[] ran = obj as int[];
                string tmpFileBlock = System.IO.Path.GetTempPath() + Thread.CurrentThread.Name + ".tmp";
                _tempFiles.Add(tmpFileBlock);
                HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(_fileUrl);
                httprequest.AddRange(ran[0], ran[1]);
                HttpWebResponse httpresponse = (HttpWebResponse)httprequest.GetResponse();
                httpFileStream = httpresponse.GetResponseStream();
                localFileStram = new FileStream(tmpFileBlock, FileMode.Create);
                byte[] by = new byte[5000];
                int getByteSize = httpFileStream.Read(by, 0, (int)by.Length); //get the count about the filestream
                while (getByteSize > 0)
                {
                    Thread.Sleep(20);
                    lock (locker) _downloadSize += getByteSize;
                    localFileStram.Write(by, 0, getByteSize);
                    getByteSize = httpFileStream.Read(by, 0, (int)by.Length);
                }
                lock (locker) _threadCompleteNum++;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message.ToString());
            }
            finally
            {
                if (httpFileStream != null) httpFileStream.Dispose();
                if (localFileStram != null) localFileStram.Dispose();
            }
            if (_threadCompleteNum == _threadNum)
            {
                Complete();
                _isComplete = true;
            }
        }
    
        /// <summary>
        /// merge file
        /// </summary>
        private void Complete()
        {
            Stream mergeFile = new FileStream(@_savePath, FileMode.Create);
            BinaryWriter AddWriter = new BinaryWriter(mergeFile);
            foreach (string file in _tempFiles)
            {
                using (FileStream fs = new FileStream(file, FileMode.Open))
                {
                    BinaryReader TempReader = new BinaryReader(fs);
                    AddWriter.Write(TempReader.ReadBytes((int)fs.Length));
                    TempReader.Close();
                }
                File.Delete(file);
            }
            AddWriter.Close();
        }
    }
    //use
    string httpUrl = @"http://www.abc.com/download/2.rar";
    string saveUrl = System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "//" + System.IO.Path.GetFileName(httpUrl);
    int threadNumber = 5;
    MultiDownload md = new MultiDownload(threadNumber, httpUrl, saveUrl);
    md.Start();

    In addition, you could refer to the following link(other sample):
    http://www.codeproject.com/Articles/21053/MyDownloader-A-Multi-thread-C-Segmented-Download-M

    Regards,
    Moonlight

    Note: This response contains a reference to a third party World Wide Web site. Microsoft is providing this information as a convenience to you. Microsoft does not control these sites and has not tested any software or information found on these sites; therefore, Microsoft cannot make any representations regarding the quality, safety, or suitability of any software or information found there. There are inherent dangers in the use of any software found on the Internet, and Microsoft cautions you to make sure that you completely understand the risk before retrieving any software from the Internet.


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.



    Tuesday, May 31, 2016 1:53 AM
    Moderator
  • Hi Moonlight Sheng, Thank you for your answer, but like I wrote I have studied some download manager applications from CodeProject and it includes the one you give me. I have understood how to do multithread download but what I don't understand yet is how to display the result of each download process on a ListView? I don't understand how to display different download processes on the same ListView just like IDM does? Hope you can help me, no need to give me the code but maybe just help me by giving me the logic flow of how it works. Thanks...
    • Edited by The_C Tuesday, May 31, 2016 3:34 AM
    Tuesday, May 31, 2016 3:33 AM
  • Hi,

    >>Hope you can help me, no need to give me the code but maybe just help me by giving me the logic flow of how it works

    You could add a new row to the listview and use a timer or sub thread to update the listview records' data.

    Sample:

                //set listview 
                listView.View = View.Details;//set mode detail
                listView.FullRowSelect = true;//set full row select
                //add column  
                listView.Columns.Add("File Name", 100, HorizontalAlignment.Right);
                listView.Columns.Add("Local Path", 80, HorizontalAlignment.Right);
                listView.Columns.Add("Curre Size", 70, HorizontalAlignment.Right);
                listView.Columns.Add("File Size", 70, HorizontalAlignment.Right);
                listView.Columns.Add("Percent", 50, HorizontalAlignment.Right);
                listView.Columns.Add("Time", 50, HorizontalAlignment.Right);
                listView.Columns.Add("State", 50, HorizontalAlignment.Right);
                //add new download task
                var item = new ListViewItem();
                item.Name = "Test";
                item.Text = "Test";
                item.SubItems.Add(@"C:\Desktop\Test.rar");
                item.SubItems.Add("0b");
                item.SubItems.Add("10Mb");//get from downloader and trans to bit.
                item.SubItems.Add("0%");
                item.SubItems.Add("0s");
                item.SubItems.Add("Start");
                listView.Items.Add(item);

    //update the download task's state //put these to a timer or sub thread, use a loop to update all records' states. listView.Items[0].SubItems[2].Text = "1Mb"; listView.Items[0].SubItems[4].Text = "10%"; listView.Items[0].SubItems[5].Text = "10s";

    Regards,

    Moonlight


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.




    Tuesday, May 31, 2016 5:28 AM
    Moderator
  • Thank you so much Moonlight Sheng... I'll try it first... 
    Tuesday, May 31, 2016 7:55 AM
  • Hi,

    if you have multiple threads then you must be careful: You cannot change Windows Forms controls from outside the UI thread! So if you want to update any controls during runtime then you have to call invoke.

    One easy way to do this could be the use of events. So inside your class that is responsible for a download you define a simply event e.g. UpdateState. You can define your own StateEventArgs class which contains all the data required to update the state. And then you fire the event inside your thread whenever you want to update the state.

    When creating the new instance of your download, you register to the event with a anonymous method that simply calls invoke. So image your event handler would be
    OnStateUpdateEvent(object sender, StateEventArgs e) { .... } and your instanze with event would be downloader.StateUpdate then the call to add the listener would be

    downloader.StateUpdate += ((sender, args) => Invoke(new EventHandler<StateEventArgs>(OnStateUpdateEvent), sender, args));

    That as a hint so that you can update your gui without writing a lot of code that checks if an invoke is required and then calls an invoke. 

    With kind regards,

    Konrad

    Tuesday, May 31, 2016 8:15 AM
  • Hi Konrad, thanks for your answer. I'll learn more about 'invoke'....

    Tuesday, May 31, 2016 1:28 PM