I'm trying to update my ObservableCollection as the FileSystemWatcher notifies changes. I know this is not possible because of cross thread operations.
So i would like to get the name of the file created/deleted/renamed when the event is fired and update it in the UI thread once the event is completed, as we do in BackgroundWorker. Can anyone tell me how to do this?
Also tell me where i should define and start this FileSystemWatcher. Currently i've defined it in the MainViewModel.
P.S.: I've seen similar questions in SO, but didn't get a clear picture
Thanks In Advance,
Veer
I would think the main view model is the right place to define the FileSystemWatcher. As for the threading issues, this is the easy way:
_watcher = new FileSystemWatcher(path);
_watcher.Created += (obj, e) =>
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
{
// Code to handle Created event
};
_watcher.Changed += (obj, e) =>
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
{
// Code to handle Changed event
};
_watcher.Renamed += (obj, e) =>
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
{
// Code to handle Renamed event
};
_watcher.Deleted += (obj, e) =>
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
{
// Code to handle Deleted event
};
// ...
_watcher.EnableRaisingEvents = true;
Each of the "Code to handle" will execute within the UI thread so it can update the ObservableCollection. Note that the FileSystemEventArgs "e" is available within this code.
If you prefer to use separate event handler methods you can call them from the above code or use this convenient shortcut:
var switchThread =
(FileSystemEventHandler handler) =>
(object obj, FileSystemEventArgs e) =>
Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
handler(obj, e))
_watcher = new FileSystemWatcher(path);
_watcher.Created += switchThread(OnCreated);
_watcher.Changed += switchThread(OnChanged);
_watcher.Deleted += switchThread(OnDeleted);
_watcher.Renamed += switchThread(OnRenamed);
_watcher.EnableRaisingEvents = true;
where OnCreated, OnChanged, OnDeleted, and OnRenamed are normal event handler methods with the normal signature, for example:
void OnChanged(object sender, FileSystemEventArgs e)
{
// Code to handle Changed event
}
Personally I prefer the first way of doing it because I don't like creating four extra 1-line methods.
Note that your view model will need to know which Dispatcher to call back on. The easiest way to do this is to derive your view model from DispatcherObject, as assumed above. Another way is for the view model's constructor or the method that registers the FileSystemWatcher events to store a copy of Dispatcher.Current in a local field or local variable, then use that for the .BeginInvoke calls.
Also note that you can use exactly the same code in your view code-behind instead of in your view model if you prefer.
public void SomeActionToBeInvokedOnTheMainThread()
{
if (someControl.Dispatcher.CheckAccess())
{
// you can modify the control
}
else
{
someControl.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(SomeActionToBeInvokedOnTheMainThread)
);
}
}
I used Ray B.'s approach but had to modify things slightly and thought I'd post an update here to maybe save others some time.
My VS2010/.NET 4.0 WPF project was throwing the error:
Cannot assign lambda expression to an implicitly-typed local variable
After some tweaking I came up with the following. Note the additional var defined to handle the Renamed event:
var switchThreadForFsEvent = (Func<FileSystemEventHandler, FileSystemEventHandler>)(
(FileSystemEventHandler handler) =>
(object obj, FileSystemEventArgs e) =>
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
handler(obj, e))));
var switchThreadForFsRenameEvent = (Func<RenamedEventHandler, RenamedEventHandler>)(
(RenamedEventHandler handler) =>
(object obj, RenamedEventArgs e) =>
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
handler(obj, e))));
_fileSystemWatcher = new FileSystemWatcher(documentCollectionPath);
_fileSystemWatcher.Created += switchThreadForFsEvent(OnFileCreated);
_fileSystemWatcher.Deleted += switchThreadForFsEvent(OnFileDeleted);
_fileSystemWatcher.Renamed += switchThreadForFsRenameEvent(OnFileRenamed);
_fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName;
_fileSystemWatcher.IncludeSubdirectories = true;
_fileSystemWatcher.EnableRaisingEvents = true;
Related
I need to pass a parameter (in C#) to an event handler and then be able to detach the event handler.
I attach the event handler and pass the parameter:
_map.MouseLeftButtonUp += (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);
The event is called as expected. I try to detach the event handler:
_map.MouseLeftButtonUp -= (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);
The code executes without error, but does not seem to detach.
If I attach the event handler the more conventional way (without passing a parameter):
_map.MouseLeftButtonUp+=_map_MouseLeftButtonUp;
and detach
_map.MouseLeftButtonUp -= _map_MouseLeftButtonUp;
everything works as expected
Detaching the event handler (that takes a parameter) via the more conventional way
_map.MouseLeftButtonUp -= _map_MouseLeftButtonUp2;
gives me an error saying the delegates don't match (which makes sense)
So my question is: Why is the event handler not really being detached when I pass a parameter, and is there a way to circumvent this problem.
When you create a lambda (anonymous) function, you're actually creating a new function each time.
The reason your first two lines don't work is because they are two completely different functions that just happen to do the same thing. The correct way to detach would be to have subscribe and unsubscribe from a function, as you've already figured out.
An alternative, that's probably not worth it, is to save your lambda to a variable.
Action<object, MouseButtonEventArgs> myEventMethod = (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);
_map.MouseLeftButtonUp += myEventMethod;
// ...
_map.MouseLeftButtonUp -= myEventMethod;
The reason is that two delegates are not equal:
// You add one delegate instance
_map.MouseLeftButtonUp += (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);
// ..And try to remove another one (not previous!) That's why the first delegate remains unremoved
_map.MouseLeftButtonUp += (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);
You can convince youself by
var x = (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);
var y = (sender, e) => _map_MouseLeftButtonUp2(sender, e, showResultsWindow);
if (Object.Equals(x, y)) { // <- You expected this behaviour
...
}
else { // <- Alas, this is a real situation: x != y
...
}
The reason of such a behaviour is that when Object.Equals is not overridden (and in case of delegates it's not) Object.Equals works as Object.RefrenceEquals does, which checks instances referenses (addresses). Sure, that addresses of x and y are different as well as two your delegates
I have a WPF C# projects with the below buttonClick event void:
public void ButtonClick(object sender, RoutedEventArgs e)
{
_worker.WorkerReportsProgress = true;
_worker.DoWork += (o, ea) =>
{
try
{
_class1.hithere();
}
catch (Exception exception)
{
MessageBox.Show(exception.Message);
}
};
_worker.ProgressChanged += (o, ea) =>
{
};
_worker.RunWorkerCompleted += (o, ea) =>
{
MessageBox.Show("Done");
};
_worker.RunWorkerAsync();
}
I have a folder in the application called InformationProviders than contains the Class1.cs file and I have implemented the correct using MyApplication.InformationProviders; statement in the MainWindow.xaml.cs file that contains the button click event above.
I have also declared the Class1 class that is then called upon in the backgroundworker DoWork event correctly as such:
readonly Class1 _class1 = new Class1();
The Class1.cs file contains this little code made just to see if it worked and it doesn't unfortunately:
public class Class1
{
public void hithere()
{
MessageBox.Show("Hi, I'm working.");
}
}
What am I missing here???? I declared the class as public and (I believe) declared all that needed to be declared to make the process work...
All it does is display a message saying "Done", meaning it has completed the backgroundworker process (even though it did not do anything at all that was stated in the DoWork event. So pretty much, launching the worker and immediately considering it finished.
Regards and thanks,
Simon
Here's the tricky thing about running a multi-threaded application: only one thread has access to the UI and perform operations on it.
In case of your code, the BackgroudWorker in it's background operation attempts to show a message using MessageBox. This won't work - it's not being "fired" on the UI thread!
If you absolutely MUST perform UI operations from inside the BackgroundWorker (which you shouldn't do - this is what the ProgressChanged event is for) then you can use a Dispatcher class.
Here's a short example:
private void Button_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (s, a) =>
{
this.Dispatcher.Invoke(new Action(() => MessageBox.Show("doing stuff")));
};
bw.RunWorkerCompleted += (s, a) =>
{
MessageBox.Show("done");
};
bw.RunWorkerAsync();
}
Also fun fact, if you use Dispatcher.Invoke (as written above), then "doing stuff" will appear first, if you use Dispatcher.BeginInvoke then "done" will appear first, because the other operation will be queued on the UI thread.
Here's the "politically correct" way to use the BackgroundWorker:
bw.WorkerReportsProgress = true;
bw.DoWork += (s, a) =>
{
bw.ReportProgress(0, "doing stuff");
};
bw.ProgressChanged += (s, a) =>
{
MessageBox.Show(a.UserState as String);
};
I found the problem, i was using the Xceed.WPF.toolkit version of the MessageBox and it was just refusing to show that UI element in a backgroundWorker. Thanks for the help though it pointed me in the right direction. hoping this will help other people
I have to load a window and in Window_Loaded I have to load some variables and show it on Window.
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (o, ea) =>
{
try
{
//code to download some variables which will show on UI of Window Loading
}
catch (Exception ex)
{
//The calling thread cannot access this object because a different thread owns it.
}
};
worker.RunWorkerCompleted += (o, ea) =>
{
};
worker.RunWorkerAsync();
}
But I am getting a threading exception. Is there any way to show the variables value on window from DoWork of Backgroundworker?
You should retrieve the data you need in the DoWork section, then assign it to ea.Result, which will make it available in the RunWorkerCompleted section.
In the RunWorkerCompleted section, you can access ea.Result again, casting the object back to whatever type you assigned in DoWork, and apply the data as needed to your UI controls.
worker.DoWork += (o, ea) =>
{
ea.Result = GetMyData();
};
worker.RunWorkerCompleted += (o, ea) =>
{
var myData = (myDataType)ea.Result;
// Assign myData as needed to UI components...
};
You need to let Dispatcher schedule your code to execute on UI thread and marshal necessary parameters. Try something like this:
Dispatcher.Invoke(
new Action<string>(() =>
{
// Access UI from here
}),
DispatcherPriority.Normal
);
Although this (or something like this, since this is notepad code) will solve your problem, you should consider using MVVM pattern in your implementation. Then you will be able to make changes to ViewModel (just update data) and UI will update accordingly.
I have a MainFrame window with imageViewer control on it. Also there is my dll which calculates changes for the image all was working fine before I decided to add ProgressDialog.(( The Idea was - firstly I am loading the image via dll to main frame (this still OK). Then if user clicks button then show ProgressDialog and in worker.DoWork create new image via the same dllwrapper class (I am using "new")
All seems to be ok but when i am trying to set my currentImage property of imageviewer control (this is nothing more then setter for Image)it show me this error!
This is the code of my userButtonClickHandler from where I am launching ProgressDialog:
void OnThumbnailClick(object sender, RoutedEventArgs e)
{
pd = new ProgressDlg();
pd.Cancel += CancelProcess;
int max = 1000;
System.Windows.Threading.Dispatcher pdDispatcher = pd.Dispatcher;
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
LibWrap lwrap = new LibWrap();//!NEW instance for dll wrapper!
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//ERROR IS HERE!!!//The calling thread cannot access this object because a different thread owns it.
//what process??
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
pd.Close();
};
worker.RunWorkerAsync();
pd.ShowDialog();
}
There is function from the same MainFrame class for canceling (There is OK too)
void CancelProcess(object sender, EventArgs e)
{
worker.CancelAsync();
}
This is class for ProgressDlg (it has nothing more then progress bar and cancel button):
public partial class ProgressDlg : Window
{
public ProgressDlg()
{
InitializeComponent();
}
public string ProgressText
{
set
{
this.lblProgress.Content = value;
}
}
public int ProgressValue
{
set
{
this.progress.Value = value;
}
}
public event EventHandler Cancel = delegate { };
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
Cancel(sender, e);
}
}
}
I am working with this problem for (almost) two days and still couldn't find the solution. Help me please if you have an idea.
1 UPDATE
It seems to me that you was right about this threads - when I am trying to load previously loaded(initial) image (from the main thread) -it loads OK but if I am trying libWrap it fails due to processes conflict!
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
imageViewer.Width = 1000;//work!
imageViewer.CurrentImage = MyPrj.App.draggedImage;//Work!
imageViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));//Fail =(!
}
2 UPDATE
I have tried this construction OnThumbnailClick
Application.Current.MainWindow.Dispatcher.BeginInvoke(new Action(() =>
{
imaeViewer.CurrentImage = lwrap.engine2(BitmapFrame.Create(FXPhotoStudio.App.draggedImage));
}
This caused same error/ Perhaps it will be correct to pass this value in MainThread (UI)? But I have no idea how.( I couldnot use serializers - becouse it is rapidly calling operation and this images are temporary/
WPF cannot alter items that were created on another thread.
So if you create an ImageViewer on one thread, you cannot alter it's properties on another thread.
Instead, use the Dispatcher, which is WPF's internal message queue for the main UI thread, to update your objects.
Or, use Henk's Answer to do your work on another thread, but return the result to the main thread so it can update your ImageViewer's properties
You need at least these changes:
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
args.Result = lwrap.engine2(BitmapFrame.Create(MyPrj.App.draggedImage));
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
if (args.Error != null)
{ ... } // handle error
else if (args.Cancelled)
{ ... } // handle Cancel
else
{
imageViewer.CurrentImage = args.Result;
}
pd.Close();
}
I'm not sure if it's enough but try again.
The imageViewer was created on the main thread of the application (which is appropriate because it is a UI control). UI controls can ONLY be accessed by the thread which created it, and that thread must have its own dispatcher (by which I mean message loop).
Remove the threading code, and it will work.
If you want this to popup the window and then show the image when the conversion completes, you will have to store the returned image in a variable until you return to the main thread, and then make the assignment to the imageViewer.
I'm gonna create a BackgroundWorker with an anonymous method.
I've written the following code :
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(
() =>
{
int i = 0;
foreach (var item in query2)
{
....
....
}
}
);
But Delegate 'System.ComponentModel.DoWorkEventHandler' does not take '0' arguments and I have to pass two objects to the anonymous method : object sender, DoWorkEventArgs e
Could you please guide me, how I can do it ?
Thanks.
You just need to add parameters to the anonymous function:
bgw.DoWork += (sender, e) => { ... }
Or if you don't care about the parameters you can just:
bgw.DoWork += delegate { ... }
If you specify a lambda, you must ensure it takes the same number of arguments:
bgw.DoWork += (s, e) => ...;
But if you're not using the arguments, you could just use an anonymous delegate without parameters:
bgw.DoWork += delegate
{
...
};
If you have written the above without lambdas how it would be?
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
and the named method:
private void backgroundWorker1_DoWork(object sender,
DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
// Assign the result of the computation
// to the Result property of the DoWorkEventArgs
// object. This is will be available to the
// RunWorkerCompleted eventhandler.
e.Result = ComputeFibonacci((int)e.Argument, worker, e);
}
But now you are using lambdas with no bound variables ()=>
You should provide two objects sender and e (which they will get type inferred later).
backgroundWorker1.DoWork += (sender, e) => ...
Lets make it simple
Lambda expressions are really handy to make the code shorter and more readable. However entry level programmers might find it a bit difficult to deal with. There are three separate concepts one should go through: anonymous methods, delegates and lambda expressions. A detailed walk-through of each of them is beyond the scope of this answer. I hope that the code example given below will serve the purpose of giving a quick view of the different approaches available.
class TestBed
{
BackgroundWorker bgw = new BackgroundWorker();
void sample()
{
//approach #1
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
//DoWorkEventHandler is nothing but a readily available delegate written by smart Microsoft guys
//approach #2, to make it a little shorter
bgw.DoWork += (s,e) =>
{
//...
};
//this is called lambda expression (see the => symbol)
//approach #3, if lambda scares you
bgw.DoWork += delegate
{
//... (but you can't have parameters in this approach
};
//approach #4, have a helper method to prepare the background worker
prepareBgw((s,e)=>
{
//...
}
);
//approach #5, helper along with a simple delegate, but no params possible
prepareBgw(delegate
{
//...
}
);
//approach #6, helper along with passing the methodname as a delegate
prepareBgw(bgw_DoWork);
//approach #7, helper method applied on approach #1
prepareBgw(new DoWorkEventHandler(bgw_DoWork));
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
//...
}
void prepareBgw(DoWorkEventHandler doWork)
{
bgw.DoWork+= doWork;
}
}
Note that we used "delegate" and not "Delegate" in this example (there is a difference between the two)