WPF application without loading a window automatically issue - c#

I have set up a WPF application so that it does not load a window automatically by modifying the app.xaml.cs as shown below and by setting the build action to Page. I have also removed the starturl from the app.xaml. I have introduced a controller class from where the application starts as you can see in the code below and in the constructor for the controller I have created and opened a new window. I deleted the original MainWindow which was produced automatically when opening the application.
Anyway the problem is that after opening the new window that I have added in the controller I then close the window and the Application terminates? Why? I don't want the application to terminate and I don't understand why it is terminating on closing the window. Or to put it another way how can I introduce a window that doesn't cause the application to terminate on closing the window? Any help greatly appreciated.
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace UserTraining
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
App()
{
InitializeComponent();
}
[STAThread]
static void Main()
{
App app = new App();
UserTrainingController control = new UserTrainingController();
app.Run();
}
}
}
UserTrainingController class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace UserTraining
{
class UserTrainingController
{
public UserTrainingController()
{
IntroductionWindow w = new IntroductionWindow();
w.Show();
}
}
}

A WPF application has a ShutdownMode property, whose value is one of the members of the ShutdownMode enumeration. This has three possible values: OnExplicitShutdown, OnLastWindowClose and OnMainWindowClose.
The default is set to OnLastWindowClose which means that the application will shutdown when either the last window closes, or Application.Shutdown() is called. I suspect this is why your application terminates when your IntroductionWindow is closed.
What you need to do is set ShutdownMode to OnExplicitShutdown instead. That way, your application will only terminate when you explicitly call Application.Shutdown().
You can set the ShutdownMode property either in code or in your App.xaml file.

After UserTrainingController is called, you get the window shown in the screen with w.Show();. When you close the window, w.Show(); terminates and comes back to the caller, which is Main method. It's the last method on the stack.
But then you run another method, app.Run(), but it's terminates immediately, returning to Main method, which after app.Run() has nothing more to do, so it terminates as well.
Since it was last method on the stack, stack is empty, meaning that your application finished.
You need to have some parent window, from which you can open your custom window.
Generally, you have to prevent stack from emptying :)

Related

Is it possible to open my winForm through Revit API button I have created?

I would like to run my winForm through the button I have created in Revit API, but I am new in this field and I am a bit stuck at the moment.
Here in my Command.cs I am stating what button does after clicking on it. Where instead of displaying "Hello World" I would like it to open my winForm.
Is there any way how can I do that? Do I need to somehow link my winForm application to this one?
namespace SetElevation
{
[Transaction(TransactionMode.Manual)]
class Command : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
TaskDialog.Show("SetElevation", "Hello World!");
return Result.Succeeded;
}
}
}
Here is my winForm Program.cs application
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinFormTest
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
welcome to Revit addin development. You’re close.
I presume if you made your addin work above, that it is compiling as a DLL, not a standalone EXE. Your Winform app appears to be a separate EXE application.
To make this work, you’ll want to add your Form1 to the DLL project. Once you’ve got it in there, you can change TaskDialog.Show to instead these two lines:
var myForm - new Form1();
myForm.ShowDialog();
With that, you’re on your way.

Why Showing a dialog in App() constructor prevents WPF app showing up?

I used visual studio to create a new wpf application. I added a constructor for the app:
using System.Windows;
namespace WpfApplication
{
public partial class App : Application
{
App() : base()
{
MessageBox.Show("foo", "bar");
}
}
}
The application will only show the message box, then it will not show the main window after you close the message box.
However, if I make it a static constructor like:
using System.Windows;
namespace WpfApplication
{
public partial class App : Application
{
static App()
{
MessageBox.Show("foo", "bar");
}
}
}
It will be able to launch the main window.
Can someone help me in understanding what's wrong with the first method?
Because StartupUrl is not set yet.
In the constructor of System.Windows.Application, it invoke an async operation. And what we care about is DoStartup method in that async operation, in which it checks if StartupUrl is null.
When invoke an async operation, it post a message and that should be handled after you call App.Run().
Calling MessageBox.Show needs to use the current dispatcher, then the previous inovked operation will be handled(but StartupUrl is not set).
For the static constructor, it is called before constructor, so no such problem exist.

WPF: polling the keyboard

I am working on a WPF application that runs on an embedded device (.NET Standard 4 for embedded). It has a whole bunch of hardware attached which gets in the way when I'm testing, so I created a DummyHardware interface that just does nothing except print log messages when I run my unit tests, or run stand-alone on my development PC.
So far so good. But: the device has a 4-key keypad which is polled. My dummy keypad class went into an infinite loop when waiting for a key to be pressed, because there were no keys to press :-) So I thought, "Ok, I'll poll the keyboard to see if 1,2,3 or 4 is pressed". But I get the exception
The calling thread must be STA...
when I called Keyboard.IsKeyDown( Key.D1 ). The keypad polling takes place in a separate thread (to decouple from the generally slow serial comms of the rest of the hardware). Any thoughts on how to proceed? Invoke?
Note: one alternative would be to just skip the "wait for key" test on dummy hardware, but then I don't know which key was pressed and the following code which relies on it won't function correctly. Yuk.
You can just set the ApartmentState to be STA. using the Thread.SetApartmentState method
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace staThread
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Thread keyboardThread;
public MainWindow()
{
InitializeComponent();
keyboardThread = new Thread(new ThreadStart(KeyboardThread));
keyboardThread.SetApartmentState(ApartmentState.STA);
keyboardThread.Start();
}
void KeyboardThread()
{
while (true)
{
if (Keyboard.IsKeyDown(Key.A))
{
}
Thread.Sleep(100);
}
}
}
}
I have a simple method that handles running on the UI thread for me:
public object RunOnUiThread(Delegate method)
{
return Dispatcher.Invoke(DispatcherPriority.Normal, method);
}
Where Dispatcher is initialised using Dispatcher.CurrentDispatcher from the UI thread. It can be called from any thread and is used like this:
UiThreadManager.RunOnUiThread((Action)delegate
{
// running on the UI thread
});

