I wanna show a dialog everytime some file has been changed... But everytime that dialog shows up, my app freeze. How can I do this with another thread? Any ideas?
protected virtual void CreateWatcher (object path)
{
if (watcher != null)
{
watcher.EnableRaisingEvents = false;
watcher.Dispose ();
}
//Create a new FileSystemWatcher.
watcher = new FileSystemWatcher ();
//Set the filter to only catch TXT files.
watcher.Filter = "*.txt";
watcher.IncludeSubdirectories = true;
watcher.NotifyFilter = NotifyFilters.LastWrite;
//Subscribe to the Created event.
watcher.Changed += new FileSystemEventHandler (OnChanged);
watcher.Created += new FileSystemEventHandler (OnChanged);
//watcher.Deleted += new FileSystemEventHandler (OnChanged);
//watcher.Renamed += new RenamedEventHandler (OnRenamed);
//Set the path to C:\\Temp\\
watcher.Path = #path.ToString();
//Enable the FileSystemWatcher events.
watcher.EnableRaisingEvents = true;
}
void OnChanged (object source, FileSystemEventArgs e)
{
NovaInteracaoMsg();
}
protected virtual void NovaInteracaoMsg ()
{
novaInteracao = new MessageDialog (this, DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.YesNo, "Foi detectada a mudança nos arquivos do modelo. Deseja inserir uma nova interação?");
ResponseType result = (ResponseType)novaInteracao.Run ();
if (result == ResponseType.Yes) {
OpenInfoWindow (novaInteracaoPath);
return;
}
else {
novaInteracao.Destroy ();
}
}
void OnRenamed (object source, RenamedEventArgs e)
{
//Console.WriteLine ("File: {0} renamed to\n{1}", e.OldFullPath, e.FullPath);
}
protected virtual void OpenInfoWindow (string path)
{
ModMemory.Iteration iterWin = new ModMemory.Iteration (path);
iterWin.Modal = true;
iterWin.Show ();
iterWin.Destroyed += delegate {
// TODO: Funções para executar quando a janela for fechada
// Possivelmente atualizar o número de interações realizadas
Console.WriteLine ("Janela modal destruída");
};
}
The problem is that you are already using another thread. Try one of the following approaches
Set the FileSystemWatcher.SynchronizingObject property so it
raises events on your UI thread. Now you can show a UI that won't
freeze or
Use Control.BeginInvoke() in the event handler.
This was a psychic debugging attempt, there was nothing in your question that helped me be sure that's the correct answer.
Call Show() instead of ShowDialog() in your form.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.show.aspx
Edit - MessageBox classes are Modal, if you want a modeless dialog window you'll have to create it yourself.
Related
I'm trying to make a monitoring page to monitor various filesystem watcher running to do some job. What I need to know is how do you get multiple filesystem watcher's to access the list boxes in the UI thread.. Here is some code:
private void WatchFile(TextBox ctrlTB,ListBox ctrlLB,FileSystemWatcher _watcher)
{
// FileSystemWatcher _watcher = new FileSystemWatcher();
//var localTB = ctrlTB as TextBox;
//var localLB = ctrlLB as ListBox;
_watcher.Path = ctrlTB.Text;
_watcher.Path = ctrlTB.Text;
_watcher.NotifyFilter = NotifyFilters.LastWrite;
_watcher.Filter = "*.xml";
_watcher.Changed += new FileSystemEventHandler(convertXML);
// _watcher.Changed += (s, e) => convertXML(s,e);
// _watcher.Error += new ErrorEventHandler(WatcherError);
_watcher.EnableRaisingEvents = true;
_watcher.IncludeSubdirectories = false;
ctrlLB.Items.Add("Started Monitoring # " + ctrlTB.Text);
ctrlLB.SelectedIndex = ctrlLB.Items.Count - 1;
}
public void convertXML(object source, FileSystemEventArgs f)
{
/// some job
}
I need to post back the status for each filesystemwatcher back to it's respective listbox. I'm declaring the FSW's at the click of the start button. Every List box has a start button where it would be declared separately. An e.g.:
private void button9_Click(object sender, EventArgs e)
{
if (!Directory.Exists(this.textBox1.Text))
{
//Form2.ActiveForm.Text = "Please select Source Folder";
// popup.Show("Please Select Source Folder");
MessageBox.Show("Please Select Proper Source Folder");
return;
}
else
{
textBox1.Enabled = false;
button9.Enabled = false;
button1.Enabled = false;
// button4.Enabled = false;
FileSystemWatcher _watcher = new FileSystemWatcher();
_watcher.SynchronizingObject = this;
WatchFile(textBox1,listBox1 ,_watcher);
}
}
How does the thread know which contrl listbox to access.
Encapsulate your WatchFile and convertXml into its own class like so
public class MyFileWatcher {
private TextBox _textBox;
private ListBox _listBox;
FileSystemWatcher _watcher;
public MyFileWatcher(TextBox textBox, ListBox listBox, ISynchronizeInvoke syncObj) {
this._textBox = textBox;
this._listBox = listBox;
this._watcher = new FileSystemWatcher();
this._watcher.SynchronizingObject = syncObj;
this._watcher.Path = textBox.Text;
this._watcher.Changed += new FileSystemEventHandler(convertXML);
this._watcher.EnableRaisingEvents = true;
this._watcher.IncludeSubdirectories = false;
// add any other required initialization of the FileSystemWatcher here.
}
protected virtual void convertXML(object source, FileSystemEventArgs f) {
// interact with this._textBox and this._listBox here as required.
// e.g.
// this._listBox.Items.Add(f.FullPath);
}
}
This way you can tie the FileSystemWatcher instance to a particular TextBox and ListBox, or any other object you desire.
Then to replace your button9_Click method:
private void button9_Click(object sender, EventArgs e)
{
if (!Directory.Exists(this.textBox1.Text))
{
//Form2.ActiveForm.Text = "Please select Source Folder";
// popup.Show("Please Select Source Folder");
MessageBox.Show("Please Select Proper Source Folder");
return;
}
else
{
textBox1.Enabled = false;
button9.Enabled = false;
button1.Enabled = false;
// instantiate your new instance of your MyFileWatcher class.
MyFileWatcher myWatcher = new MyFileWatcher(textBox1,listBox1 ,this);
}
}
Note: I have not actually compiled or executed this code so there may be exceptions. But the overall pattern should solve what you're after.
My FileSystemWatcher isn't throwing any events. I've looked at these similar questions, none seem to be an answer for my problem:
*Edit: My goal is to capture when an XLS file is copied to or created in a directory.
Filesystemwatcher doesn't trigger event
FileSystemWatcher - event not firing the second time
FileSystemWatcher Changed event doesn't fire
FileSystemWatcher - only the change event once firing once?
Monitor class:
public class Monitor
{
FileSystemWatcher watcher = new FileSystemWatcher();
readonly string bookedPath = #"\\SomeServer\SomeFolder\";
public delegate void FileDroppedEvent(string FullPath);
public event FileDroppedEvent FileDropped;
public delegate void ErrorEvent(Exception ex);
public event ErrorEvent Error;
public Monitor()
{
watcher.Path = bookedPath;
watcher.Filter = "*.xls";
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Changed += new FileSystemEventHandler(watcher_Changed);
watcher.Error += new ErrorEventHandler(watcher_Error);
}
void watcher_Error(object sender, ErrorEventArgs e)
{
Error(e.GetException());
}
void watcher_Changed(object sender, FileSystemEventArgs e)
{
if (e.ChangeType != WatcherChangeTypes.Created) return;
FileDropped(e.FullPath);
}
public void Start()
{
watcher.EnableRaisingEvents = true;
}
public void Stop()
{
watcher.EnableRaisingEvents = false;
}
}
Simple form with Listbox:
public partial class Form1 : Form
{
Monitor monitor = new Monitor();
public Form1()
{
InitializeComponent();
FormClosing += new FormClosingEventHandler(Form1_FormClosing);
Load += new EventHandler(Form1_Load);
monitor.FileDropped += new Monitor.FileDroppedEvent(monitor_FileDropped);
monitor.Error += new Monitor.ErrorEvent(monitor_Error);
}
void Form1_Load(object sender, EventArgs e)
{
monitor.Start();
}
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
monitor.Stop();
}
void monitor_Error(Exception ex)
{
listBox1.Items.Add(ex.Message);
}
void monitor_FileDropped(string FullPath)
{
listBox1.Items.Add(FullPath);
}
}
What am I doing wrong?
Try this out. Works for me for a very similar task.
watcher.NotifyFilter = NotifyFilters.FileName;
watcher.Created += new FileSystemEventHandler(handler);
watcher.Renamed += new RenamedEventHandler(handler);
This may be because the file metadata hasn't been updated yet. This may happen if you are continuously writing to the file.
Have you tried the following:
watcher.Path = directory name;
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = "*.xls";
watcher.Changed += OnDirectoryChange;
watcher.Error += OnError;
watcher.EnableRaisingEvents = true;
// Watch only files not subdirectories.
watcher.IncludeSubdirectories = false;
Your issue is with the filters as well as your events I believe. NotifyFilters.LastAccess will only trigger when a file is opened. Try using:
NotifyFilters.LastWrite | NotifyFilters.CreationTime
This will watch for written/created files. Next, hook up to the Created delegate to handle newly created files:
watcher.Created += YourDelegateToHandleCreatedFiles
The way FileSystemWatcher works is to first use the NotifyFilters to limit the event triggers. Then, you use the actual events to do the work. By hooking into the Created event you'll only do work when a file is created.
This question already has answers here:
FileSystemWatcher Changed event is raised twice
(44 answers)
Closed 6 years ago.
public void startWatch()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = Path.GetDirectoryName(_file);
watcher.Filter = Path.GetFileName(_file);
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Changed += watcher_Changed;
watcher.EnableRaisingEvents = true;
}
public void watcher_Changed(object sender, FileSystemEventArgs e)
{
// Jump twice
}
Why this event jump twice after my text file changed?
Here is the sample to avoid event raising.
public void OnChanged(object source, FileSystemEventArgs e)
{
FileSystemWatcher watcher = null;
try
{
watcher = (FileSystemWatcher)source;
watcher.EnableRaisingEvents = false;
}
finally
{
if (watcher != null)
{
watcher.EnableRaisingEvents = true;
}
}
}
This is mt first time trying to write a not web based program, and my first time writing anything in C#.
I need a program that monitors folders, but I can't get it to work.
I have used the example from this post Using FileSystemWatcher with multiple files but is trying to make it a form.
My current problem comes in the ProcessQueue function where fileList apparently is defined in another thread.
Whenever a file is actually submitted to the watched folder I get an error that using fileList is a cross thread call
Can anyone explain this error to me, and how to fix it?
namespace matasWatch
{
public partial class Form1 : Form
{
private int n = 1;
private bool isWatching = false;
private List<string> filePaths;
private System.Timers.Timer processTimer;
private string watchedPath;
private FileSystemWatcher watcher;
public Form1()
{
filePaths = new List<string>();
watchedPath = "C:\\Users\\username\\Desktop\\test";
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (!isWatching)
{
button1.Text = "Stop";
isWatching = true;
watcher = new FileSystemWatcher();
watcher.Filter = "*.*";
watcher.Created += Watcher_FileCreated;
watcher.Error += Watcher_Error;
watcher.Path = watchedPath;
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
}
else {
button1.Text = "Watch";
isWatching = false;
watcher.EnableRaisingEvents = false;
watcher.Dispose();
watcher = null;
}
}
private void Watcher_Error(object sender, ErrorEventArgs e)
{
// Watcher crashed. Re-init.
isWatching = false;
button1_Click(sender, e);
}
private void Watcher_FileCreated(object sender, FileSystemEventArgs e)
{
filePaths.Add(e.FullPath);
if (processTimer == null)
{
// First file, start timer.
processTimer = new System.Timers.Timer(2000);
processTimer.Elapsed += ProcessQueue;
processTimer.Start();
}
else{
// Subsequent file, reset timer.
processTimer.Stop();
processTimer.Start();
}
}
private void ProcessQueue(object sender, ElapsedEventArgs args)
{
try
{
fileList.BeginUpdate();
foreach (string filePath in filePaths)
{
fileList.Items.Add("Blaa");
}
fileList.EndUpdate();
filePaths.Clear();
}
finally
{
if (processTimer != null)
{
processTimer.Stop();
processTimer.Dispose();
processTimer = null;
}
}
}
}
}
I assume that fileList is a windows forms control. The ProcessQueue method is called from a timer thread which is by default a background thread. The fileList control resides in the UI thread. You need to use the Invoke() method of the form passing it in a delegate the updates the fileList control.
Invoke(new Action(() =>
{
fileList.BeginUpdate();
foreach (string filePath in filePaths)
{
fileList.Items.Add("Blaa");
}
fileList.EndUpdate();
filePaths.Clear();
}));
Try using System.Windows.Forms.Timer instead of System.Timers.Timer so the timer tick event is executed on the UI thread.
See here for more details.
So, I'm trying to make a file changed notifier, and I need to make it so the text in a textbox updates whenever the contents of the file are changed. This is what I have so far:
string path = "C:/Users/Max/Dropbox/Public/IM.txt";
StringBuilder b = new StringBuilder();
private void Window_Loaded(object sender, EventArgs e)
{
TB.Text = File.ReadAllText(path);
b.Append(TB.Text);
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = path.Remove(path.Length - 6, 6);
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = "*.txt";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
TB.SelectionStart = TB.Text.Length;
TB.ScrollToCaret();
}
private void OnChanged(object source, FileSystemEventArgs e)
{
TB.Text = File.ReadAllText(path);
}
This seems to raise the event correctly, but as soon as it touches the code in the OnChanged event, the program exits, no errors or anything, just closes. I have tried to stop it from closing, I have even tried putting e.Cancel under the formclosing event, but nothing seems to work. Any ideas? I can provide more info if needed.
Have you tried wrapping the code in try catch
private void OnChanged(object source, FileSystemEventArgs e)
{
try
{
TB.Text = File.ReadAllText(path);
}catch(Exception e)
{
//Show exception in messagebox or log to file.
}
}
Try this in your Changed method
if (TB.InvokeRequired)
{
TB.Invoke(new MethodInvoker(delegate { TB.Text = File.ReadAllText(path); }));
}