I am working with a team on an application that has a report generator and then a report viewer. At the moment we have not been able to open up the report viewer in a separate thread. These two applications should be independent once opened, and if one is closed it should not effect the other.
The Report Viewer has one UI set of files and the main UI has another set. My question is how can we open up the Viewer UI in a separate thread once the main UI is back in it's "idle" state (not hidden, just not processing anything)?
Any short code snippets would be helpful, at this point I am completely lost on how to open up the viewer in a new thread...
Perhaps something along the lines of this:
private void MethodName {
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(ThreadProc));
t.Start();
}
and the ThreadProc code will look like this:
public static void ThreadProc()
{
Application.Run(new Application_Name());
}
MethodName (for me) is actually
serverToolStripMenuItem_Click(object sender, EventArgs e)
As it is an event-driven code.
Application_Name will be the form or Application you want to run.
Hope this helps.
Related
this might sound dumb, I am new in WPF and in my application I open an additional window using Task.Factory.StartNew because the function runs in separate thread. After the debugging stops, this window stays open but without any images, only the text/clicking/animations are shown, but through this I can open other windows, as if the software was still running, which actually does through a process called "WPFSurface.exe", I assume it's some sort of debugging or just a bug but I can't find any information on google or stackoverflow. Here's the code that I use to create the window:
Task.Factory.StartNew(() =>
{
System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
var controls = new ScreenControls(display);
controls.Show();
}));
});
I also close the application using this code (Yes it's binded correctly to Closed event)
private void MainWindow1_Closed(object sender, EventArgs e)
{
Application.Current.Shutdown();
}
Thank you for your time!
This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 7 years ago.
I'm trying to be able to access some controls in my main form from an other thread.
Let's consider this picture:
I want to Instantiate that control (It's a panel in my case) into the second thread.
My problem is that i have found a LOT of answers that just modifies a control (Set the text of a textbox for instance) and not be able to read/write it's properties like it's an object. (Delegates and stuff)
My current code: (Not working because i've created the panel in the other thread)
public partial class Main : Form
{
Graphics g;
Thread drawCanvasThread;
int pos = 0;
public Main()
{
InitializeComponent();
}
private void Main_Load(object sender, EventArgs e)
{
g = canvas.CreateGraphics();
drawCanvasThread = new Thread(() => HandleCanvas(canvas));
drawCanvasThread.Start();
}
private void HandleCanvas(Panel objCanvas)
{
Panel canvas = objCanvas;
Point mousePos;
while(true)
{
mousePos = canvas.PointToClient(Cursor.Position);
//UPDATE CANVAS
//DRAW CANVAS
Thread.Sleep(17); //1000 / 17 ~~= 60
}
}
private void Main_FormClosing(object sender, FormClosingEventArgs e)
{
drawCanvasThread.Abort();
}
}
PS: The thread "How to update the GUI from another thread in C#?" doesn't really answers my question, because i want to read the object properties, and not only write. Though it's a very interesting thread.
EDITED
You don't want to "instantiate" the control into the other thread.
There are some differences if you're working with Winforms (Win32) vs WPF (Windows Presentation Foundation). The Win32 UI libraries ("Winforms") are not thread-safe. You'll get unpredictable results, memory leaks and outright crashes if you allow any thread other than the main UI thread to directly fiddle with controls.
The WPF UI library is thread-safe, on the other hand, but there are still plenty of issues to be aware of and I'm not trying to address them all in this short (I hope) answer. ;-)
You really don't even want access across a thread boundary to a reference to a control instance. What you want to do is signal the UI thread to do what you want. There's more than one way to skin that cat, and you'll want to do some research. But the basic idea is that you set some kind of shared state from the other thread that tells the UI thread to take action, or raise an event that executes on the UI thread, and you initiate your interaction with the control from that event handler. Look up concepts like a message pump. But what you almost definitely don't want to do is fiddle with the Windows message loop to "subclass" the forms and controls.
Other useful tools include the BackgroundWorker class, and the newer Tasks library. You should pick and use one or the other, but not both simultaneously. The general model is that you'll launch a task thread from your UI thread, and when it returns it will raise an event in the UI thread from where you can do useful work with the controls on your forms.
I have a WPF project and from the main window i am creating and loading some bunch of user controls, there is some large data i am loading in background and then updating a built-in control throw the dispatcher, that works fine, the problem is that some of the user controls loads a lot of data, for example the very first thing i load in the main area of my main window, what i want is to put a loading label instead, load the main window as fast as possible so the user see this label and run in background the creation of that user control and when is done add it as a child of my main container area on my main window while i remove the loading label, if i follow the same philosophy i run into the same error like when i run a task and then try to update the window without using the dispatcher. i want to be able of create the user control asynchronous then update the main window.
Code:
User Control:
public partial class CustomUserControlGallery : UserControl
{
public CustomUserControlGallery()
{
InitializeComponent();
}
...
}
On the backend class of the main window:
public partial class MainWindow : Window
{
CustomUserControlGallery _customUserControlGallery;
public MainWindow()
{
InitializeComponent();
Task t = new Task({
//Can't use the _customUserControlGallery's Dispatcher because object is uninitialized and this.Dispatcher not working either.
_customUserControlGallery = new CustomUserControlGallery(); //Error Here.
_gridContainer.Dispatcher.Invoke(new Action(() => _gridContainer.Children.Add(_customUserControlGallery)));
_loadingLabel.Visbility = Visibility.Collapse;
});
t.Start();
}
...
}
I don't know how to handle this situation with the thread associated to the user control and the main thread.
Error:
{"The calling thread must be STA, because many UI components require this."}
You're doing this wrong. All controls must be created & operate on the UI Thread. That said, you can use the BackgroundWorker class to load the data.
You typically do this by disabling the control whose data is being loaded in the background or hiding it & displaying a progress indicator in its place. Then, you start your BackgroundWorker. That can communicate how far along it is using the ReportProgress method. Finally, when it's finished running, the RunWorkerCompleted event is fired, and you use that to either enable the control, or to hide the progress indicator & show the control.
Some quick & dirty (untested) code:
Place this in your Initialize() or control constructor:
private BackgroundWorker loadData = new BackgroundWorker();
loadData.DoWork += loadData_DoWork;
loadData.ProgressChanged += loadData_ProgressChanged; // Only do this if you are going to report progress
loadData.WorkerReportsProgress = true;
loadData.WorkerSupportsCancellation = false; // You can set this to true if you provide a Cancel button
loadData.RunWorkerCompleted += loadData_RunWorkerCompleted;
private void DoWork( object sender, DoWorkEventArgs e ) {
BackgroundWorker worker = sender as BackgroundWorker;
bool done = false;
while ( !done ) {
// If you want to check for cancellation, include this if statement
if ( worker.CancellationPending ) {
e.Cancel = true;
return;
}
// Your code to load the data goes here.
// If you wish to display progress updates, compute how far along you are and call ReportProgress here.
}
}
private void loadData_ProgressChanged( object sender, ProgressChangedEventArgs e ) {
// You code to report the progress goes here.
}
private void loadData_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e ) {
// Your code to do whatever is necessary to put the UI into the completed state goes here.
}
What you are essentially saying (I think) is that Your app becomes sluggish while your control renders a large amount of data.
This is a problem that needs to be solved via virtualisation. You cannot create a control on a background thread, have it render its data behind the scenes and then pop it into existence. You can create controls on separate dispatchers, but they cannot share the same visual and logical tree, so you will not be able to have one as a child of the other.
Virtualisation is what you need to focus on. Depending on the control you can use a variety of virtualisation settings. Try googleing the subject as there is a lot of information on how to achieve this effectively. Most likely you will want to use things like virtualizing stackpanels and container recycling.
You cannot create UI controls with different Dispatchers and use them with each other. It's just not allowed. What you want to do is on your Task you do the heavy lifting work without UI updates and when it is done you push it back to the Dispatcher to update the UI.
In your case, I wouldn't even use Dispatcher.Invoke. Since you are using Task, it has a TaskScheduler.FromCurrentSynchronizationContext() that you can pass in the constructor.
What is the purpose of instantiating controls in a different thread if you're just going to put it back to the Main dispatcher? It's not expensive to do that.
I have a project that I'm doing with
Microsoft VSTO (office 2013 excel)
I have certain things that make calls that take maybe 10 seconds to come back.
Ideally I would like to display an progress bar or some status... After a lot of searching I found an article that is titled:
How do I create a splash screen window for the VSTO applications?
http://www.datazx.cn/Fv7p5a/xw/oa2v/2q7xs6/mcccjfti-988m-f8r8-8d44-bstb4rfsi4xm23rsdfd.html
So I started creating this code in a form, but then I realize that I need to call it up within my methods and really attach events etc...
The article says to
"display a modal form on a background thread" What is the best way to do this?
I find it easier to use modal less form on main thread and so far haven't seen any problem with modal less approach. Something like code below
var splashWindow = new SplashWindow();
splashWindow.Show();
splashWindow.SetMessage("Starting please wait...");
DoSomeWork(splashWindow);
splashWindow.Close();
Following you will see a way I programmed a Splash Screen for Excel-VSTO in C#. My Excel file is enabled for macros (.xlsm). These are the steps:
Create your splash screen. Let's assume the name of the form is SplashScreen.
Go to the code of the object ThisWorkbook.cs
Check the code looks like:
public partial class ThisWorkbook
{
SplashScreen SC = new SplashScreen();
private async void ThisWorkbook_Startup(object sender, System.EventArgs e)
{
SC.Show();
await Task.Delay(3500);
SC.Close();
more code...
}
}
It is important that you notice that I added the word async to the subroutine.
private void ThisWorkbook_Startup(object sender, System.EventArgs e)
I hope this is very useful.
I have a options window and a window that displays color based on these options and Kinect data. So far everything's on one thread (as far as I know; I haven't done any threading).
Now, I'm adding an option to open a viewer window that will need to be updated with lowest possible latency. All this entails is creating a window and showing it:
viewer = new SkeletalViewer.MainWindow();
viewer.Show();
When this event fires, the color window stops displaying colors (i.e. the event that fires 30 times a second on the main thread stops firing), but the viewer is displayed perfectly. I want the viewer and the color window to both be updated.
From reading other questions, it sounds like the solution is to create the viewer on a new thread. I'm encountering a lot of problems with this, though.
This fires when I click the button to open the viewer:
private void launchViewerThread_Click(object sender, RoutedEventArgs e)
{
Thread viewerThread = new Thread(delegate()
{
viewer = new SkeletalViewer.MainWindow();
viewer.Dispatcher.Invoke(new Action(delegate()
{
viewer.Show();
}));
});
viewerThread.SetApartmentState(ApartmentState.STA); // needs to be STA or throws exception
viewerThread.Start();
}
Regardless of if I just call viewer.Show() or Invoke() it as above, the line throws an exception: Cannot use a DependencyObject that belongs to a different thread than its parent Freezable. Here's how I understand Invoke(): it accesses viewer's dispatcher, which knows what thread the object is running on, and can then call methods from that thread.
Should I be trying to put this viewer on a new thread? Is the problem even a question of threads? The user will not be interacting with the viewer.
Anyone know why this doesn't work? Thanks for the help.
You need to call Show() on the same thread that the window is created on - that's why you are getting the error. Then you also need to start a new Dispatcher instance to get the runtime to manage the window.
private void launchViewerThread_Click(object sender, RoutedEventArgs e)
{
Thread viewerThread = new Thread(delegate()
{
viewer = new SkeletalViewer.MainWindow();
viewer.Show();
System.Windows.Threading.Dispatcher.Run();
});
viewerThread.SetApartmentState(ApartmentState.STA); // needs to be STA or throws exception
viewerThread.Start();
}
See the Multiple Windows/Multiple Threads example at: http://msdn.microsoft.com/en-us/library/ms741870.aspx
So I was running into a similar issue where a new window failed to open on a new thread. The exception was "cannot use a dependencyobject that belongs to a different thread".
The issue ended up being that the window was using a global resource (Background brush). Once I froze the brush resource, the window loaded just fine.
I am not sure if this will solve your problem but can you try creating a thread proc (to open a viewer window) which is executed on a different thread and then have a dispatcher.beginInvoke to update the main window ,
Here is some code-
in the constructor register this
public MainWindow()
{
UpdateColorDelegate += UpdateColorMethod;
}
// delegate and event to update color on mainwindow
public delegate void UpdateColorDelegate(string colorname);
public event UpdateColorDelegate updateMainWindow;
// launches a thread to show viewer
private void launchViewerThread_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(this.ThreadProc);
t.Start();
}
// thread proc
public void ThreadProc()
{
// code for viewer window
...
// if you want to access any main window elements then just call DispatchToMainThread method
DispatchToUiThread(color);
}
//
private void DispatchToUiThread(string color)
{
if (updateMainWindow != null)
{
object[] param = new object[1] { color};
Dispatcher.BeginInvoke(updateMainWindow, param);
}
}
// update the mainwindow control's from this method
private void UpdateColorMethod(string colorName)
{
// change control or do whatever with main window controls
}
With this you can update the main window controls without freezing it, Let me know if you have any questions