I have a Winform GUI that have an UDPClient listener thread working on second plane to avoid halt the GUI; when the thread receive something call a Sub in the WinForm code; that Sub process the data and must to fill different TextBox depending on the received data.
As you know if I try to change the text property of any control from the Sub I will receive a Cross Thread error.
So in order to avoid that I make some delegates for a few control(just a test) and works OK.
But the WinForm have more than 100 controls and I was wondering if there's some way to do it with less code.
Making a search I found this two questions.
Multi-threaded WPF Application: Dispatcher Invoke. A more efficient way?
Change WPF controls from a non-main thread using Dispatcher.Invoke
On the first link they talk about use Dispacher, something like
Public Shared Sub UiInvoke(a As Action)
Application.Current.Dispatcher.Invoke(a)
End Sub
I wrote that code in my Form but the IDE(VS2010/NET4.0) says that "Current" is not a member.
I think I'm missing something, I never used before or do something similar as Dispacher, I usually use Delegates.
What I'm doing wrong? There's another way to control many controls with one Delegate?
I just need to read or write the text property.
You can use anonymous Subs:
Me.Invoke(
Sub()
' Update controls here
End Sub)
Related
I am working on a wpf project...I've faced similar issues in the past,but didn't get the answer to it and that's why i'm asking again :)
My WPF app has 2 windows,one works as a splash screen and the 2nd one is just a basic window with a canvas.The splash screen has a BackGroundWorker.Now,i have this code :
Dim h2 as new Window2
For Each fi As FileInfo In New DirectoryInfo(Application.StartupPath +
"\data\img\em_sml").GetFiles()
h2.canvas.children.add(new Button)
Now, my question is how do i use this code in the backgroundworker in Window1 ?? I tried this :
Dim method as Delegate
Private Sub BgWorker_DoWork(sender As Object, e As DoWorkEventArgs) Handles BgWorker.DoWork
If h2.Dispatcher.CheckAccess Then
For Each fi As FileInfo In New DirectoryInfo(Application.StartupPath +
"\data\img\em_sml").GetFiles()
h2.canvas.children.add(new Button)
Else
h2.Dispatcher.BeginInvoke(DispatcherPriority.Normal, method)
For Each fi As FileInfo In New DirectoryInfo(Application.StartupPath +
"\data\img\em_sml").GetFiles()
h2.canvas.children.add(new Button)
End if
A few things i'ld like to clear here :
• I got the sample code from another SO post(i converted it from c#)
• Dim method as Delegate,i don't really know how to use a delegate function
• The code above returns some exceptions like Parameter name:Method , Value can not be null(I know what this means but as i said,i don't know how i can use the delegate function in such a case)
Any help would be appreciated
The code you're using is trying to invoke to the UI thread via Dispatcher.BeginInvoke(). This is the way to do it in threads, but not in a BackgroundWorker. In a BGW you're supposed to call the ReportProgress() method combined with subscribing to the ProgressChanged event.
However for what you're trying to do, this is not good at all...
For starters: You should NEVER create controls in a background thread! All work related to the UI (User Interface) must always, always, always be done on the UI thread ONLY.
Secondly: There isn't really a good reason for trying to do this in a background thread. The way you're doing it now constantly updates the UI thread, causing it to lag/freeze anyway. What you could do to minimize the lag is to add the buttons in batches, but then as I said before you shouldn't be creating controls in a background thread at all.
Finally: The whole operation of iterating a few files and creating buttons for them really isn't very heavy. Unless you have thousands of files (in which case you should only display them in batches) this will not take that long to perform on the UI thread.
Conclusion: Skip the BackgroundWorker and run your code on the UI thread instead. If you have a really huge amount of files to load, store their paths in a list, only load them in batches and let the user decide when to load the next batch of files.
As I continue to adapt to the new world of wpf and mvvm I find myself with another problem which is proving to be a little vexing.
In windows forms if I needed a new form to take a parameter I simply passed the information that I needed to that form's constructor. As Winforms is essentially a code behind model anyway this wasn't an issue.
I now find myself with a MainWindow (controlled by MainWindowViewModel) and a Login (controlled by LoginViewModel) which is shown as a dialog first. I have sorted the issue of the missing dialog result and I can get the main window to show after closing the login dialog.
The login dialog is shown and responded to in the Application startup with the following code;
dim dlg As New Login
If dlg.ShowDialog() = True Then
Else
Current.Shutdown(0)
End If
and I use the use the following in the code behind of the login window to take care of showing the main window when the login dialog closes;
Public Sub New
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Application.Current.ShutdownMode =ShutdownMode.OnExplicitShutdown
End Sub
Protected Overrides Sub OnClosed(e As EventArgs)
MyBase.OnClosed(e)
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose
End Sub
Now the Mainwindow already knows that it's datacontext is MainWindoViewModel because I set that up when designing it with blend, so how do I pass any parameters that I want to pass from the LoginViewModel to the MainWindowViewModel?
I can read c# but would prefer vb if possible. I know that this is something that I will want to be doing often so if there is a generic way to do this then that's the way I'd like to go.
Thanks
Mvvm frameworks generally implement some kind of messaging system to allow data to be moved between viewmodels or sometimes between viewmodel and view.
You can look at the way messaging is handled in some of the more popular frameworks such as MvvmLight or MvvmCross. It's usually just like an event that uses a weak reference to prevent tight coupling, so you can register for a type of message and then from another viewmodel you publish a message to any registered listeners.
So you will either need to implement your own kind of messenger, or now might be a time to look at starting to use a third party framework, unless you have specific reasons for avoiding this.
I have this:
Shows a waiting animation to 'block' the UI while performs a loading operation in the background.
At the end of the loading I call a method that instances a User Control and displays some data by using Bindings (and ObservableCollection among others)
This User Control gets displayed and user can interact with it, however the ObservableCollection seems to be stuck in another thread as it doesn't allow to add new items to it.
I've tried to update the UI at the Completed event of a BackgroundWorker, using Dispatcher, using DispatchTimer... all of this displays the User Control, but the ObservableCollection stays of out reach for adding.
The code that tries to add items to the collection is inside the UserControl.
The exact error is: "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread"
This does not happen if I don't do the loading in the background.
Thank you for any workaround for this.
By the way, trying to add the items using Dispatcher doesn't work either.
In other words, what I would like to do is to create an object in the UI Thread while being in the background... I know this may sounds silly.
You may have to check which Dispatcher you are using? In your case you could be referring to two different dispatchers.
Also why not use thread safe observable collection?
Usually I will create the objects on my UI thread, then populate them with data obtained from a background thread.
For example,
void async LoadButton_Clicked(object sender, EventArgs e)
{
MyCollection = new ObservableCollection<SomeItem>();
// Can also use a BackgroundWorker
var collectionData = await GetCollectionData();
foreach(var item in collectionData)
{
MyCollection.Add(item);
}
}
I'm using C# 5.0 async and await keywords for asynchronous operations, but you can also use a BackgroundWorker that does your background work.
You can also use Dispatcher.BeginInvoke() for some lighter background work (such as copying data into MyCollection), although for heavy work I find it still locks up the UI so I prefer to use background threads.
It is not possible to modify the contents of an ObservableCollection on a separate thread if a view is bound to this collection, instead you can override ObservableCollection and provide support for it and use it across your application.
This sample contains exactly what you want - http://tomlev2.wordpress.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/
When it comes to threads and ui-elements one of the most important rules to follow which may safe you a lot of trouble in the long run is to keep ui-element instantiation on the ui-thread. Surely you can manage that. And if you need to change those objects from another thread you can use the Dispatcher.
(The threading model reference may also be of interest)
Thank you everyone for your help... a guy from MS visited the company (sorry for the commercial annotation) to do other things, I stoled him and show this behavior. In a matter of 2 minutes founds the source of the problem... which I'm not sure to really understand.
It happens that I'm using an ICollectionView to display a sorted/filtered version of my problematic ObservableCollection. I was creating this ICollectionView in the constructor of my class, so at the moment of deserialization it was created in another thread. He suggested to move this creation to a further time in code (when the related property gets read). This solved the problem.
However the ObservableCollection, created in that other thread, now lets me add new item. Not sure why, but now it works.
Sorry for being this late and thank you again.
I'm able to run 2 or more WPF windows on different thread.
The problem is that now my application in splitted in many windows.
What I really want is have a main window containg a grid in which every cell contains an element managed by a different thread.
Is it possible to create a UIElement/Component managed by a thread that is not the one which manage the parent/ containing window?
or
Is it possible to encapsulate a window that runs on a different thread in some frame/UIElement?
Thanks
Is it possible to use a MediaElement to project a window into a Panel?
"What I really want is have a main window containg a grid in which every cell contains an element managed by a different thread."
One way to go about this would be to create your elements normally in the cell. Create a regular class ViewModel that doesn't touch the UI but runs on it's own thread. This class is the brains behind what you're actually trying to DO with in your cells, not what you're trying to SHOW in your cells. This ViewModel class should implement INotifyPropertyChanged when it's data is has been updated. In your MainWindow.cs file you can set your cell elements' DataContext to these ViewModels. Lastly, in your XAML you can Bind things you're trying to show with the Properties in your ViewModel.
I know I breezed over a lot of details, but it's a starting point. Lots of help to be had around here if you need any.
It's not possible in WPF and even if it was it would have been a bad idea:
It's not possible in WPF because WPF element can only be used by the thread that created them, if you add a child element from another thread they wouldn't be able to communicate.
In pure Win32 it is possible - but it joins the two threads message queues so the threads are no longer independent (so even if you find a hack that makes it work with WPF it still doesn't help you)
Any thread that has UI and performs a long running task can hung the entire system - so it's impotent to never perform any long running task in a UI thread - instead run the long task in a background thread
because you have to keep the UI thread responsive -> it should never be busy for a noticeable length of time -> it can handle all your windows because it's not too busy.
e.g. in Winforms I'd write...
// on UI Thread
BackgroundWorker workerThread = new BackgroundWorker();
workerThread.DoWork += new DoWorkEventHandler(LoadChildren);
workerThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OnLoadChildrenCompleted);
while (workerThread.IsBusy)
{
Application.DoEvents();
}
In WPF what is the equivalent of Application.DoEvents in Winforms?
I have a property called Children in my ViewModel class. A HierarchicalDataTemplate has been setup to read Items from the Children property.
A TreeView displays the nodes. When the user expands a node, the children of the node are generated from the results of this property
public Node Children
{
get
{
// 1. time-consuming LINQ query to load children from a SQL DB
// 2. return results
}
}
So I'd like to run 1. on a background thread and wait for it to complete before returning the results... keeping the UI responsive.
Googling led me to this page which has uses DispatcherFrames to simulate the above method. But this seems to be too much work.. which hints at 'Am I doing this right?'
As I understand it, you've got this sort of flow:
Do some prep work (UI thread)
Do some background work (other thread)
Do some finishing work (UI thread)
You want to wait for the second bullet to finish before running the code in the third.
The easiest way to do that is make the second bullet's code call back into the UI thread (in the normal way) to trigger the third bullet to execute. If you really, really want to use local variables from the method, you could always use an anonymous method or lambda expression to create the delegate to pass to the background worker - but normally it would be cleaner to just have a "PostBackgroundWork" method or something like that.
EDIT: This wouldn't be nice for a property as you've shown in your edited question, but I'd refactor that as a request to fetch the children with a callback when it's completed. This avoids the whole mess of reentrancy, and makes it clearer what's actually going on.
Calling DoEvents on the UI thread in a loop like this is not recommended practice in WinForms or WPF.
If your application can't continue until this thread has finished its work, then why is it on another thread?
If some parts of your application can continue, then disable those bits that can't and reenable them when your completion callback is called. Let the rest of the system get on with its stuff. No need for the loop with DoEvents in it, this is not good practice.
Take a look at the community content on MSDN.
This is a good article on DoEvents.
In WPF what is the equivalent of Application.DoEvents in Winforms?
There is none built-in, but you can easily write your own. Indeed, WPF gives you more power around message processing than does Winforms. See my blog post on Dispatcher Frames here. It includes an example showing you how to simulate Application.DoEvents().