I currently have two different event handlers in C#, which perform two different functions. Although how could I combine the two methods together, so only 1 button could perform both actions? (Taking into account that button1_Click event must be performed first.)
private void button2_Click(object sender, EventArgs e)
{
var file = File.AppendText(#"c:\output2.txt");
foreach (string tmpLine in File.ReadAllLines(#"c:\output.txt"))
{
if (File.Exists(tmpLine))
{
file.WriteLine(tmpLine);
}
}
file.Close();
}
private void button1_Click(object sender, EventArgs e)
{
using (StreamWriter sw = File.AppendText(#"c:\output.txt"))
{
StreamReader sr = new StreamReader(#"c:\filename.txt");
string myString = "";
while (!sr.EndOfStream)
{
myString = sr.ReadLine();
int index = myString.LastIndexOf(":");
if (index > 0)
myString = myString.Substring(0, index);
sw.WriteLine(myString);
}
button2_Click(sender, e);
}
}
Instead of writing the code in the event handler, bring them out into two functions and then call those functions whichever way you want from the event handler.
You could, if I'm understanding you correctly, have one event handler call another. An event handler is "just" a method after all, so:
private void button1_Click(object sender, EventArgs e)
{
// All the code that's currently there
button2_Click(sender, e);
}
Or, you could extract the code from the event handlers into separate methods:
private void button1_Click(object sender, EventArgs e)
{
WriteToOutputDotTxt();
OtherMethodThatWritesToOutputDotTxt();
}
private void button2_Click(object sender, EventArgs e)
{
OtherMethodThatWritesToOutputDotTxt();
}
private void WriteToOutputDotTxt()
{
// Code that's currently in button1_Click
}
private void OtherMethodThatWritesToOutputDotTxt()
{
// Code that's currently in button2_Click
}
The code doesn't have to be contained within the event handlers, in fact you'll find it easier (if you're interested!) to test your code if you can separate it away from your UI. You could, for example, have a class called ProcessOutputFile and move the WriteToOutputDotTxt and OtherMethodThatWritesToOutputDotTxt methods onto that class. It's then a lot easier to write tests for that code as it's not "tied into" the UI code.
I try to keep most logic out of event handlers and create functions with logical names that i call from the event handlers. Then they can be called from wherever.
Just attach two event handlers to some button:
somebutton.Click += new EventHandler(button1_Click);
somebutton.Click += new EventHandler(button2_Click);
// given these two methods extracted from your events
void DoBar(object sender, EventArgs e)
{
var file = File.AppendText(#"c:\output.txt");
foreach (string tmpLine in File.ReadAllLines(#"c:\filename.txt"))
{
if (File.Exists(tmpLine))
{
file.WriteLine(tmpLine);
}
}
file.Close();
}
void DoFoo(object sender, EventArgs e)
{
using (StreamWriter sw = File.AppendText(#"c:\output.txt"))
{
StreamReader sr = new StreamReader(#"c:\filename.txt");
string myString = "";
while (!sr.EndOfStream)
{
myString = sr.ReadLine();
int index = myString.LastIndexOf(":");
if (index > 0)
myString = myString.Substring(0, index);
sw.WriteLine(myString);
}
}
}
// you can subscribe like this
button1.Click += DoFoo;
button1.Click += DoBar;
button2.Click += DoBar;
EDIT
forgot my sender and eventargs
Related
I have a method in my class that has some loops inside.
Main purpose of this method is converting some files so I put a progressbar in my form that should get updated after each file has been converted.
I tried every possible combination and I read everything I could but I couldn't solve this issue.
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
converterProgressBar.Value = e.ProgressPercentage;
}
is called only after the main loop of my method has been executed.
This is my method:
public string Convert()
{
convertBtn.Enabled = false;
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
totalCount = files.length;
bw.RunWorkerAsync();
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
foreach (string file in files)
{
countFile++;
if (chk.Checked)
{
class1.DoJob();
}
using (// some code))
{
using (//some other code))
{
try
{
using (// again some code)
{
// job executing
}
}
catch (exception
{
}
}
}
convertedVideosL.Text = txtToUpdate;
convertedVideosL.Refresh();
}
countFile = countFile + 1;
MessageBox.Show("Done");
countFile = -1;
return outputFile;
}
And here are the BackgroundWorker Event Handlers:
void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= totalCount; i++)
{
if (bw.CancellationPending)
{
e.Cancel = true;
}
else
{
int progress = Convert.ToInt32(i * 100 / totalCount);
(sender as BackgroundWorker).ReportProgress(progress, i);
}
}
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
converterProgressBar.Value = e.ProgressPercentage;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == false)
{
convertedVideosL.Text = "Finished!";
}
else
{
convertedVideosL.Text = "Operation has been cancelled!";
}
}
But I cannot get to update the progress bar for every file that is converted.
It waits for the foreach loop to end and then calls bw_ProgressChanged.
If I put RunWorkerAsync() inside the foreach loop an exception is thrown that says the BackgroundWorker is busy and cannot execute other tasks.
It seems to me obvious that DoWork() only executes a for loop then it shouldn't be aware of the conversion going on but ProgressChanged should be fired by ReportProgress(progress,i).
Could please someone explain me why and help me with a solution?
Thanks!
Currently the conversion is not executed by the instance of the BackgroundWorker type. The conversion should be called from the DoWork event handler.
Please consider extracting the conversion-related functionality:
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
foreach (string file in files)
{
// Details...
}
into the separate method. After that just call the method from the DoWork event handler.
Pseudo-code to demonstrate the idea:
public void StartConversion()
{
...
TWorkerArgument workerArgument = ...;
worker.RunWorkerAsync(workerArgument);
// No message box here because of asynchronous execution (please see below).
}
private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
e.Result = Convert(worker, (TWorkerArgument)e.Argument);
}
private static TWorkerResult Convert(BackgroundWorker worker, TWorkerArgument workerArgument)
{
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
foreach (string file in files)
{
// Details...
worker.ReportProgress(percentComplete);
}
return ...;
}
private void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Show the message box here if required.
}
Please replace the TWorkerArgument and TWorkerResult types appropriately.
Also, please refer to the example which uses the BackgroundWorker class for the additional details: How to: Implement a Form That Uses a Background Operation, MSDN.
I am trying to create a small form in C# to find one string in a TMX file (xml) and replace it for another. Then it would create an output file with all the modifications.
The form contains a search button to locate the file in the local disk and a REPLACE button which it would change "srclang="all"" for "srclang="en-US"".
So far I have the following:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void openFileDialog1_FileOk(object sender, CancelEventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
using (FileDialog fileDialog = new OpenFileDialog())
{
if (DialogResult.OK == fileDialog.ShowDialog())
{
string filename = fileDialog.FileName;
textBox1.Text = fileDialog.FileName;
}
}
}
private void label1_Click(object sender, EventArgs e)
{
}
private void button3_Click(object sender, EventArgs e)
{
this.Close();
}
private void button2_Click(object sender, EventArgs e)
{
StreamWriter writer = null;
Dictionary<string, string> replacements = new Dictionary<string, string>();
replacements.Add("*all*", "en-US");
// ... further replacement entries ...
using (writer = File.CreateText("output.txt"))
{
foreach (string line in File.ReadLines(textBox1.Text))
{
bool replacementMade = false;
foreach (var replacement in replacements)
{
if (line.StartsWith(replacement.Key))
{
writer.WriteLine(string.Format("{1}",
replacement.Key, replacement.Value));
replacementMade = true;
break;
}
}
if (!replacementMade)
{
writer.WriteLine(line);
}
}
}
File.Replace("output.txt", textBox1.Text, "ORIGINAL_TMX_FILE.bak");
}
}
}
This code is from Dave R. from this site, it really works with TXT files but I am not sure what I am doing wrong. I am totally a newbie here.
If anyone could help me write some lines to make it work, I would really appreciate it!
Probably the simplest thing that could work would be to use these methods
File.WriteAllLines()
File.ReadAllLines()
as in the example below:
File.WriteAllLines(
outputFileName,
File.ReadAllLines(inputFileName)
.Select(line => line.Replace(#"srclang=""en-US""", #"srclang=""all"""));
If there could be whitespace in the search text, you could replace the call to string.Replace with a Regex.Replace.
I think I understand what you're trying to do and the following segment is the essence of your code:
if (line.StartsWith(replacement.Key))
{
writer.WriteLine(string.Format("{1}",
replacement.Key, replacement.Value));
replacementMade = true;
break;
}
If your dictionary will always have one element, you only have to change the parameter of the WriteLine call. You're writing only the replacement value instead of the whole line with the replacements done on it. You might want to have something like
writer.WriteLine(line.Replace(replacement.Key, replacement.Value)
If you want to check for several replacements, store the replaced line in the inner loop and only write it at the end and remove the break command.
Another unrelated observation: the replacement.Key parameter in the WriteLine call is omitted since {1} refers to the second additional parameter (counting starts from 0).
I need to watch several file at different time and sometimes at the same time.
I am using this as a test:
namespace FilewatcherTest
{
public partial class Form1 : Form
{
private System.IO.FileSystemWatcher FSWatcherTest;
public Form1()
{
InitializeComponent();
FSWatcherTest = new FileSystemWatcher();
EventHandling();
FSWatcherTest.Path = #"d:\tmp";
FSWatcherTest.Filter = "file.txt";
// Begin watching.
FSWatcherTest.EnableRaisingEvents = true;
}
protected void EventHandling()
{
FSWatcherTest.Changed += FSWatcherTest_Changed;
FSWatcherTest.Deleted += FSWatcherTest_Deleted;
FSWatcherTest.Renamed += FSWatcherTest_Renamed;
FSWatcherTest.Created += FSWatcherTest_Created;
}
private void FSWatcherTest_Changed(object sender, System.IO.FileSystemEventArgs e)
{
WriteToLog("File Changed");
}
private void FSWatcherTest_Created(object sender, System.IO.FileSystemEventArgs e)
{
WriteToLog("File Created");
}
private void FSWatcherTest_Deleted(object sender, System.IO.FileSystemEventArgs e)
{
WriteToLog("File Deleted");
}
private void FSWatcherTest_Renamed(object sender, System.IO.RenamedEventArgs e)
{
WriteToLog("File Renamed");
}
private void WriteToLog(string message)
{
using (var sw = new StreamWriter(#"d:\tmp\service.log", true))
{
sw.WriteLine(string.Format("{0} {1}", DateTime.Now,message));
}
}
}
}
Of course I'll change the hardcoded paths once I have something in place since this is going into a service I created.
My question is, can I use the same file watcher or should I use a unique one for each file?
If I use the same one, how do I know which file is raising the event?
Thanks!!
EDIT
Sorry I haven't used filesystemwatcher before and didn't know it mattered but the files will be in different directories and not of the same file type.
can I use the same file watcher or should I use a unique one for each file?
In your case, I don't think there is a reason to create a new instance of FileSystemWatcher for every file you're watching. Yes, you can use the same one. You can use a filter such as "*.txt" or whatever you need to watch a set of files...
If I use the same one, how do I know which file is raising the event?
The FileSystemEventArgs has a Name property which returns the name of the file that triggered the event.
So for example:
private void FSWatcherTest_Created(object sender, System.IO.FileSystemEventArgs e)
{
string fileName = e.Name;
WriteToLog("File Created: " + fileName);
}
in my Winform a read file method is implemented on button click.when big files are read my Ui hangs until the loop is over.I need to have control on my UI all the time.
i know that is done before and i tried some but i am still having an
error when i try to use some form controls like this :(translated!)
the access of control element comboBox1 is from another thread rather than the thread in which it is created in !!!
private void button1_Click(object sender, EventArgs e)
{
//some code
using (StreamReader sr = new StreamReader(file, System.Text.Encoding.ASCII))
{
while (sr.EndOfStream == false)
{
line = sr.ReadLine();
UpdateProgressBar(line.Length);
}
}
//some code
}
Add a BackgroundWorker to your class on Form (or Control) load. Then hookup its events:
BackgroundWorker _worker;
void Form_Load(object sender, EventArgs e)
{
_worker = new BackgroundWorker();
_worker.DoWork += _worker_DoWork;
_worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
_worker.ProgressChanged +=_worker_ProgressChanged;
_worker.WorkerReportsProgress = true;
}
private void button1_Click(object sender, EventArgs e)
{
_worker.RunWorkerAsync(file);//pass on the file name
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
var file = e.Argument as String;
using (StreamReader sr = new StreamReader(file, Encoding.ASCII))
{
while (sr.EndOfStream == false)
{
line = sr.ReadLine();
_worker.ReportProgress(line.Length);
}
}
}
private void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Report porogress bar change
UpdateProgressBar(e.ProgressPercentage);
}
private void _worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//do any stuff you want after reading the file.
}
Read more about this here.
Use a BackgroundWorker to do the work on a separate thread.
Use BackgroundWorker
[edit] Tutorial available here
You really should have consulted the documentation before asking a question. Anyways, here's an example of how you can do it using a BackgroundWorker:
private void button1_Click(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker;
worker.WorkerReportsProgress = true;
worker.ProgressChanged += ProgressChanged;
worker.DoWork += ReadStream;
worker.RunWorkerAsync(comboBox1.Text);
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
UpdateProgressBar(e.ProgressPercentage);
comboBox1.Text = e.UserState.ToString();
}
private void ReadStream(object sender, DoWorkEventArgs doWorkEventArgs)
{
BackgroundWorker worker = sender as BackgroundWorker;
string line;
string comboBoxText = doWorkEventArgs.Argument.ToString();
using (StreamReader sr = new StreamReader("file", System.Text.Encoding.ASCII))
{
while (!sr.EndOfStream)
{
line = sr.ReadLine();
worker.ReportProgress(line.Length);
worker.ReportProgress(line.Length, "NEW COMBOBOX TEXT");
}
}
}
use backgroundworker to carry out heavy operations.
it has event to report progress of the task , so you can use it to update progress bar.
I'm creating a List of FileSystemWatchers.
List<ExtSystemFileWatcher> fswMonitors = new List<FileSystemWatcher> ();
The number of them in the list is dynamic depending on the user. This is done from a INI file and an array of Monitor objects from my own Monitor class are created. The class simply has varibles like the Montior number, Path to monitor, Ext to look for etc.
if (iNumberMonitors > 0)
{
obMonitors = ReadMonitors(iNumberMonitors);
for (int iCounter = 0; iCounter < iNumberMonitors; iCounter++)
{
FileSystemWatcher fswCurrent = new FileSystemWatcher();
fswCurrent.Path = obMonitors[iCounter].strMonPath;
fswCurrent.EnableRaisingEvents = true;
fswCurrent.NotifyFilter = NotifyFilters.FileName;
fswCurrent.Filter = "*." + obMonitors[iCounter].strMonExt;
fswCurrent.Deleted += OnDelete;
fswMonitors.Add(fswCurrent);
}
}
In the 'OnDelete' Method that each FileSystemWatcher calls if the Delete event fires I need to know which of the FileSystemWatchers is calling it.
My question is how can I know which FileSystemMonitor in the List is calling the method?
Do you need anything else more than just checking sender in your eventHandler?
private void OnDelete(object sender, ...)
{
var watcher = (FileSystemWatcher) sender;
// probably list.IndexOf here if you really need an index
}
You can use a closure where you hook your "Delete event"
instead of passing the method itself:
fswCurrent.Deleted += OnDelete;
you pass something like
fswCurrent.Deleted += (sender, e) => OnDelete(sender, e, iCounter)
of course you will need to change the signature of OnDelete to take the additional Index.
After reading your comment you might don't need it though, as the other answers suggests.
The event handler has a sender parameter that is a reference to the FileSystemWatcher that raised the event.
private static void OnDeleted(object source, FileSystemEventArgs e)
{
FileSystemWatcher watcher = source as FileSystemWatcher;
if(watcher != null)
{
string deletedFile = e.FullPath;
//Update db with watcher and deletedFile
}
}
"sender" parameter in OnDelete method will point to the originator of the event
void OnDelete(object sender, EventArgs e)
{
var watcher = ((FileSystemWatcher) sender);
.....
}
Just cast the sender parameter of the event handler which you attach to the Deleted event.
For example:
class Program
{
static void Main(string[] args)
{
var watcher = new FileSystemWatcher { Path = #"c:\temp", Filter = "*.txt" };
watcher.Deleted += watcher_Deleted;
watcher.EnableRaisingEvents = true;
Console.ReadLine();
}
static void watcher_Deleted(object sender, FileSystemEventArgs e)
{
var watcher = sender as FileSystemWatcher;
}
}
May be check if each of these pooled objects might have an hashcode in it. Which should be unique.