Closing XNA from Windows Forms - winforms

I have a game running in XNA.
In order to create the menus and dialogs I am using windows forms.
My main issue - however, is with my 'Game Over' dialog.
When you die, a message appears asking if you want to try again. When you do - it opens up another instance of xna (so you have two running).
When you select 'Try Again' I would like the first to close and to open a second one.
XNA Game1.cs
GameOver gameover = new GameOver(level, levelManager, kills);
gameover.ShowDialog();
this.Exit();
'GameOver' is the name of the windows form that displays the game over stats.
(This takes the level that the user is on and starts the game at that level)
GameOver.cs (Windows form)
private void button1_Click(object sender, EventArgs e)
{
Visible = false;
Thread thread = new Thread(() =>
{
Game1 game = new Game1(level);
game.Run();
});
thread.Start();
thread.Join();
}
Any help is much appreciated.

i'll sum up the whole thing:
You need to write a reset method that will reset the game window. you can do it by taking all the code in the contractor and put them in another method and call that method in the constructor and in the reset method. make sure you don't leave anything off the reset. any member or connection that need to be initialized.
you can also open a new window and close the current one, but that's not the right way of doing things

Related

Closing the New Window on a new Thread (WPF)

Hi I had posted a question along these lines recently but this is now a little more specific to my requirements. So, I have an Application where the user needs to log in. The log in process can take some time so I decided to put up a little animated GIF to show it is doing something. Sounds simple...!!??
I noticed soon that the login process was freezing the animation so I thought, I will put the login process on its own thread. I had countless instances of it referencing objects on the UI Thread so thought I would try the other way round and have the Image display on a new thread. Same issue - so I decided to create a new window containing the image, format it accordingly and display this as a new thread! Simple! That (bit) worked... I click to login, animation appears and disappears onces login is complete. So the Thread variable is set as global one:
Friend g_thLoading As Thread
And when the Login button is clicked I have the following:
g_thLoading = New Thread(AddressOf LoginSplashScreen)
g_thLoading.SetApartmentState(ApartmentState.STA)
g_thLoading.IsBackground = True
g_thLoading.Name = "LoginThread"
g_thLoading.Start()
VerifyLogin() 'Process that takes a while...
g_thLoading.Abort()
Then the method that is called in the new thread:
Sub LoginSplashScreen()
Dim SplashScreenWin As New SplashScreen()
Try
SplashScreenWin.ShowDialog()
System.Windows.Threading.Dispatcher.Run()
Catch ex As Exception
SplashScreenWin.Close()
SplashScreenWin = Nothing
End Try
End Sub
This works - but not if I have to click the button more than once. However If (for example) the user enters the wrong credentials, clicks login (the above processes and completes) they are prompted to re-enter - click the login button again... but this time, the window doesnt display (but oddly does appear in the Task Bar)... Then the application is forced to close (nothing in debug on why that is).
I am confident that the Dialogue Window is closing correctly after the first instance as i) it is no longer in the Task Bar and secondly I have put some checks on the Windows Close event. I am fairly confident that the created Thread is closed after the first instance as I can see it drop off from the Thread Window in Visual Studio... So - I am at a total loss. I have also tried the Join function on the thread but this just hangs the process before it gets to g_thLoading.Abort()
I am open to any advice on how I can go about achieving my end goal... whether it is expanding on what I have done here or another suggestion altogether. I have messed around with the Background Worker but not had much more luck there.
Use the BackgroundWorker class to implement your long running processes. The class allows you to specify code that will run on a background thread (in the DoWork event handler), code that will run during "updates" on the thread that created the BackgroundWorker in the ProgressChanged event handler, and code that will run when the process completes, again on the thread that created the BackgroundWorker in the RunWorkerCompleted event handler.
Using it goes something like this:
private class LoginParameters {
public string Name { get; set; }
public string Password { get; set; }
// Any other properties needed
}
// Make this a property of your form.
BackgroundWorker LoginWorker { get; set; }
// Somewhere in your UI code after the user clicks the "login button"
LoginWorker = new BackgroundWorker();
LoginWorker.WorkerReportsProgress = true;
LoginWorker.WorkerSupportsCancellation = true; // Can set to false if you don't allow the operation to be cancelled.
LoginWorker.DoWorker += DoLogin;
LoginWorker.ProgressChanged += ReportProgress;
LoginWorker.RunWorkerCompleted += LoginFinished;
LoginParameters login = new LoginParameters {
// Code to initialize everything here
};
LoginWorker.RunWorkerAsync(login);
// Put this in the click event handler for the Cancel button, if you have one
if ( LoginWorker != null )
LoginWorker.CancelAsync();
private void DoLogin(object sender, DoWorkEventArgs e) {
BackgroundWorker worker = (BackgroundWorker) sender;
LoginParameters login = (LoginParameters) e.Argument;
// Your logic to process the login goes here. It should periodically do the following to check to see if the user clicked the cancel button:
if ( worker.CancellationPending ) {
e.Cancel = true;
return;
}
// When you want to update the UI, do this:
worker.ReportProgress( percentComplete, objectWithOtherDataToWriteToTheUI );
// When you're done, just return.
}
private void ReportProgress(object sender, ProgressChangedEventArgs e) {
// Your code to extract the data you need to update the display from the arguments & to then update the display goes here. Remember, this runs on the UI thread
}
private void LoginFinished( object sender, RunWorkerCompletedEventArgs e ) {
if (e.Cancelled == true)
// Your code to inform the user of the cancellation here
else if (e.Error != null)
// All unhadgled exceptions throws by the DoWork event handler end up here
// Your code to inform the user of the error here
else {
// Your code to inform the user of the success goes here.
// Remember, this runs on the UI thread.
// I recommend you set the form BackgroundWorker property to null after its finished, as you can't reuse it after its finished.
LoginWorker = null;
}
}
Sorry this is in C# if you're looking for VB.NET, but it shouldn't be hard to translate.

