• 0

C# Progress Bar Help


Question

I'm pretty sure i can figure out how to make use of the progress bar itself but its getting to the point of using it that becomes a problem. I am a nOOb programmer so please bear with me. I've created a simple program to copy a file from one location to another. I am trying to impliment a progress bar to show how far along the task is. I'm pretty familiar with the basic functions of the progress bar itself as I have been able to get it to auto increment itself. I'm just not sure how to get information about the copy progress. Heres a snippet of my code if someone can please give me a pointer.

private void backupButton_Click(object sender, System.EventArgs e)

{

string sourceFile = System.Configuration.ConfigurationSettings.AppSettings

["sourceFileKey"];

string destinationFile = System.Configuration.ConfigurationSettings.AppSettings

["destinationFileKey"];

if (File.Exists( destinationFile ))

{

File.Delete( destinationFile );

}

File.Copy( sourceFile, destinationFile);

}

Thank you all in advance

Link to comment
Share on other sites

18 answers to this question

Recommended Posts

  • 0

I think instead of using File.Copy, you'll have to read one line of the file at a time and copy it into a different file, then increment the progress bar (Y)

Link to comment
Share on other sites

  • 0

If you're doing chunky copy as gameguy suggests, you better move the filecopy code into a seperate thread, or else your UI will lock up / be sluggish like **** until it's done. Have fun with the cross thread invokations required, they're fun if you never did that before.

Link to comment
Share on other sites

  • 0

Well how else would you do it? I'm still new relatively new to programming too :p I just can't see how he would update the progress bar without copying the file himself.

Link to comment
Share on other sites

  • 0

Well, any time consuming code in the same thread that the UI runs on, will slow it down, like this the UI can't process messages while timeconsuming code is running, especially when it takes long time, you get a lock up. If you want to avoid slowdowns or lockups, you need to run the time consuming code in a seperate thread, but you can't call UI functions from a different thread they run on, you get to invoke stuff to bypass that (Avalon wont have these issues). Assuming a progress bar called pgCopy, it'd look somewhat like this:

delegate void SetProgressEvent(int value);

private void SetProgress(int value)
{
    pgCopy.Value = value;
}

private void CopyThread()
{
    // Copy code (while, Read, etc)

        // Get percentage and invoke UI thread
        int percentage = nCopied * 100 / nTotal;
        // this = the form, change if implemented outside of the form
        this.Invoke(new SetProgressEvent(SetProgress), new object[] { percentage } );

    // Copy code
}

private void DoBackup()
{
    new Thread(new ThreadStart(CopyThread)).Start();
}

Add state control for the CopyThread and UI control en-/disabling if you want.

Link to comment
Share on other sites

  • 0

Yeah, I understand the concept of running the slow code in a different thread and all, but how else would one update the progress bar without "chunky" copying? :huh:

Link to comment
Share on other sites

  • 0

Well, he said he's a newbie. Either he tries to get the idea of threading and UI invokations, which I doubt a newbie would want to screw around with, or he drops the progress bar and uses the easier File.Copy function.

Link to comment
Share on other sites

  • 0

I've been looking around on several forums and have came across a C++ function CopyFileEx which apparently can be used to report the progress of a copy job. Is there an alternative in C#? I haven't ran across one yet.

P.S.: Servo, I'm having a little trouble interpreting your code. An explination would be greatly appreciated.

Link to comment
Share on other sites

  • 0

thanks for the info amr but like i said earlier, I'm not having problems implimenting a progress bar, only catching the progress of a File.Copy or equivalent operation.

Link to comment
Share on other sites

  • 0

OK. I did the work for you... I want a cookie! :)

This involves threading, so pay attention. I didn't add a lot, so this is pretty basic functionality. Obviously, you'll want to modify the copy class to take a soure and destination parameter in its constructor. I just hardcoded it for simplicity.

This bit is your declarations for PInvoke interop.

using System;
using System.Runtime.InteropServices;

namespace Win32
{
	class Win32Imports
	{
  [DllImport("kernel32.dll")]
  public static extern bool CopyFileEx(string lpExistingFileName, string lpNewFileName,
 	 CopyProgressDelegate lpProgressRoutine, IntPtr lpData,
 	 [In] ref bool pbCancel, uint dwCopyFlags);

  public delegate uint CopyProgressDelegate( long TotalFileSize,
 	 long TotalBytesTransferred,
 	 long StreamSize,
 	 long StreamBytesTransferred,
 	 uint dwStreamNumber,
 	 uint dwCallbackReason,
 	 IntPtr hSourceFile,
 	 IntPtr hDestinationFile,
 	 IntPtr lpVoid );

  
  public const uint PROGRESS_CONTINUE = 0;
  public const uint PROGRESS_CANCEL = 1;
  public const uint PROGRESS_STOP = 2;
  public const uint PROGRESS_QUIET = 3;

  public const uint CALLBACK_CHUNK_FINISHED = 0x00000000;
  public const uint CALLBACK_STREAM_SWITCH = 0x00000001;

  public const uint COPY_FILE_FAIL_IF_EXISTS = 0x00000001;
  public const uint COPY_FILE_RESTARTABLE = 0x00000002;
  public const uint COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x00000004;
  public const uint COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x00000008;

	}
}

This is what you would put in your form aside from your normal designer generated stuff.

        // This delegate will update your progress bar
  public delegate void UpdateProgressBar( double val );