WPF DocumentViewer: Switching between windows makes the window as if it has crashed

I have a WPF application with a custom control, based on DocumentViewer. There is a "Print" button, which leads to printer selection dialog. When I select a certain printer ("novaPDF Lite Server v7") and then press the "Print" button, a dialog box with printer settings is displayed. That dialog box appears in the task bar as a separate application.
When I activate that printer dialog, then activate some other application and then switch back to the WPF application, its client are (everything below the title bar) is white. It looks as if it hangs.
When I close the printer settings dialog, the WPF application starts to look in the normal way (all the controls are there).
I suppose that the problem is caused by the way the print process is started. Maybe it can fixed by doing the printing in a separate thread.
Here's how it is done now:
using System.Printing;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
namespace MyControls
{
public class MyDocumentViewer : DocumentViewer
{
[...]
protected override void OnPrintCommand()
{
base.OnPrintCommand();
this.FirePrintFinished();
}
[...]
}
What can I do in order to fix the problem?
Update 1 (03.09.2013): Changing the MyDocumentViewer so that printing is done asynchronously didn't help.
namespace MyControls
{
public class MyDocumentViewer : DocumentViewer
{
[...]
protected override void OnPrintCommand()
{
var worker = new PrintWorker(this);
Dispatcher.Invoke(new Action(worker.DoWork));
}
public void OnPrintCommandBase()
{
base.OnPrintCommand();
}
[...]
}
public class PrintWorker
{
private readonly MyDocumentViewer _myDocumentViewer;
public PrintWorker(MyDocumentViewer myDocumentViewer)
{
_myDocumentViewer = myDocumentViewer;
}
public void DoWork()
{
_myDocumentViewer.OnPrintCommandBase();
_myDocumentViewer.FirePrintFinished();
}
}
}
Update 2 (05.09.2013): The problem can only be reproduced, when I access the machine with the application via Remote Desktop (it does not occur, if I run the program locally).
Dispatcher is here to run a delegate in the UI thread, Dispatcher.Invoke() will run a delegate synchronously on the thread the Dispatcher is associated with (see msdn).
What you need may be Dispatcher.BeginInvoke(), or you should even use BackgroundWorker class to have a real background operation.
I'd also recommend you using Application.Current.Dispatcher (see this question: Dispatcher.CurrentDispatcher vs. Application.Current.Dispatcher)

Console.Write in .NET GUI Application

In a C# .NET GUI Application. I also need the console in the background for some tasks. Basically, I'm using a Thrid Party library for some processing (takes lot of time) which writes its intermediate results to console. This processing is a computationally time taking task. So, I'm assigning this task to backgroundworker. I mean background worker calls these library functions. But problem is there is no way for me to show the user status of computation, because I don't have source of the library. I was hoping Console will be shown. But surprisingly Console.WriteLine doesn't seem to work. I mean, there isn't any console window shown. How come?
EDIT:
I tried setting application type = console. But there seems to be a problem. Only, main thread is able to access the console. Only Console.WriteLines executed by main (Application) thread are displayed on console. Console.WriteLines executed by other (BackgroundWorker)threads of the GUI, the output is not shown. I need console only for Background workers. I mean, When background worker starts, console starts & when it ends console will be off.
Create your own console window and use the Console.SetOut(myTextWriter); method to read anything written to the console.
Set your application type to "Console Application". Console applications can also create GUI windows with no problem, and write to the console at the same time.
If you don't have control of the main application, and you want to make sure that a console is shown, you can p/invoke AllocConsole (signature here).
This isn't the same as being a console application though, your application will always get a separate console window, which might be surprising to someone who launched it from a command prompt window. You can work around that with AttachConsole (signature and example here) but shell redirection of the output still won't work. That's why I suggest setting the application subsystem to console if you can.
Followed by #jgauffin, here is the implementation of Console.SetOut method.
Create a TextWriter inherited class.
using System;
using System.Text;
using System.IO;
using System.Windows.Forms;
namespace ConsoleRedirection
{
public class TextBoxStreamWriter : TextWriter
{
TextBox _output = null;
public TextBoxStreamWriter(TextBox output)
{
_output = output;
}
public override void Write(char value)
{
base.Write(value);
_output.AppendText(value.ToString()); // When character data is written, append it to the text box.
}
public override Encoding Encoding
{
get { return System.Text.Encoding.UTF8; }
}
}
}
And in the Form, code as below.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace ConsoleRedirection
{
public partial class FormConsole : Form
{
// That's our custom TextWriter class
TextWriter _writer = null;
public FormConsole()
{
InitializeComponent();
}
private void FormConsole_Load(object sender, EventArgs e)
{
// Instantiate the writer
_writer = new TextBoxStreamWriter(txtConsole);
// Redirect the out Console stream
Console.SetOut(_writer);
Console.WriteLine("Now redirecting output to the text box");
}
// This is called when the "Say Hello" button is clicked
private void txtSayHello_Click(object sender, EventArgs e)
{
// Writing to the Console now causes the text to be displayed in the text box.
Console.WriteLine("Hello world");
}
}
}
Original code is from https://saezndaree.wordpress.com/2009/03/29/how-to-redirect-the-consoles-output-to-a-textbox-in-c/
You can check the link for cross-thread calls and advanced implementations at comments.

Categories