如何在控制台项目中创建WPF窗口?

16

我最近开始了一个C#项目(VS 2008),类型为“控制台(console)”项目,在其中编写了一些库、测试程序等。现在我想添加几个 WPF 窗口,但好像控制台项目不允许这样做。对我来说这有点奇怪,因为我之前用 Java 开发时并没有这个问题。那么,我应该如何添加一个 WPF 窗体(我会从我的“主”类中进行实例化)?

5个回答

40

被接受的答案在理论上是正确的。他所问的只是他是否确定他真的需要一个控制台应用程序。但我必须同意,作为这个确切问题的答案,我更喜欢你的回答。 - Mathias Lykkegaard Lorenzen
@JamesWilkins,谢谢你的提醒,我已经删除了它。不过这个答案仍然有用,所以我很遗憾看到负评。 - Peter
你可以尝试这个链接:http://code-phix.blogspot.com/2013/11/creating-wpf-project-from-scratch.html ;) - James Wilkins

33

我曾经有同样的问题并寻找类似的答案。我在各个地方找到了信息,所以我把我找到的整理在一个地方。我还需要一种隐藏和显示控制台窗口的方法,所以我发现这个方法行得通(适用于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...");
    }
    
希望这能为某些人节省时间,谢谢!;)
小贴士:在我的情况下,我发现在新线程中调用InitializeWindows()很有帮助;但是,这意味着您必须在创建Application对象的同一线程中创建UI对象(以及其他事物)。要与新线程通信,我只需使用Dispatcher类(WinApp.Dispatcher.BeginInvoke())在WPF线程上下文中运行请求。
对于Windows 8/10:如果您正在调试并且在输出窗口中看不到任何文本,请看这里:https://dev59.com/W6nka4cB1Zd3GeqPQplB#49145317

2
哇,这个应该得到更多的点赞。它不仅教你如何做,还会介绍一些额外的小技巧,让你了解为什么需要以这种方式进行操作。谢谢! - Chopnut
我还有一个问题。现在,我该如何从WPF窗口的点击按钮中调用控制台类方法?谢谢。 - Chopnut
你只需要调用它。Console类的方法是线程安全的:“使用这些流进行I/O操作是同步的,这意味着多个线程可以从流中读取或写入数据。” https://learn.microsoft.com/en-us/dotnet/api/system.console?redirectedfrom=MSDN&view=netframework-4.7.2 - James Wilkins

2
你应该将库代码移动到其他“类库”项目中,并从控制台项目中使用它。你的WPF窗口应该在另一个“WPF应用程序”项目中,该项目也将引用你的“类库”。

0
感谢Aku和Dmitriy,我创建了另一个项目(WPF),该项目将引用我的基于控制台的代码。

-1
你确定需要控制台项目吗?你可以创建一个“WPF应用程序”项目,并添加对你的库等的引用。如果尝试从控制台应用程序显示WPF窗口,由于控制台和WPF应用程序之间的线程模型差异,你将会得到一个异常。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接