// this is the callback for the CopyEx function declared in Win32.Win32Imports
  public uint CopyProgressCallback( long totalFileSize,
 	 long totalBytesTransferred, long streamSize, long streamBytesTransferred,
 	 uint dwStreamNumber, uint dwCallbackReason, IntPtr hSourceFile,
 	 IntPtr hDestFile, IntPtr lpVoid )
  {

 	 System.Console.WriteLine( "TotalFileSize: {0}\nTotalBytesTransferred: {1}",
    totalFileSize, totalBytesTransferred );

 	 double pct = ((double)totalBytesTransferred / (double)totalFileSize);
 	 System.Console.WriteLine( "Percent complete: {0:p}", pct );

//**** this invokes the progress bar updater delegate ****//
 	 progressBar1.Invoke( new UpdateProgressBar( this.UpdateProgress ), 
    new object[] { pct } );

// not really sure what to put here. you may want to modify the delegate to return void.
 	 return Win32Imports.PROGRESS_CONTINUE;
  }

// this is the delegate that updates the progress bar.
  private void UpdateProgress(double val )
  {
 	 progressBar1.Value = (int)(val * 100);
  }

// this creates the class that does the file copy in a threaded manner.
  private void button1_Click(object sender, System.EventArgs e)
  {
 	 ThreadedCopyWithCallback tcpy = new ThreadedCopyWithCallback( new
    Win32Imports.CopyProgressDelegate( this.CopyProgressCallback ) );
  }

// the threaded file copy class
	class ThreadedCopyWithCallback
	{
  private Win32Imports.CopyProgressDelegate mDelegate;
  private const string src = @"c:\xpsp2_2149_usa_x86fre_procd1.iso";
  private const string dest = @"w:\";

  public ThreadedCopyWithCallback( Win32Imports.CopyProgressDelegate cpDelegate )
  {
 	 mDelegate = cpDelegate;

 	 Thread t = new Thread( new ThreadStart( CopyFile ) );
 	 t.Start();
  }

  private void CopyFile()
  {
 	 bool val = false;

 	 Win32Imports.CopyFileEx( src, dest + "test.iso", mDelegate, IntPtr.Zero, ref val, 
    Win32Imports.COPY_FILE_RESTARTABLE |
    Win32Imports.COPY_FILE_ALLOW_DECRYPTED_DESTINATION );
  }


	}

Link to comment
Share on other sites

  • 0

I'm done in a few, just quick checking. If Alias starts before I'm done, it'll take another hour!

--edit

Here, a quick hack job for the thread starter:

http://www.tomservo.cc/filecopier.txt

Use the ProgressingFileCopier class for your progress bar, should be selfexplanatory, if not, I'll be back after Alias.

Edited by Tom Servo
Link to comment
Share on other sites

  • 0
Well, the OS hates progress bars :/

I don't blame you. I hate the stupid default segmented progress bar. I was only kidding anyhow.

Speaking of OS stuff. Here's a class that uses the SHFileOperation to do copying, moving, deleting, and renaming of files. Even comes with the OS dialog. Yes, it uses interop, but removes the burden of threading. This could probably be expanded upon to test of OS and IE versions since shell ops rely on them, too. See MSDN SHFileOperation for caveats lector. Can you tell I don't want to work today... hehehe

namespace Win32
{
	public enum FileOp
	{
  Move = 0x0001,
  Copy = 0x0002,
  Delete = 0x0003,
  Rename = 0x0004
	}

	[Flags]
	public enum FileOpFlags
	{
  MultiDestFiles = 0x0001,
  ConfirmMouse = 0x0002,
  Silent = 0x0004,
  RenameCollision = 0x0008,
  NoConfirmation	= 0x0010,
  WantMappingHandle = 0x0020,
  AllowUndo = 0x0040,
  FilesOnly = 0x0080,
  SimpleProgress = 0x0100,
  NoConfirmMkDir = 0x0200,
  NoErrorUI = 0x0400,
  NoCopySecurityAttributes = 0x0800,
  NoRecursion = 0x1000,
  NoConnectedElements = 0x2000,
  WantNukeWarning = 0x4000,
  NoRecursiveReparse = 0x8000
	}

	class ShellFileOperation
	{
  [DllImport("shell32.dll", SetLastError=true)]
  protected static extern int SHFileOperation( ref SHFileOpStruct lpFileOp );

  [StructLayout(LayoutKind.Sequential)]
  protected struct SHFileOpStruct
  {
 	 public IntPtr hwnd;
 	 public uint wFunc;
 	 public string pFrom;
 	 public string pTo;
 	 public FileOpFlags fFlags;
 	 public bool anyOperationsAborted;
 	 public IntPtr nameMapping;
 	 public string progressTitle;
  }

  protected static SHFileOpStruct fos;

  public static void DoFileOperation(string src, string dest, string dialogTitle,
 	 FileOp op, FileOpFlags flags, Form parent )
  {
 	 string fs = flags.ToString( "G" );
 	 if( fs.IndexOf( "WantMappingHandle" ) > -1 )
    throw new ArgumentException( "Mapping handle argument is not specified in this method" );

 	 DoFileOperation( src, dest, dialogTitle, op, flags, parent, IntPtr.Zero );
  }

  public static void DoFileOperation(string src, string dest, string dialogTitle,
 	 FileOp op, FileOpFlags flags, Form parent, IntPtr mappingHandle )
  {
 	 fos = new SHFileOpStruct();
 	 fos.hwnd = parent.Handle;
 	 fos.pFrom = src;
 	 fos.pTo = dest;
 	 fos.progressTitle = dialogTitle;
 	 fos.wFunc = (uint)op;
 	 fos.fFlags = flags;
 	 fos.anyOperationsAborted = false;
 	 fos.nameMapping = mappingHandle;

 	 SHFileOperation( ref fos );
  }

	}
}

// Usage
...
 	 ShellFileOperation.DoFileOperation(
    src, dest + "test.iso", "Testing Shell Copy...",
    FileOp.Copy, FileOpFlags.NoCopySecurityAttributes, this );

Link to comment
Share on other sites

This topic is now closed to further replies.