Has any one out there in the field used Dispatcher.Yield?

In "Lucian Wischik - Async Part 2 -- deep dive into the new language feature of VB/C#" for NDC 2012, the recommended use of Dispatcher.Yield is introduced to me. Does anyone out there have examples (and explanations) of how (and why) this call is used in the wild?
Well for example if you have a long running task but you still need to update your UI you can use Yield.
Yield gives you the ability to leave the current thread context and allow other code to run in the underlying context.
public async void MyButton_Click(object sender, RoutedEventArgs e)
{
for( int i=0; i < 10000; i++)
{
ProcessSomeStuff(i);
// await the Yield to ensure all waiting messages
// are processed before continuing
await Task.Yield();
}
}
In the example above you can process stuff async but calling Yield will allow events on the UI thread to execute also,
I love to use "await Dispatcher.Yield()" whenever I develop WPF applications.
It is easy to use and barely makes any problems in most cases.
The most useful case using Dispatcher.Yield() is when there is a small time of lag in a operation, which can not be bypassed by "Task", "Thread" etc.
For example, let's say that there is a command button which opens a new window or tab.
private void aButton_Click(object sender, RoutedEventArgs e)
{
// Do some ui stuff
// You can not use Task here at large.
}
What happens here is the whole application stopping, and the button UI remains pressed until the new window or tab opens. It is bad user experience and makes the apllicaiton seem so much slow.
and here is a code with a trick.
private async void aButton_Click(object sender, RoutedEventArgs e)
{
await Dispatcher.Yield();
// Do some ui stuff
}
In here, your code works differently. Dispathcer will process other ui operations first before getting into the job. The button pressed motion will be released first and then a new window will come.
Application stopping will be same, but to users the application will be much smooth and fast. So it is usually a good idea to adopt Dispatcher.Yield() in your application.
In addition, Task.Yield() is different from Dispatcher.Yield(). Try two options and see the results.

Reliably Restart a Single-Instance WPF application

I would like to have my current application close and restart. I have seen many posts on this, however none of these seem to work for me.
I have tried
System.Diagnostics.Process.Start(System.Windows.Application.ResourceAssembly.Location);
System.Windows.Application.Current.Shutdown();
However this only restarts the application once. If I press my 'restart' button again (on the already restarted app), it only closes.
I have also tried launching a new System.Diagnostics.Process and closing the current process, but again this does not restart, it simply closes.
How can I restart my current WPF application?
You could create another application which you start when exiting your app and which in return does start your application again. Kind of like how a patcher would work, only without patching anything. On the plus side you could have a loop in that "restart-application" which checks all running processes for your main application process and only tries to re-start it once it does not appear in the process any longer - and you got the bare bones for a patcher also :) Whilst you do not seem to have a problem with restarting your application due to it still being in the processlist - it is the way I would go for when doing it in a production environment as this gives you the most control IMHO.
Edit:
That part in the button event handler (or wherever you want to restart your app with) of your main app (Process2BRestarted.exe in my case):
private void cmdRestart_Click(object sender, EventArgs e)
{
var info = new ProcessStartInfo();
info.FileName = "ProcessReStarter";
info.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(info);
Application.Exit();
}
This should go into your utility/restarter application (ProcessReStarter.exe over here):
private void MainForm_Load(object sender, EventArgs e)
{
// wait for main application process to end
// really should implement some kind of error-checking/timer here also
while (Process.GetProcessesByName("Process2BRestarted").Count() > 0) { }
// ok, process should not be running any longer, restart it
Process.Start("Process2BRestarted");
// and exit the utility app
Application.Exit();
}
Clicking the restart button will now create a new process ProcessReStarter.exe, which will iterate through the process list of all running processes - checking whether Process2BRestarted is still running. If the process does not appear in the list (any longer) it will now start a new Process2BRestarted.exe process and exit itself.

