[WPF][C#] 當不需要華麗的使用者介面或是需要啟動參數時....!? - 開啟WPF的Console視窗

  • 36864
  • 0
  • C#
  • 2013-07-15

如果我們使用Visual Studio中內建的專案樣版建立WPF專案,並且按下F5執行,就會看到專案中預設的MainWindow直接跳出來,這是很合理,也很合邏輯的一件事(這不是廢話嗎?!)。不過,寫過Windows Form應用程式或是寫過Windows Console Application的朋友們可能就會好奇:「我如果想在命令列輸入一些啟動參數的時候怎麼辦?」,抑或:「我在WPF中可以使用Console.WriteLine()並且有個簡單的介面追踨一些變數的值嗎?」;甚至是:「如果我想要做一個精簡模式,直接從命令列下指令,就能完成特定的工作;這樣WPF辦得到嗎?」。關於上面提到的問題,很開心的告訴大家:當然可以!!

 

如果我們使用Visual Studio中內建的專案樣版建立WPF專案,並且按下F5執行,就會看到專案中預設的MainWindow直接跳出來,這是很合理,也很合邏輯的一件事(這不是廢話嗎?!)。不過,寫過Windows Form應用程式或是寫過Windows Console Application的朋友們可能就會好奇:「我如果想在命令列輸入一些啟動參數的時候怎麼辦?」,抑或:「我在WPF中可以使用Console.WriteLine()並且有個簡單的介面追踨一些變數的值嗎?」;甚至是:「如果我想要做一個精簡模式,直接從命令列下指令,就能完成特定的工作;這樣WPF辦得到嗎?」。關於上面提到的問題,很開心的告訴大家:當然可以!!

 

其實WPF專案中原生就內建支援Console模式了,不過,它的Console模式和過去大家習慣的長相和用法就還是有蠻大的差別:首先,它的Console模式會另外新開出一個Console視窗;其次,WPF的Console視窗中的標題、前景和背景顏色都是可以輕鬆的去設定的,這個是和傳統的Console模式有蠻大差異的地方。

 

要開出WPF 的Console視窗最簡單的方法如下:在Visual Studio 2010的選單列中選取Project -> [專案名稱] Properties,接著在專案屬性的Application頁籤中,將Output type由原來的Windows Application改為Console Application,按下F5進行除錯,就可以看到內建的Console視窗囉!!

imageimage

下面是我修改後實際執行的畫面--執行時會直接開啟兩個視窗(原本的WPF應用程式視窗和Console視窗)喔!!!

image

而且我們只需要在CodeBehind檔中,跟平常使用System.Console一樣,以C#就可以簡單的控制並且和Console視窗進行互動喔!!

例如下面簡單的範例(如果想對Console多一點了解,可以參考MSDN上的Console Class):

直接在MainWindow()的建構子中操作Console視窗

public MainWindow()
{
    //設定Console視窗的大小,注意,這邊的寬和高指的是文字的行數和列數,而不是畫面上的點喔!!
    Console.SetWindowSize( 80 , 24 );

    //設定Console視窗的起始位置
    Console.SetWindowPosition( 0 , 0 );

    //設定Console視窗的標題
    Console.Title = "WPF Console Demo";

    //設定文字前景色
    Console.ForegroundColor = ConsoleColor.Red;

    Console.WriteLine( "Console視窗啟動" );

    //可以再次設定前景色,之後的文字就會以新的顏色列印
    Console.ForegroundColor = ConsoleColor.White;
    Console.WriteLine( "歡迎使用Console視窗!!" );

    InitializeComponent();
}

好玩嗎?! 還沒完,還有別的東西可以玩!!如果將專案設定成Console Application的話,那就會一次出現WPF應用程式的主畫面和Console視窗,那~~如果我們希望藉由命令列給的參數來決定要開敵的是Console視窗或是WPF視窗的話,可以怎麼做呢!?

來~讓我們一起來做一次!!還是先從一個標準的WPF應用程式開始(請自行建立一個新的WPF應用程式再跟著作下去吧~):首先,打開App.xaml檔,溫柔的把 StartupUri="MainWindow.xaml" 這一段拿掉

接著我們要建立一個靜態的ConsoleHelper類別,來幫助我們開啟或是關閉Console視窗,內容如下:

 

ConsoleHelper.cs

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;


[SuppressUnmanagedCodeSecurity]
public static class ConsoleHelper
{
    private const string Kernel32_DllName = "kernel32.dll";

    [DllImport( Kernel32_DllName )]
    private static extern bool AllocConsole();

    [DllImport( Kernel32_DllName )]
    private static extern bool FreeConsole();

    [DllImport( Kernel32_DllName )]
    private static extern IntPtr GetConsoleWindow();

    [DllImport( Kernel32_DllName )]
    private static extern int GetConsoleOutputCP();

    public static bool HasConsole
    {
        get
        {
            return GetConsoleWindow() != IntPtr.Zero;
        }
    }

    public static void Show()
    {
        if( !HasConsole )
        {
            AllocConsole();
            InvalidateOutAndError();
        }
    }

    public static void Hide()
    {
        if( HasConsole )
        {
            SetOutAndErrorNull();
            FreeConsole();
        }
    }

    public static void Toggle()
    {
        if( HasConsole )
        {
            Hide();
        }
        else
        {
            Show();
        }
    }

    static void InvalidateOutAndError()
    {
        Type type = typeof( System.Console );
        System.Reflection.FieldInfo _out = type.GetField( "_out" , System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic );
        System.Reflection.FieldInfo _error = type.GetField( "_error" , System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic );
        System.Reflection.MethodInfo _InitializeStdOutError = type.GetMethod( "InitializeStdOutError" , System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic );
        _out.SetValue( null , null ); _error.SetValue( null , null );
        _InitializeStdOutError.Invoke( null , new object[] { true } );
    }

    static void SetOutAndErrorNull()
    {
        Console.SetOut( TextWriter.Null );
        Console.SetError( TextWriter.Null );
    }

}

再來,我們得覆寫App.xaml.cs中的 OnStartup方法,並且在裡面加上我們要的條件判斷,用來決定要開啟的是Console視窗或是WPF應用程式主視窗(下面的範例為,若有輸入啟動參數,則會開啟Console視窗,否則就會開啟WPF應用程式的主視窗)。

 

App.xaml.cs中覆寫後的OnStartup方法

protected override void OnStartup( StartupEventArgs e )
{
    base.OnStartup( e );

    if( e.Args.Count() > 0 )
    {
        //呼叫出Console視窗,這邊記得要先呼叫出來,再針對它來做其他的設定,以免出錯喔!!
        ConsoleHelper.Show();

        //設定Console視窗的大小,注意,這邊的寬和高指的是文字的行數和列數,而不是畫面上的點喔!!
        Console.SetWindowSize( 80 , 24 );

        //設定Console視窗的起始位置
        Console.SetWindowPosition( 0 , 0 );

        //設定Console視窗的標題
        Console.Title = "WPF Console Demo";

        //設定文字前景色
        Console.ForegroundColor = ConsoleColor.Red;

        Console.WriteLine( "Console視窗啟動" );

        //可以再次設定前景色,之後的文字就會以新的顏色列印
        Console.ForegroundColor = ConsoleColor.White;
        Console.WriteLine( "歡迎使用Console視窗!!" );

        Console.WriteLine( string.Format( "參數數目:{0}" , e.Args.Count().ToString() ) );

        string input = string.Empty;

        while( input.ToLower() != "exit" )
        {
            Console.WriteLine( "請輸入exit關閉程式:" );

            input = Console.ReadLine();
        }

        App.Current.Shutdown();
    }
    else
    {
        App.Current.StartupUri = new System.Uri( "MainWindow.xaml" , System.UriKind.RelativeOrAbsolute );
    }
}

接著按下F5進行Debug,會發現一如往昔的,WPF主視窗出現了。不過,這個不是重點,我們要測的是當有啟動參數的時候要跳出Console視窗~而設定啟動參數在VisualStudio中可以輕鬆的辦到,只需要開啟專樣屬性面版,接著在Debug頁籤中的Start Options下方那個Command line arguments文字方塊中輸入供Debug模式使用的啟動參數後按下存檔鈕就行啦!!

image

按下F5來測試看看~~

image

這樣一來WPF應用程式就可以依照參數的不同來切換要顯示的是Console模式或是應用程式主畫面啦!!

 

最後,附上專案範例,請自行取用: