c# BackgroundWorker and Treeview in winform - c#

Env: C#, VStudio 2013, 4.5 Framework, Winforms
Goal : Insert, inside a Treeview, the content of a folder (Sub folder + files) so that user can select those needed. Display a progressbar that show the progress of loading the files and folders in the TreeView.
What i've done so far : Everything in my goal but ...
Error: "This BackgroundWorker is currently busy and cannot run multiple tasks concurrently". I get this error sometimes when I go use other apps while my application is running.
My code :
void backgroundWorkerTreeView_DoWork(object sender, DoWorkEventArgs e)
{
var progress = (((float)(int)e.Argument / (float)totalFilesInTN) * 100);
var value = (int)Math.Round((float)progress);
backgroundWorkerTreeView.ReportProgress(value, e.Argument.ToString());
}
void backgroundWorkerTreeView_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
stStripBarMain.Value = e.ProgressPercentage;
toolStripStatusLabelPrct.Text = " Loading " + e.UserState + " of " + totalFilesInTN;
}
void backgroundWorkerTreeView_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//do the code when bgv completes its work
}
private void ListDirectory(TreeView treeView, string path)
{
try
{
treeView.Nodes.Clear();
if (path != "")
{
var rootDirectoryInfo = new DirectoryInfo(path);
treeView.Nodes.Add(CreateDirectoryNode(rootDirectoryInfo, 0));
}
}
catch (Exception e)
{
txtLog.Text = txtLog.Text + "[" + DateTime.Now + "] " + e.Message + "\r\n";
}
}
private TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo, int indice)
{
var directoryNode = new TreeNode(directoryInfo.Name);
foreach (var directory in directoryInfo.GetDirectories())
{
if ((directory.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden)
{
directoryNode.Nodes.Add(CreateDirectoryNode(directory, indice));
}
}
foreach (var file in directoryInfo.GetFiles())
{
if ((IsNullOrEmpty(ext)) ||
(Array.IndexOf(ext, Path.GetExtension(file.Name.ToLowerInvariant())) >= 0))
{
if ((GetTriggerEvent(file.FullName).Contains(txtParamEvent.Text)) || (txtParamEvent.Text == ""))
{
indice++;
backgroundWorkerTreeView.RunWorkerAsync(indice);
TreeNode newTN = new TreeNode();
newTN.Text = file.Name + #" (" + GetTriggerEvent(file.FullName) + #")";
newTN.Name = file.FullName;
directoryNode.Nodes.Add(newTN);
newTN.Tag = "msg";
}
}
Application.DoEvents();
}
return directoryNode;
}
Thanks for the help
Richard

Related

Thread safety / Invoke method causes StackOverflowException

This is an app that uses WMI to fetch Local Administrator group members on a remote computer. In an attempt to make threadsafe calls to update my main UI from within Background Worker I am getting a StackOverflowException. I have copied the example from another thread on Stack. Could someone help me to identify the cause?
private void getLocalAdministrators(string serverName)
{
try
{
System.Management.ManagementScope scope = new System.Management.ManagementScope("\\\\" + serverName + "\\root\\cimv2");
scope.Connect();
StringBuilder qs = new StringBuilder();
qs.Append("SELECT PartComponent FROM Win32_GroupUser WHERE GroupComponent = \"Win32_Group.Domain='" + serverName + "',Name='" + "Administrators'\"");
System.Management.ObjectQuery query = new System.Management.ObjectQuery(qs.ToString());
System.Management.ManagementObjectSearcher searcher = new System.Management.ManagementObjectSearcher(scope, query);
foreach (System.Management.ManagementObject group in searcher.Get())
{
string groupDetails = serverName + tab + group["PartComponent"].ToString() + tab;
string domainPart = groupDetails.Split('=')[1];
domainPart = domainPart.Replace("\"","").Replace(",Name","");
string accountPart = groupDetails.Split('=')[2];
accountPart = accountPart.Replace("\"", "");
if (query != null)
{
updateUISafely(serverName + tab + domainPart + tab + accountPart);
}
else
{
updateUISafely("Error with: " + serverName);
}
}
}
catch (Exception ex)
{
updateUISafely("Error with: " + serverName + ". " + ex.Message);
}
}
public delegate void ProcessResultDelegate(string result);
void updateUISafely(string result)
{
if (textBox2.InvokeRequired)
{
var d = new ProcessResultDelegate(updateUISafely);
d.Invoke(result);
}
else
{
textBox2.AppendText(result + nl);
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string[] strArray;
strArray = textBox1.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
int objectCount = strArray.Count();
int count = 0;
foreach (string str in strArray)
{
getLocalAdministrators(str);
count++;
backgroundWorker1.ReportProgress((100 * count) / objectCount);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
foreach (string item in resultsList)
{
textBox2.AppendText(item + Environment.NewLine);
}
btnGet.Enabled = true;
}
You are invoking (aka calling) the delegate, causing it to repeatedly call updateUISafely without ever invoking on the UI thread.
You should call Invoke on the form/control instead providing the delegate as parameter.
Use:
this.Invoke(d);

C# access to TreeNode parameter

Here is code:
private static TreeNode CreateDirectoryNode(DirectoryInfo directoryInfo)
{
var directoryNode = new TreeNode(directoryInfo.Name + " (" + DirSize(new DirectoryInfo(directoryInfo.FullName)) + " bytes)" + " (" + directoryInfo.GetFileSystemInfos().Length + " files)"+ directoryInfo.CreationTime);
foreach (var directory in directoryInfo.GetDirectories())
directoryNode.Nodes.Add(CreateDirectoryNode(directory));
foreach (var file in directoryInfo.GetFiles())
directoryNode.Nodes.Add(new TreeNode(file.Name + " (" + file.Length + " bytes)"));
return directoryNode;
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
string selectedNodeText = "a";
textBox1.Text = selectedNodeText;
}
I need to access directoryInfo.CreationTime from TreeNode and display it in treeView1_AfterSelect text.Box1 but I can't find right way.
You can use the Tag property of TreeNode to put the value there and then access it in the event :
directoryNode.Tag = directoryInfo;
and then in event you can access it :
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
var directoryInfo = e.Node.Tag as DirectoryInfo;
var time = directoryinfo.CreationTime;
}
or:
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
var directoryInfo = treeView1.SelectedNode.Tag as DirectoryInfo;
var creationTime = drInfo.CreationTime;
}
Hope it helps!
Tree nodes expose the Tag property that is used to store and retrieve a custom information under nodes. It can even hold a reference to a complex structure.
https://msdn.microsoft.com/en-us/library/system.windows.forms.treenode.tag(v=vs.110).aspx

c# updater help ( restart once update is complete )

Hello all i have managed to make a basic program that will do a update for me but one thing i want to try and do when the update form is open to close the main form then restart program when the update is finished but have no idea how to do this
this is the code i have currently
namespace update_test
{
public partial class Form1 : Form
{
// folder calls needed
string updatepath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "updatetest");
public Form1()
{
InitializeComponent();
}
// copying files
private static void DirectoryCopy(
string sourceDirName, string destDirName, bool copySubDirs)
{
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
DirectoryInfo[] dirs = dir.GetDirectories();
// If the source directory does not exist, throw an exception.
if (!dir.Exists)
{
throw new DirectoryNotFoundException(
"Source directory does not exist or could not be found: "
+ sourceDirName);
}
// If the destination directory does not exist, create it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
// Get the file contents of the directory to copy.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
// Create the path to the new copy of the file.
string temppath = Path.Combine(destDirName, file.Name);
// Copy the file.
file.CopyTo(temppath, true);
}
// If copySubDirs is true, copy the subdirectories.
if (copySubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
// Create the subdirectory.
string temppath = Path.Combine(destDirName, subdir.Name);
// Copy the subdirectories.
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
}
}
}
public void downloadfile()
{
//client.DownloadFile("http://elfenliedtopfan5.co.uk/update/elfenlied_weapons.zip", updatepath);
WebClient client = new WebClient();
string file = Path.Combine(updatepath, "elfenlied_weapons.zip");
client.DownloadFileAsync(new Uri("http://elfenliedtopfan5.co.uk/update/elfenlied_weapons.zip"), file);
client.DownloadProgressChanged += client_DownloadProgressChanged;
MessageBox.Show(file);
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
int bytesin = int.Parse(e.BytesReceived.ToString());
int totalbytes = int.Parse(e.TotalBytesToReceive.ToString());
int kb1 = bytesin / 1024;
int kb2 = totalbytes / 1024;
label1.Text = kb1.ToString() + "KB out of " + kb2.ToString() + "KB (" +e.ProgressPercentage.ToString() + "%)";
progressBar1.Value = e.ProgressPercentage;
if (e.ProgressPercentage == 100)
{
exactzip();
}
}
public void update()
{
string downloadurl = "";
Version newversion = null;
string xmlurl = "http://elfenliedtopfan5.co.uk/xml/update.xml";
XmlTextReader reader = null;
try
{
reader = new XmlTextReader(xmlurl);
reader.MoveToContent();
string elementname = "";
if ((reader.NodeType == XmlNodeType.Element)&& (reader.Name == "update_test"))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
elementname = reader.Name;
}
else
{
if((reader.NodeType == XmlNodeType.Text)&& (reader.HasValue))
{
switch (elementname)
{
case "version":
newversion = new Version(reader.Value);
break;
case "url":
downloadurl = reader.Value;
break;
}
}
}
}
}
}
catch(Exception e)
{
MessageBox.Show(e.Message);
}
finally
{
if (reader != null)
reader.Close();
}
Version applicationvershion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
if (applicationvershion.CompareTo(newversion) < 0)
{
DialogResult dialogResult = MessageBox.Show("Version " + newversion.Major + "." + newversion.Minor + "." + newversion.Build + " of elfenliedprograms do you want to update now ?", "Update Avalible!", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
var myForm = new Form1();
myForm.Show();
}
else if (dialogResult == DialogResult.No)
{
//do something else
}
else
{
MessageBox.Show("program currently upto date :) ");
}
}
}
public void seeiffile()
{
// Set to folder path we must ensure exists.
string updatepathex = updatepath;
try
{
// If the directory doesn't exist, create it.
if (!Directory.Exists(updatepathex))
{
Directory.CreateDirectory(updatepathex);
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
// if all above works then follow this
public void exactzip()
{
using (ZipFile zip = ZipFile.Read (Path.Combine(updatepath, "elfenlied_weapons.zip")))
{
foreach (ZipEntry e in zip)
{
e.ExtractExistingFile = ExtractExistingFileAction.OverwriteSilently;
e.Extract(updatepath);
// e.Extract(updatepath);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
seeiffile();
downloadfile();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
ShowHideForm1(true);
}
private void ShowHideForm1(bool show)
{
Form1 f1 = Application.OpenForms.OfType<Form1>().SingleOrDefault();
if (f1 != null)
{
if (show)
{ f1.Show(); }
else
{ f1.Hide(); }
}
}
private void Form2_Shown(object sender, EventArgs e)
{
ShowHideForm1(false);
}
}
}
then i have another form attached to this application called
elfenliedprograms.cs
witch contains this code
namespace update_test
{
public partial class elfenliedprograms : Form
{
public elfenliedprograms()
{
InitializeComponent();
}
private void elfenliedprograms_Load(object sender, EventArgs e)
{
update();
}
public void update()
{
string downloadurl = "";
Version newversion = null;
string xmlurl = "http://elfenliedtopfan5.co.uk/xml/update.xml";
XmlTextReader reader = null;
try
{
reader = new XmlTextReader(xmlurl);
reader.MoveToContent();
string elementname = "";
if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "update_test"))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
elementname = reader.Name;
}
else
{
if ((reader.NodeType == XmlNodeType.Text) && (reader.HasValue))
{
switch (elementname)
{
case "version":
newversion = new Version(reader.Value);
break;
case "url":
downloadurl = reader.Value;
break;
}
}
}
}
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
finally
{
if (reader != null)
reader.Close();
}
Version applicationvershion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
if (applicationvershion.CompareTo(newversion) < 0)
{
DialogResult dialogResult = MessageBox.Show("Version "+ newversion.Major + "." + newversion.Minor + "." + newversion.Build + " of elfenliedprograms do you want to update now ?", "Update Avalible!", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
var myForm = new Form1();
myForm.Show();
}
else if (dialogResult == DialogResult.No)
{
//do something else
}
else
{
MessageBox.Show("program currently upto date :) ");
}
}
}
so basically once the update triggers there is a update it will call elfenliedprograms.cs but it will not shut down the main form if i do that it will shut the whole process down and im not sure how to go about making it download then restart app so all new stuff is updated it has taken me a good 4 months just to get this working the way i want not very good when it comes to updating a aplication so i would need help on this part sorry to be a pain and ask
thank you for

How to use validation in DataGridView

In my program, when I click a particular row in DataGridView, if that row contains "\" it should pop up an error message that "\ is not allowed in name or in path". I don't know how to do that.
Here is the code:
namespace OVF_ImportExport
{
public partial class Form1 : Form
{
string sName = "";
string sPath = "";
public Form1()
{
InitializeComponent();
}
private void dataGridView1_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
foreach (DataGridViewRow row in dataGridView1.SelectedRows)
{
sName = row.Cells[0].Value.ToString();
sPath = row.Cells[1].Value.ToString();
}
}
private void BtnCreate_Click(object sender, EventArgs e)
{
richTextBox1.Text = "";
StreamWriter file = new StreamWriter("Export.bat");
file.WriteLine("c: ");
file.WriteLine("cd \\");
file.WriteLine("cd Program Files ");
file.WriteLine("cd VMware");
file.WriteLine("cd VMware OVF Tool");
foreach (DataGridViewRow row in dataGridView1.SelectedRows)
{
sName = row.Cells[0].Value.ToString();
sName = sName.Trim();
sPath = row.Cells[1].Value.ToString();
file.WriteLine("start ovftool.exe --powerOffSource vi://" + TxtUsername.Text + ":" + TxtPassword.Text + "#"
+ TxtIP.Text + sPath + " " + "\"" + TxtBrowsepath.Text + "\\" + sName + "\\" + sName + ".ovf" + "\"" + Environment.NewLine);
}
file.WriteLine("pause");
MessageBox.Show("Batch File Created","Batch File");
file.Close();
}
try using this:
// Attach DataGridView events to the corresponding event handlers.
this.dataGridView1.CellValidating += new DataGridViewCellValidatingEventHandler(dataGridView1_CellValidating);
method for above event handler:
private void dataGridView1_CellValidating(object sender,
DataGridViewCellValidatingEventArgs e)
{
// Validate the CompanyName entry by disallowing empty strings.
if (dataGridView1.Columns[e.ColumnIndex].Name == "CompanyName")
{
if (String.IsNullOrEmpty(e.FormattedValue.ToString()))
{
dataGridView1.Rows[e.RowIndex].ErrorText =
"Company Name must not be empty";
e.Cancel = true;
}
}
}

Creating a statusfield for users

I need some guidance here on why this isn't working:
So here's the issue, I want to give my users a little status field so they can check how long it will take and get a coffee or two for them.
My Problem is that the statusfield (2 Labels), are not updated during the process.
This is my current code :
private void Cancel_Click(object sender, EventArgs e)
{
this.Close();
}
private void start_change_Click(object sender, EventArgs e)
{
DialogResult dr = MessageBox.Show("Start process?", "DateChanger", MessageBoxButtons.OKCancel, MessageBoxIcon.Hand);
if (dr == DialogResult.OK)
{
//get files
List<String> d = new List<String>();
label_status_title.Text = "Status: collecting Data, take a coffee while waiting.\nfiles changed: 0 files";
d = getFiles("H:\\");
int i = 0;
double diff = 0.0;
//modify files
label_status_title.Text = "Status: changing files.\nfiles changed: 0/" + d.Count + " files.";
foreach (String s in d)
{
String label = "\nfile: " + s;
//create newDate and modify creation and lastwrite
DateTime actualDate = Directory.GetLastWriteTime(s).Date;
DateTime newDate = new DateTime(2015, 03, 01);
diff = (newDate - actualDate).TotalDays;
label += "\nactual creation date: " + Directory.GetCreationTime(s).Date;
label += "\nnew creation date: " + newDate.Date;
label += "\nactual last write date: " + Directory.GetLastWriteTime(s).Date;
label += "\nnew last write date: " + newDate.Date;
if (diff > 400)
{
try
{
//set new timevalues
Directory.SetCreationTime(s, newDate);
Directory.SetCreationTimeUtc(s, newDate);
Directory.SetLastWriteTime(s, newDate);
Directory.SetLastWriteTimeUtc(s, newDate);
}
catch (UnauthorizedAccessException UAE)
{
}
i++;
label += "\nchange needed.";
}
else
{
label += "\nchange not needed.";
}
label_status.Text = label;
label_status_title.Text = "Status: changing files.\nfiles changed: " + i + "/" + d.Count + " files.";
}
MessageBox.Show("Process finished, changed: " + i + "/" + d.Count + " files.");
}
}
private List<String> getFiles(string sDir)
{
List<String> files = new List<String>();
try
{
foreach (string f in Directory.GetFiles(sDir))
{
files.Add(f);
}
foreach (string d in Directory.GetDirectories(sDir))
{
files.AddRange(getFiles(d));
}
}
catch (System.Exception excpt)
{
MessageBox.Show(excpt.Message);
}
return files;
}
private void DateChanger_Load(object sender, EventArgs e)
{
String label = "";
label_status_title.Text = "Status: \nfiles changed: 0 files";
label += "file: ";
label += "\nactual creation date: ";
label += "\nnew creation date: ";
label += "\nactual last write date: ";
label += "\nnew crealast writetion date: ";
label_status.Text = label;
}
I also tried the suggestion of using MethodInvoker, but that also didn't work either. Any guidance or suggestions here are appreciated.
Thanks.
Mirko
p.s. if there is a better solution than using labels or text boxes for this feel free to tell me. :)
Youre Method start_change_Click(object sender, EventArgs e) is blocking the main thread. To avoid this, use a separate thread to update the labels.
Check out this post: Thread freezes main UI
Just refresh the Label after assigning it a new Text value.
label_status_title.Text = "Status: changing files.\nfiles changed: " + i + "/" + d.Count + " files.";
label_status_title.Refresh(); //added

Categories