WPF window from a Console project?

I recently started a C# project (VS 2008) as a 'Console' project where I wrote a few libraries, test programs, etc. Now I'd like to add a couple of WPF windows, but it looks like console project won't let me do that. I'm coming from Java so this is a little strange. How can I add a WPF form (which I will instantiate my self from my "main" class?


Solution 1:

The accepted answer is not entirely true, I'm afraid, just add the [STAThread] attribute before your mainmethod and make references to the right libraries (like System.Windows) and you're all set to add wpf windows.

EDIT : in the comments @JamesWilkins supplied me with this usefull link : http://code-phix.blogspot.be/2013/11/creating-wpf-project-from-scratch.html

Solution 2:

I had the same question and was looking for a similar answer. I found info all over the place, so I'm putting what I found in one place. I also needed a way to hide and show the console window, so I found out this worked (for VS 2013+):

  1. Create a new console project (be sure to select the .NET framework version you need to use - I needed to use .Net 4.0 myself). Make sure to have the following references:

    • PresentationFramework
    • PresentationCore
    • WindowsBase
    • System.xaml
  2. Right-click on the project in the solution explorer, select "Properties", and change the project Output Type to Windows Application. This prevents the console window from showing on startup (if you want that, skip this step).

  3. While controlling the console window is not necessary in order to add WPF windows, it can be useful. If you don't need this, skip to #4. In the "Program" class for the console, add this in order to control the window:

    public class Program
    {
      [DllImport("kernel32.dll", SetLastError = true)]
      static extern bool AllocConsole(); // Create console window
    
      [DllImport("kernel32.dll")]
      static extern IntPtr GetConsoleWindow(); // Get console window handle
    
      [DllImport("user32.dll")]
      static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
    
      const int SW_HIDE = 0;
      const int SW_SHOW = 5;
    

    This allows to create, hide, and show the console window. I created these methods to do this:

      static void ShowConsole()
      {
          var handle = GetConsoleWindow();
          if (handle == IntPtr.Zero)
              AllocConsole();
          else
              ShowWindow(handle, SW_SHOW);
      }
    
      static void HideConsole()
      {
          var handle = GetConsoleWindow();
          if (handle != null)
              ShowWindow(handle, SW_HIDE);
      }
    

    These are mostly self explanatory, but if the project is in window mode, GetConsoleWindow(); returns null, so here we test if the handle is null (zero in this case), and if so, a console window needs to be created (only once). After this, GetConsoleWindow(); will always return a handle to use.

  4. As stated in another answer already, you need to add [STAThread] on a line before your console's Main method. This is required, as WPF needs to run in a Single Threaded Apartment environment.

      [STAThread]
      static void Main(string[] args)
      {
      }
    
  5. Adding a window: To do this, just add a user control to your project and name it "MainWindow" (or whatever you like). Just right-click the project node in the solution explorer and select Add->User Control.... Open the MainWindow.xaml.cs code behind and change MainWindow : UserControl to MainWindow : Window. Next, open the MainWindow.xaml file and change the first tag <UserControl to <Window (and make sure the closing tag gets renamed also, which should be automatic if using Visual Studio). Close all "MainWindow" editor tabs and reopen (just to be sure, may not be necessary). You should see MainWindow.xaml now show a window in the design pane.

  6. Showing the WPF window: To do this, we need to start the window message loop, which is really easy. To begin, I created some properties to store the objects. Just put this somewhere in the Program class.

    public static Application WinApp { get; private set; }
    public static Window MainWindow { get; private set; }
    

    Next we have to create the message loop by creating a System.Windows.Application object, then pass it the main window. I created this method to perform this task:

    static void InitializeWindows()
    {
        WinApp = new Application();
        WinApp.Run(MainWindow = new MainWindow()); // note: blocking call
    }
    

    and that's it! To test this, put some content in your main window and do this:

    [STAThread]
    static void Main(string[] args)
    {
        ShowConsole(); // Show the console window (for Win App projects)
        Console.WriteLine("Opening window...");
        InitializeWindows(); // opens the WPF window and waits here
        Console.WriteLine("Exiting main...");
    }
    

Hope that helps saves someone time, cheers! ;)

TIP: I found it helpful, in my case, to call InitializeWindows() in a new thread; however, that means that you must create UI objects (among other things) in the the same thread that the Application object was created in. To communicate with the new thread, I just used the Dispatcher class (WinApp.Dispatcher.BeginInvoke()) to run requests in the WPF thread context.

For Windows 8/10: If you are debugging and you don't see any text in your output window, take a look here: https://stackoverflow.com/a/49145317/1236397