Using ProgressBar while populating a huge TreeView - c#

I am populating a treeview with a lot of data, and usually it takes a while for program to give back the control to user (I guess it is just too much process on the treeview side). So I decided to give a progress bar to show things are going fine to user.
In this treeview I select a folder and all xml files in that folder will be added to this folder and the treeview looks like this:
+ FOLDER NAME 1
|__xml file name 1
|__xml file name 2
+FOLDER NAME 2
|__xml file name 1
|__xml file name 2
So problem is, my progressbar will be completed actually much faster than the treeview itself, I think it is the time for progam that is making the treeview visible that takes the must time. How can I do this?
This is my code for browing a folder and passing it to a function to populate treeview (Sorry taht is too long but I though it gonna make it more clear) As you see I want that 50% of progress bar to be filled in this part and other 50 while adding nodes to treeview:
private void toolStripBrowseDirectory_Click(object sender, EventArgs e)
{
//Reset progressbar
toolStripProgressBar.Value = 0;
folderBrowser.ShowNewFolderButton = false;
DialogResult result = folderBrowser.ShowDialog();
int countAllfiles = 0;
int countGoodFiles = 0;
if (result == DialogResult.OK)
{
toolStripProgressBar.Value = 0;
string selectedFolder = folderBrowser.SelectedPath;
string[] files = Directory.GetFiles(selectedFolder, "*.xml", SearchOption.AllDirectories);
List<string> goodFiles = new List<string>();
toolStripProgressBar.Maximum = files.Length;
countAllfiles = files.GetLength(0);
//Folder name
FileInfo folderInfo = new FileInfo(selectedFolder);
string folderName = folderInfo.Name;
if (countAllfiles != 0)
{
foreach (string file in files)
{
//file name
FileInfo fileInfo = new FileInfo(file);
string fileName = fileInfo.Name;
try
{
XmlDocument checkFileType = new XmlDocument();
checkFileType.Load(file);
if (checkFileType.SelectSingleNode("//Document/#Type").Value == "Test Results")
{
countGoodFiles++;
goodFiles.Add(file);
}
}
catch (Exception a)
{
StartForm.setDebug("Comparison Mode", a.Message, 21, "Can not read Selected XML file '" + fileName + "'", "Check file for compatibilyty", "File was excluded from list");
}
this.toolStripProgressBar.PerformStep();
}
if (countGoodFiles == 0)
{
MessageBox.Show("There are no compatible XML files (exported from ZTR files) in '" + folderName + "'", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
StartForm.setDebug("Comparison Mode", "N/A", 20, "There are no compatible XML files (exported from ZTR files) in '" + folderName + "'", "Select a folder that contains compatible files!", "No file has been added to Select File Tree");
toolStripProgressBar.Value = 100;
}
else if (countGoodFiles > 0)
{
this.Cursor = Cursors.WaitCursor;
AddFilesToTree(folderName, goodFiles.ToArray());
this.Cursor = Cursors.Default;
}
}
else
{
MessageBox.Show("There are no XML files in '" + folderName + "'", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
And this is for poulating the treeview
private void AddFilesToTree(string folder, string[] files)
{
//Define folder node
TreeNode folderNode = new TreeNode();
folderNode.Text = folder;
folderNode.Name = "folder";
folderNode.Tag = folder;
treeViewBrowsedFiles.Nodes.Add(folderNode);
//Define file node
foreach (string file in files)
{
try
{
//To check if the file is a valid test result (converted to XML from ZTR files)
XmlDocument checkFileType = new XmlDocument();
checkFileType.Load(file);
LoadZTRCmp xml = new LoadZTRCmp(file);
TreeNode fileNode = new TreeNode();
fileNode.Text = xml.FileInfo("BatchNumber");
fileNode.Name = "file";
fileNode.Tag = file;
folderNode.Nodes.Add(fileNode);
this.toolStripProgressBar.PerformStep();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
folderNode.Toggle();
}
So how to do this right way?
Update:
** SOLVED **
I found the problem, I had to do this line before using function to add nodes to treeview!!!
toolStripProgressBar.Maximum += countGoodFiles;
AddFilesToTree(folderName, goodFiles.ToArray());
fixed MAX value for progress bar
Still progressbar finishes (maxed out) before TreeView starts to load...

int progressSteps = (files.Length / 100)/2;
That's quite mysterious but I certainly don't understand the logic. The code is hard to read, you should break it up and use more helper methods. Try this instead:
toolStripProgressBar.Maximum = files.Length;

You can skip the progress bar if you just call Application.DoEvents() every so often, it'll show the TreeView expanding with the nodes and should be relevant enough to the user to know how much longer they have to wait.

Maybe use the progress bar mode of marquee, which just moves back and forth across the progress bar, and then hide the progress bar when the tree is done loading? That way you'd still get the visibility of the application is doing some work, and the user could know to wait?
progressBar1.Style = ProgressBarStyle.Marquee;

Check the value toolStripProgressBar.Maximum . Is it equal to the ProgressBar's Value when your process completes?
If your ProgressBar's Step property is 1 it should be equal to the number of steps you want to display on your ProgressBar. It's possible toolStripProgressBar.Maximum is set too high and so it never reaches the end before your process completes.

A better approach is probably to populate the branches of the tree on a need basis; so when your user opens a new branch, just get the sub-branches. Populating an entire tree in one go on start up is wasteful because the chances are that your user is only interested in a small part of the tree and does not want to wait for the entire thing to load just so that they can view the small part of the tree in which they are interested.

Take a look at the BackgroundWorker. It allows you to populate your treeview from a separate tread, making your user interface a lot more responsive. It also supports reporting progress, so if you still want to display a progress bar it is perfectly possible.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
http://www.codeproject.com/KB/cpp/BackgroundWorker_Threads.aspx

Related

Convert files following the order of a list C#

I need to convert images(like .jpg) to PDF files for an assignment for school. I have a ListBox where I put the pages of the PDF file, so the user can reorder the list and convert the files in that order.
I have the files in a temporary folder in order to get the files there to convert them to PDF.
My problem here is : how do I convert the files with the order that the user had chosen?
I already searched and I tried to do a Class with the strings ID and Name so i get the ID from the item in the ListBox and change it on a new list. And i think after, I do a foreach() loop where I get the files from the temporary folder and merge them in a new PDF file, but to do in the order I want, I think I have to compare the name of the file with the name in the list and, if it matches, convert and add it, if not, pass to the next file.
But I don't know how to do it.
Can please someone help me getting this right?
Thanks in advance!
I'm sending my code to:
//the open files button
private void proc2_Click(object sender, EventArgs e)
{
OpenFileDialog dialogo = new OpenFileDialog();
dialogo.Title = "Search files";
dialogo.InitialDirectory = #"E:\";
dialogo.Filter = "Images (.bmp,.jpg,.png,.tiff,.tif) |*.bmp;*.jpg;*.png;*tiff;*tif|All of the files (*.*)|*.*";
DialogResult resposta = dialogo.ShowDialog();
if (resposta == DialogResult.OK)
{
string caminhoCompleto = dialogo.FileName;
caminho2 = dialogo.SafeFileName;
caminhotb2.Text = caminhoCompleto;
string fish = "";
string path = #"C:\temporario";
if(Directory.Exists(path))
{
fish=Path.Combine(path, caminho2);
}
else
{
Directory.CreateDirectory(path);
fish = Path.Combine(path, caminho2);
}
File.Create(fish);
listaimg.Items.Add(caminho2);
}
}
public string[] GetFilesImg4() //jpg files
{
if (!Directory.Exists(#"C:\temporario"))
{
Directory.CreateDirectory(#"C:\temporario");
}
DirectoryInfo dirInfo = new DirectoryInfo(#"C:\temporario");
FileInfo[] fileInfos4 = dirInfo.GetFiles("*.jpg");
foreach (FileInfo info in fileInfos4)
{
if (info.Name.IndexOf("protected") == -1)
list4.Add(info.FullName);
}
return (string[])list4.ToArray(typeof(string));
}
If both actions happen in the same process, you can just store the list of file names in memory (and you already do add them to listaimg):
public string[] GetFilesImg4() //jpg files
{
string tempPath = #"C:\temporario";
if (!Directory.Exists(tempPath))
{
foreach (string filename in listimga.Items)
{
if (!filename.Contains("protected"))
list4.Add(Path.Combine(tempPath, filename);
}
}
return (string[])list4.ToArray(typeof(string));
}
if these are different processes then you can just dump content of your listimga at some point and then read it from the same file. In the example below I store it to file named "order.txt" in the same directory, but logic may be more complicated, such as merging several files with a timestamp and such.
// somewhere in after selecting all files
File.WriteAllLines(#"c:\temporario\order.txt", listimga.Items.Select(t=>t.ToString()));
public string[] GetFilesImg4() //jpg files
{
string tempPath = #"C:\temporario";
if (!Directory.Exists(tempPath))
{
var orderedFilenames = File.ReadAllLines(Path.Combine(tempPath, "order.txt")); // list of files loaded in order
foreach (string filename in orderedFilenames)
{
if (!filename.Contains("protected"))
list4.Add(Path.Combine(tempPath, filename);
}
}
return (string[])list4.ToArray(typeof(string));
}
it's also a good idea to examine available method on a class, such as in this case string.IndexOf(s) == -1 is equivalent to !string.Contains(s) and the latter is much more readable at least for an English speaking person.
I also noticed that your users have to select documents one by one, but FileOpen dialogs allow to select multiple files at a time, and I believe it preserves the order of selection as well.
If order of selection is important and file open dialogs don't preserve order or users find it hard to follow you can still use multiple file selection open dialog and then allow to reorder your listimga list box to get the order right.

Iterate Over Media Files Properties And Display Filenames into Listbox

I'm writing a simple WinForms application which I will use prior to when I edit my video footage in order to know the total length of all the video files when combined and "edited".
So ideally, I load them up then get the duration of each video and tally them up.
However I get stuck in terms of which collection class to use since I am using an external library IWMPMedia in addition to WindowsMediaPlayer to get the filenames and duration.
When I use a string array to get the filename, I get the whole path, which is something I don't want. Please see the following code snippet:
using WMPLib;
public partial class frmBacklinkMediaSerializer : Form
{
WindowsMediaPlayer wmp;
IWMPMedia mediainfo;
Double Duration(String file)
{
wmp = new WindowsMediaPlayer();
mediainfo = wmp.newMedia(file);
return mediainfo.duration;
}
void callDialogBox()
{
OpenFileDialog theDialog = new OpenFileDialog();
theDialog.Filter = "MP4 Videos (*.mp4)|*.mp4";
theDialog.FilterIndex = 0;
theDialog.Multiselect = true;
DialogResult result = theDialog.ShowDialog();
string[] selected = theDialog.FileNames;
string strFilename = theDialog.FileName;
if (result == DialogResult.OK)
{
FileInfo oFileInfo = new FileInfo(strFilename);
string temp = Duration(strFilename).ToString();
TimeSpan conversion =
TimeSpan.FromSeconds(Duration(strFilename));
if (strFilename != null || strFilename.Length != 0)
{
MessageBox.Show("My file names are below: " + "\n\n" + mediainfo.name + "\n\n" + "My file duration is: " + conversion.ToString(), "Video Properties", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
}
}
}
//I then call callDialogBox() on a button click event
The output shows as per the below screenshot:
I want to be able to select multiple files and show the in the listbox but I get a:
Additional information: COM object that has been separated from its underlying RCW cannot be used.
When adding items to the listbox.
So two problems:
1. Being able to select and list filenames below one another
2. List the filenames in the listbox
With regards to point 1. , the below code snippet does somewhat achieve it but gives me the entire filename..
string[] selected = theDialog.FileNames;
foreach(string items in selected)
{
MessageBox.Show("Here are your filenames: " + items);
}
Gives me the entire path. Read up on some resources online but I can't seem to get what I want.
I had a look at: How do I drag n drop files into a ListBox with it showing the file path? and it didn't do the trick.
Try the following to add the filenames to the ListBox:
listBox1.Items.AddRange(selected);
Once this is done, you can then display your total duration.
Edit:
To list only file names, use following line.
listBox1.Items.AddRange(selected.Select(o=>System.IO.Path.GetFileName(o)).ToArray());

columns combining when c# appends to csv

We've created a desktop timer that our users are using to track their daily tasks and projects. It outputs data to a .csv file when they close the application. Occasionally they are going to need to manually update the csv file to either take time off or add time. When they have been doing this in the current state after they save all the columns are combined into column A. I am unclear on what is causing this and tried to research encoding but couldn't find anything I could relate to this scenario.
Full form1.cs: full form1.cs code
Code related to csv:
//event handler for the closing event -- the output dump is here so the timer can be used all day and captures even on an accidetnal close
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//if the file exists we don't need to write the headers so we can skip the additional line write and just append the data created
if (File.Exists(dskPath + "\\" + agentName + ".csv"))
{
using (var file = new StreamWriter(dskPath+"\\" + agentName + ".csv", true, Encoding.UTF8))
{
foreach (var data in TimerData)
{
file.WriteLine(string.Join(",", data.agentID, data.agentStatus, data.statusTime, data.startTime, data.currentDate, data.hydraTask, data.clientname, data.publishdate, data.notes));
}
file.Close();
notifyIcon1.Icon = null;
MessageBox.Show("The file for " + agentName + " has been written to G:\\Communicator Ops\\Population Health\\Pop Process\\Time Tracking\\User Data.", "Application Closed");
}
}
//if the file hasn't been created before we can drop the headers in for the columns
else
{
using (var file = new StreamWriter(dskPath +"\\"+ agentName + ".csv", true, Encoding.UTF8))
{
file.WriteLine(string.Join(",", "Agent ID", "Agent Status", "Status Time", "Start Time", "Current Date", "hydra task number", "Client Name", "Publish Date", "Notes"));
foreach (var data in TimerData)
{
file.WriteLine(string.Join(",", data.agentID, data.agentStatus, data.statusTime, data.startTime, data.currentDate, data.hydraTask, data.clientname, data.publishdate, data.notes));
}
file.Close();
notifyIcon1.Icon = null;
MessageBox.Show("The file for " + agentName + " has been written to your desktop.","Application Closed");
}
}
}
I have a theory about what might be occurring. The C# code looks OK. I think the users are opening the file in a way you were not expecting. I think they are doing the following:
1) Opening Excel and then navigating to the file location. This will cause this dialog to open:
2) They hit Next and get to step 2.
At this step they may be leaving Tab selected as the delimiter which is NOT correct. This is where they may be causing your problem.
They hit Next again
3)
Finally they hit Finish and everything ends up in column A.
If I double click on the desktop icon it seems to work OK. If I select comma as the delimiter, and uncheck Tab, that worked too.
That is my guess. Hopefully this will help you out.

Putting each line from a text file into an array C#

I am working on selecting a text file with a folder pathway via a Windows form in C# and gathering information on each pathway. At the minute, I can import the file and display only the second pathway in the text file, but no information on the folder. Here is the code I have:
private void btnFilePath_Click(object sender, EventArgs e)
{
//creating a stream and setting its value to null
Stream myStream = null;
//allowing the user select the file by searching for it
OpenFileDialog open = new OpenFileDialog();
open.InitialDirectory = "c:\\";
open.Filter = "txt files (*.txt)|*.txt";
open.FilterIndex = 2;
open.RestoreDirectory = true;
//if statement to print the contents of the file to the text box
if (open.ShowDialog() == DialogResult.OK)
{
try
{
if ((myStream = open.OpenFile()) != null)
{
using (myStream)
{
txtFilePath.Text = string.Format("{0}", open.FileName);
if (txtFilePath.Text != "")
{
lstFileContents.Text = System.IO.File.ReadAllText(txtFilePath.Text);
//counting the lines in the text file
using (var input = File.OpenText(txtFilePath.Text))
{
while (input.ReadLine() != null)
{
//getting the info
lstFileContents.Items.Add("" + pathway);
pathway = input.ReadLine();
getSize();
getFiles();
getFolders();
getInfo();
result++;
}
MessageBox.Show("The number of lines is: " + result, "");
lstFileContents.Items.Add(result);
}
}
else
{
//display a message box if there is no address
MessageBox.Show("Enter a valid address.", "Not a valid address.");
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read the file from disk. Original error: " + ex.Message);
}
}
}
I was thinking that copying each line to a variable using a foreach or putting each line into an array and looping through it to gather the information.
Can anyone advise me which would be most suitable so I can go to MSDN and learn for myself, because, I'd prefer to learn it instead of being given the code.
Thanks!
I am not sure what your question is since you seemed to have answered it. If you want us to review it you question would be better suited to Code Review: https://codereview.stackexchange.com/
If you want to use MSDN look here: http://msdn.microsoft.com/en-us/library/System.IO.File_methods(v=vs.110).aspx
Spoiler alert, here is how I would do it:
string[] lines = null;
try
{
lines = File.ReadAllLines(path);
}
catch(Exception ex)
{
// inform user or log depending on your usage scenario
}
if(lines != null)
{
// do something with lines
}
to just gather all lines into array i would use
var lines = File.ReadAllLines(path);
If you want to have more reference rather than the answer itself, take these links one by one all of them explaining things in different manner.
C# File.ReadLines
How to: Read From a Text File (C# Programming Guide)
How to: Read a Text File One Line at a Time (Visual C#)
Hope it will help you to learn more about File IO operations in C#.

to create a gallery in asp.net by iterating through the folders

I want to create a gallery in asp.net the way it should work is as follows:
iterate through gallery folder
select all the folder and show them as albums with cover pic as thumbnail.jpg in that folder
when clicked on the album it should display the content of the folder (images).
My approach for creating this was to iterate through the folders and make views and link button based on that for albums and to display the content of album in that view as images using repeater control, but that didn't work out as it had many errors while implementing it. and I had to write the whole thing in on_init() function because of dynamic views.I can implement the html and js part (like for light box and other visual stuffs).
Please suggest some better approach maybe with some code example. Please use c# . Thanks
I can not just write you all the code... but ill give you a startup code:
int scanLVL = 4;//or however deep you need to go...
public void GetImageFromDir(string sourceDir, int startLVL)
{
if (startLVL <= scanLVL)
{
// Here you can process files found in the directory.
string[] fileEntries = Directory.GetFiles(sourceDir);
Label_showdata.Text +="<br />Dir:" + Path.GetFileName(sourceDir) ;
foreach (string fileName in fileEntries)
{
// do something with fileName
String tree = "";
for (int i = 0; i < startLVL; i++)
tree += " ";
Label_showdata.Text += "<br />" + tree + Path.GetFileName(fileName);
}
// Going in subdirectories of this directory.
string[] subdirEntries = Directory.GetDirectories(sourceDir);
foreach (string subdir in subdirEntries)
if ((File.GetAttributes(subdir) & FileAttributes.ReparsePoint) != FileAttributes.ReparsePoint)
GetImageFromDir(subdir, startLVL + 1);
}
}
This will print you on your web page(actualy in that Label_showdata) all the files and the directory where they are.
Basically from here you will need to wrap that data into a table and bind it in whatever control you feel comfortable with... Is not that hard actually... but it takes a bit more time to write exactly all the code (time that unfortunatlly i do not have at the moment...)

Categories