How to join this two things? How to create a widget (or any canvas) to draw in it from another thread?
You can edit one of the examples in the OpenTk source download here to have a GTK# window and a OpenTK Gamewindow in the same application.
Download OpenTK source here:
http://sourceforge.net/projects/opentk/files/latest/download
First, make sure that the examples work by building and running the examples. Try the OpenTK multithreading one specifically, it should give you two windows with spinning cubes.
Now, edit the example to spawn a gtk# window instead of a second openTK gamewindow.
Open the file opentk/Source/Examples/OpenTK/Test/Multithreading.cs
You will need to make a function to create a gtk window, like so
static void gtkWindow() {
Application.Init ();
var gtkform = new Gtk.Window("test");
var btn = new Gtk.Button("flip");
btn.Clicked += HandleBtnClicked;
gtkform.Add(btn);
gtkform.ShowAll();
Application.Run();
}
Now edit the main loop to launch this window, like so:
// launch threads
for (int i = 0; i < ThreadCount; i++)
{
if (i == 0) {
Thread t = new Thread(RunGame);
t.IsBackground = true;
t.Priority = ThreadPriority.BelowNormal;
t.Start();
threads.Add(t);
} else {
Thread t = new Thread(gtkWindow);
t.IsBackground = true;
t.Priority = ThreadPriority.BelowNormal;
t.Start();
threads.Add(t);
}
}
You will now have a Gtk# window and OpenTK game windows in the same application.
Related
I wrote a C# COM exposed class library. I call its exposed methods from third party software.
I wrote a WPF dialog to watch the log entries as the business logic is performed.
The WPF dialog is started on a separate thread, prior to starting the actual method generating log entries. This is working pretty good.
The issue I have is that if I close the logwatcher window before to the main method DoTheActualWorkAsync() is finished, the DoTheActualWorkAsync() method stops running and the methods seems deadlocked.
Any ideas how to solve this? Why does closing a WPF window in a separate thread freeze the main thread?
This is my code:
// Exposed Method
public void DoALotOfWorkWrapper()
{
// Start WPF LogWatchWindow on new thread
var logWatcherThread = this.GetLogWatcherThread();
// Peform a lot of business logic, block for procedure to be ready
DoTheActualWorkAsync().Wait();
// Join logwatcher thread so method doesn't finish before window is closed
WaitForLogWatcherThread(logWatcherThread);
}
private Thread GetLogWatcherThread()
{
var signalEvent = new ManualResetEvent(false);
var logWatchWindowThread = new Thread(() =>
{
var vm = new LogWatchWindowViewModel();
var win = new LogWatchWindow
{
DataContext = vm
};
win.Show();
vm.HookupLog4NetEvents(win);
win.Closed += (sender2, e2) =>
{
vm.DetachLog4NetEvents();
win.Dispatcher.InvokeShutdown();
};
signalEvent.Set();
Dispatcher.Run();
});
logWatchWindowThread.SetApartmentState(ApartmentState.STA);
logWatchWindowThread.Start();
// Block to wait for Window and its viewmodel to be ready for displaying new log4net entries
signalEvent.WaitOne();
signalEvent.Reset();
return logWatchWindowThread;
}
private static void WaitForLogWatcherThread(Thread logWatcherThread)
{
logWatcherThread.Join();
}
I still have a problem with the splash screen. I don't want to use the property SC.TopMost=true.
Now my application scenario is as follows:
in progeram.cs:
[STAThread]
static void Main()
{
new SplashScreen(_tempAL);// where _tempAL is an arrayList
Application.Run(new Form1(_tempAL));
}
in SplashScreen class:
public SplashScreen(ArrayList _Data)
{
DisplaySplash()
}
private void DisplaySplash()
{
this.Show();
this.TopMost = true;
this.CenterToScreen();
this.SetTopLevel(true);
_allServerNarrators = new string[10];
for (int i = 0; i < _allServerNarrators.Length; i++)
_allServerNarrators[i] = null;
GetFromServer();
this.Hide();
_serverData = new ArrayList();
_thisData.Add(_allServerNarrators);
_thisData.Add(_serverNarrators);
}
private void GetFromServer()
{
_serverNarrators = new ArrayList();
string _file = "Suras.serverNar";
if (!Directory.Exists("c:\\ASGAQuraan"))
Directory.CreateDirectory("c:\\ASGAQuraan");
while (counter < 4 && _serverFiles == null)
{
if (Download("c:\\ASGAQuraan", _ftpServerIP, _file))
{
StreamReader _strReader = new StreamReader
("c:\\ASGAQuraan\\"+_file,System.Text.Encoding.Default);
string _line = _strReader.ReadLine();
string _word;
while (true)
{
while (_line != null)
{
_word = _line.Substring(0, _line.IndexOf("*"));
int _narId = Convert.ToInt32(_word);
_line = _line.Substring(2);
int k = 0;
_serverNarratorNode = new ArrayList();
while (true)
{
int ind = _line.IndexOf("*");
if (ind > 0 && ind < _line.Length)
{
string str = _line.Substring(0, (ind));
if (k == 0)
{
_allServerNarrators[_narId] = str;
_serverNarratorNode.Add(str);
}
else
{
_serverNarratorNode.Add(str);
}
_line = _line.Substring(ind + 1);
k++;
}
else
{
_line = null;
break;
}
}
_serverNarrators.Add(_serverNarratorNode);
_serverFiles = "added";
}
_line = _strReader.ReadLine();
if (_line == null)
{
break;
}
}
}
else
counter++;
}
}
What I want is something in the splash screen class which waits until the thread finishes.
For more details, please tell me what I need to tell you.
Same question, same answer:
The .NET framework has excellent built-in support for splash screens. Start a new WF project, Project + Add Reference, select Microsoft.VisualBasic. Add a new form, call it frmSplash. Open Project.cs and make it look like this:
using System;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;
namespace WindowsFormsApplication1 {
static class Program {
[STAThread]
static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
new MyApp().Run(args);
}
}
class MyApp : WindowsFormsApplicationBase {
protected override void OnCreateSplashScreen() {
this.SplashScreen = new frmSplash();
}
protected override void OnCreateMainForm() {
// Do your time consuming stuff here...
//...
System.Threading.Thread.Sleep(3000);
// Then create the main form, the splash screen will close automatically
this.MainForm = new Form1();
}
}
}
You've entered dangerous territory by creating UI prior to your call to Application.Run(). Application.Run is essentially your program's message pump. By displaying the UI before you start the application's message pump, you make typical UI interaction effectively impossible on the premature UI. For a splash screen this may not seem relevant, but it will matter if (e.g.) there's a request to make the splash screen disappear if it's clicked on, or you want to use a BackgroundWorker.
These can be worked around by creating a message pump in your splash screen (by making it modal via a call to ShowDialog() instead of Show()), but that's treating the symptom when treating the problem really isn't that difficult.
I'd strongly encourage nobugz's answer in this case. The framework provides the support you need. While features in the Microsoft.VisualBasic namespace aren't always very discoverable to C# programmers, they can be a real timesaver and lifesaver for cases like this.
Good luck!
Following across 2 threads is a bit confusing, but I'm going to take a stab and say this...
I don't fully understand your design here, but if the issue is that when you launch a second application the splash screen form turns white... It's most likely due to the fact that splash screen is busy executing all of that code in GetFromServer(). So busy that it has no time to re-paint itself.
To remedy this problem I would suggest that you use the BackGroundWorker component to execute the GetFromServer method. This will run that method in a separate thread and leave the form's thread free to re-paint itself.
Unfortunately I don't have enough reputation to comment on someones answer yet. :( This is meant to be the answer to Colonel Panics comment on Hans Passants answer.
His problem was that a MessageBox shown from new FormMain(args)will be shown behind the splash screen. The key is to invoke the MessageBox from the thread the splash screen runs in:
splashScreen.Invoke(new Action(() => {
MessageBox.Show(splashScreen, "the message");
}));
Where splashScreen is a reference to the splash screen object that has been created in OnCreateSplashScreen and obviously has to be given to the new Form1 object.
You really should give more details about your problem. I could be completely wrong, but I'm going to take a shot in the dark. From what I'm imagining is going on and you want, you want the splash screen to show, do some processing in another thread, then the splash screen to go away when finished.
To do this, you're going to want to move the GetFromServer() call to a BackgroundWorker. Then move the
this.Hide();
_serverData = new ArrayList();
_thisData.Add(_allServerNarrators);
_thisData.Add(_serverNarrators);
code to the BackgroundWorker_RunWorkerCompleted event handler.
To use the BackgroundWorker:
1) Initialize the BackGroundWorker
BackgroundWorker myWorker = new BackgroundWorker();
2) Add event handlers
myWorker.DoWork += new DoWorkEventHandler(myWorker_DoWork);
//put the work you want done in this one
myWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(myWorker_RunWorkerCompleted);
//this gets fired when the work is finished
3) Add code to the event handlers.
4) Call myWorker.RunWorkerAsync() to start working.
As a separate note, you don't seem to be doing anything with the ArrayList that you're passing to the splash screen's constructor. Is this intended?
We are trying to use Testing Automation FX for automating the basic workflow and testing in our application. The record function otherwise works flawlessly except in one specific case where the Windows Form in question is started from a different thread.
I noticed that the map generates an indistinguishable parent for the form in our application. The form is of type UIWindow and the following is the designer code for same.
this.uiWindow2.Comment = null;
this.uiWindow2.MatchedIndex = 0;
this.uiWindow2.MsaaName = null;
this.uiWindow2.MsaaRole = System.Windows.Forms.AccessibleRole.Client;
this.uiWindow2.Name = "uiWindow2";
this.uiWindow2.ObjectImage = ((System.Drawing.Bitmap)(resources.GetObject("uiWindow2.ObjectImage")));
this.uiWindow2.OwnedWindow = true;
this.uiWindow2.Parent = this.Application;
this.uiWindow2.UIObjectType = TestAutomationFX.UI.UIObjectTypes.Window;
this.uiWindow2.UseCoordinatesOnClick = true;
this.uiWindow2.WindowClass = "";
Since we don't have a parent window to our form, the only logical conclusion I could come up with was that windows was creating a wrapper around the form when it was started from a different thread.
What this does is that when tafx runs the test cases, it sometimes fails to find uiWindow2 or hooks onto some other window thus failing the test. This is totally random. I would like to know if there is a possible solution for this situation?
We are spawning window in a different thread using the following code snippet
private void LaunchBlottFormNewOnNewThread()
{
Form blottForm = new BlottForm();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(blotterForm);
}
private void StartBlott()
{
Thread blottThread = new Thread(new ThreadStart(LaunchBlottFormNewOnNewThread));
blottThread.SetApartmentState(ApartmentState.STA);
blottThread.Name = "BlottThread";
blottThread.IsBackground = true;
blottThread.Start();
}
What is the easiest way to fire a splash screen (Which disappears on its own) in a C# / .NET 2.0 winforms app? It looks like the VisualBasic assembly (which can be called from C# nonetheless) has a way to do this, but are there any simple examples?
Thanks
There's a detailed tutorial on Code Project which puts the splash screen on its own thread so the main app can get on with loading up.
Easiest way would be to create a form and allow it to kill itself after some time it is shown. But, things gets more complicated if you want this form to be able to display some application loading progress while application is initializing, and disappear for example 3 seconds after application is really ready for use.
Idea would include putting the splash screen on completely different thread from the main application. It's thread function should go like that:
static void ThreadFunc()
{
_splash = new Splash();
_splash.Show();
while (!_shouldClose)
{
Application.DoEvents();
Thread.Sleep(100);
if (new Random().Next(1000) < 10)
{
_splash.Invoke(new MethodInvoker(_splash.RandomizeText));
}
}
for (int n = 0; n < 18; n++)
{
Application.DoEvents();
Thread.Sleep(60);
}
if (_splash != null)
{
_splash.Close();
_splash = null;
}
}
Then, you can use this to show and hide it:
static public void ShowSplash()
{
_shouldClose = false;
Thread t = new Thread(ThreadFunc);
t.Priority = ThreadPriority.Lowest;
t.Start();
}
internal static void RemoveSplash()
{
_shouldClose = true;
}
I still have a problem with the splash screen. I don't want to use the property SC.TopMost=true.
Now my application scenario is as follows:
in progeram.cs:
[STAThread]
static void Main()
{
new SplashScreen(_tempAL);// where _tempAL is an arrayList
Application.Run(new Form1(_tempAL));
}
in SplashScreen class:
public SplashScreen(ArrayList _Data)
{
DisplaySplash()
}
private void DisplaySplash()
{
this.Show();
this.TopMost = true;
this.CenterToScreen();
this.SetTopLevel(true);
_allServerNarrators = new string[10];
for (int i = 0; i < _allServerNarrators.Length; i++)
_allServerNarrators[i] = null;
GetFromServer();
this.Hide();
_serverData = new ArrayList();
_thisData.Add(_allServerNarrators);
_thisData.Add(_serverNarrators);
}
private void GetFromServer()
{
_serverNarrators = new ArrayList();
string _file = "Suras.serverNar";
if (!Directory.Exists("c:\\ASGAQuraan"))
Directory.CreateDirectory("c:\\ASGAQuraan");
while (counter < 4 && _serverFiles == null)
{
if (Download("c:\\ASGAQuraan", _ftpServerIP, _file))
{
StreamReader _strReader = new StreamReader
("c:\\ASGAQuraan\\"+_file,System.Text.Encoding.Default);
string _line = _strReader.ReadLine();
string _word;
while (true)
{
while (_line != null)
{
_word = _line.Substring(0, _line.IndexOf("*"));
int _narId = Convert.ToInt32(_word);
_line = _line.Substring(2);
int k = 0;
_serverNarratorNode = new ArrayList();
while (true)
{
int ind = _line.IndexOf("*");
if (ind > 0 && ind < _line.Length)
{
string str = _line.Substring(0, (ind));
if (k == 0)
{
_allServerNarrators[_narId] = str;
_serverNarratorNode.Add(str);
}
else
{
_serverNarratorNode.Add(str);
}
_line = _line.Substring(ind + 1);
k++;
}
else
{
_line = null;
break;
}
}
_serverNarrators.Add(_serverNarratorNode);
_serverFiles = "added";
}
_line = _strReader.ReadLine();
if (_line == null)
{
break;
}
}
}
else
counter++;
}
}
What I want is something in the splash screen class which waits until the thread finishes.
For more details, please tell me what I need to tell you.
Same question, same answer:
The .NET framework has excellent built-in support for splash screens. Start a new WF project, Project + Add Reference, select Microsoft.VisualBasic. Add a new form, call it frmSplash. Open Project.cs and make it look like this:
using System;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;
namespace WindowsFormsApplication1 {
static class Program {
[STAThread]
static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
new MyApp().Run(args);
}
}
class MyApp : WindowsFormsApplicationBase {
protected override void OnCreateSplashScreen() {
this.SplashScreen = new frmSplash();
}
protected override void OnCreateMainForm() {
// Do your time consuming stuff here...
//...
System.Threading.Thread.Sleep(3000);
// Then create the main form, the splash screen will close automatically
this.MainForm = new Form1();
}
}
}
You've entered dangerous territory by creating UI prior to your call to Application.Run(). Application.Run is essentially your program's message pump. By displaying the UI before you start the application's message pump, you make typical UI interaction effectively impossible on the premature UI. For a splash screen this may not seem relevant, but it will matter if (e.g.) there's a request to make the splash screen disappear if it's clicked on, or you want to use a BackgroundWorker.
These can be worked around by creating a message pump in your splash screen (by making it modal via a call to ShowDialog() instead of Show()), but that's treating the symptom when treating the problem really isn't that difficult.
I'd strongly encourage nobugz's answer in this case. The framework provides the support you need. While features in the Microsoft.VisualBasic namespace aren't always very discoverable to C# programmers, they can be a real timesaver and lifesaver for cases like this.
Good luck!
Following across 2 threads is a bit confusing, but I'm going to take a stab and say this...
I don't fully understand your design here, but if the issue is that when you launch a second application the splash screen form turns white... It's most likely due to the fact that splash screen is busy executing all of that code in GetFromServer(). So busy that it has no time to re-paint itself.
To remedy this problem I would suggest that you use the BackGroundWorker component to execute the GetFromServer method. This will run that method in a separate thread and leave the form's thread free to re-paint itself.
Unfortunately I don't have enough reputation to comment on someones answer yet. :( This is meant to be the answer to Colonel Panics comment on Hans Passants answer.
His problem was that a MessageBox shown from new FormMain(args)will be shown behind the splash screen. The key is to invoke the MessageBox from the thread the splash screen runs in:
splashScreen.Invoke(new Action(() => {
MessageBox.Show(splashScreen, "the message");
}));
Where splashScreen is a reference to the splash screen object that has been created in OnCreateSplashScreen and obviously has to be given to the new Form1 object.
You really should give more details about your problem. I could be completely wrong, but I'm going to take a shot in the dark. From what I'm imagining is going on and you want, you want the splash screen to show, do some processing in another thread, then the splash screen to go away when finished.
To do this, you're going to want to move the GetFromServer() call to a BackgroundWorker. Then move the
this.Hide();
_serverData = new ArrayList();
_thisData.Add(_allServerNarrators);
_thisData.Add(_serverNarrators);
code to the BackgroundWorker_RunWorkerCompleted event handler.
To use the BackgroundWorker:
1) Initialize the BackGroundWorker
BackgroundWorker myWorker = new BackgroundWorker();
2) Add event handlers
myWorker.DoWork += new DoWorkEventHandler(myWorker_DoWork);
//put the work you want done in this one
myWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(myWorker_RunWorkerCompleted);
//this gets fired when the work is finished
3) Add code to the event handlers.
4) Call myWorker.RunWorkerAsync() to start working.
As a separate note, you don't seem to be doing anything with the ArrayList that you're passing to the splash screen's constructor. Is this intended?