MessageBox.Show early in App startup causes app to terminate

As part of my App's startup procedure, it checks data integrity, and if it finds a problem it pops up a message to the user telling them that it might take a while to repair things.
I'm showing the message using MessageBox.Show. Because the data check is done from a worker thread, I'm switching over to the UI thread to make that call, and then setting a ManualResetEvent to tell the worker thread when the user has acknowledged the message.
I kick off the data check/load very early in the app's lifecycle from the constructor in the main Application class, by spinning off a worker thread (using the ThreadPool).
When I run with the debugger, and the message is displayed, the app just waits for input. When I run without the debugger, the app terminates after displaying the dialog for 10 seconds.
That 10 seconds is a big clue - it tells me that the OS thinks the app took too long to initialize (the OS kills apps that take too long to start up).
I think that my MessageBox.Show is blocking the UI thread before the App.RootFrameNavigating has a chance to be invoked.
My questions:
Does my diagnosis sound right?
I'd prefer to kick off my data load early, because it is almost entirely IO, except for this Message Box, and the sooner I can get my Model loaded, the better, but do you normally delay your data load until later in the app lifecycle?
Any other ideas/suggestions? I can't guarantee which page will be the start page, because the app could be resuming to any page. I'm also thinking of having the MessageBox.Show delay itself until the app has initialized, perhaps polling away for a flag set by App.RootFrameNavigating - does that make sense?
I think your problem is a result of kicking off the worker thread in the Application constructor. You should use the appropriate life-cycle event, in this case: PhoneApplicationService.Activated Event
So, the solution I've come up with is to still kick off the data load in a worker-thread from the Application's constructor, but in my PhoneService's class ShowDialog method that I invoke to invoke MessageBox.Show, I check to see if the initial navigation has occurred:
private readonly ManualResetEvent _appInitialized = new ManualResetEvent(false);
public void AppInitialized()
{
_appInitialized.Set();
}
public void ShowDialog(string caption, string text, Action<MessageBoxResult> callback, MessageBoxButton button = MessageBoxButton.OKCancel)
{
_appInitialized.WaitOne();
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
var result = MessageBox.Show(text, caption, button);
if (callback != null)
{
callback(result);
}
});
}
Then in my Application class:
private bool _firstNavigate = true;
private void RootFrameNavigating(object sender, NavigatingCancelEventArgs e)
{
if (_firstNavigate)
{
_firstNavigate = false;
var navigationService = (NavigationService) sender;
navigationService.Navigated += NavigationServiceNavigated;
}
....
private void NavigationServiceNavigated(object sender, NavigationEventArgs e)
{
var navigationService = (NavigationService)sender;
navigationService.Navigated -= NavigationServiceNavigated;
PhoneServices.Current.AppInitialized();
}
Anyone see any issues with this approach? Anyone come up with a better way?

Control.IsAccessible

I need to check if a c# WinForm Window (FORM Class) has been initialized and waiting for user events. But I could not find out how to manage that.
Therefore I had the idea to set the Control.IsAccessible Flag of the Form to true, within the OnLoad Event of the Windows Form.
My question is now, what is the Control.IsAccessible Flag origin intended for? Or is there an other solution to check if the Winform is initialized.
Thanks for your help
I do not know what IsAccessible is intended for but for the check you are doing you want Created
if(myForm.Created)
{
//Do stuff
}
I had a whole bunch of problems with it, here is one of my old question on SO that helped me out a lot with it.
Control.IsAccessible just means the control is visible to accessibility applications.
You can check myForm.Created to see if the window exists.
You can also register an event handler for the Application.Idle event, which occurs when the application has finished initializing and is ready to begin processing windows messages.
Here is a common usage:
public int Main(string[] args)
{
Application.Idle += WaitUntilInitialized;
}
private void WaitUntilInitialized(object source, EventArgs e)
{
// Avoid processing this method twice
Application.Idle -= WaitUntilInitialized;
// At this point, the UI is visible and waiting for user input.
// Begin work here.
}

Resources