如何在ac#winforms程序中添加类似控制台的控制台

我有一个程序来监视调试消息,我已经尝试使用TextBox并将消息附加到它但它不能很好地扩展,并在消息数量变大时减慢速度。 然后我尝试了一个ListBox,但在添加新消息时,滚动正在捕捉到顶部。 它也不允许像文本框那样剪切和粘贴。

什么是实现类似控制台的更好方法,如winforms窗口中嵌入的元素。

编辑:我仍然希望能够嵌入像visual studio这样的输出窗口,但由于我无法找到一个简单的方法,所以我使用的是两个解决方案。 除了使用有效的RichTextBox之外,你必须时不时地清除它。 我使用的是我用的控制台。 这是我写的一个小包装类来处理这个问题。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; namespace Con { class Ext_Console { static bool console_on = false; public static void Show(bool on,string title) { console_on = on; if (console_on) { AllocConsole(); Console.Title = title; // use to change color Console.BackgroundColor = System.ConsoleColor.White; Console.ForegroundColor = System.ConsoleColor.Black; } else { FreeConsole(); } } public static void Write(string output) { if (console_on) { Console.Write(output); } } public static void WriteLine(string output) { if (console_on) { Console.WriteLine(output); } } [DllImport("kernel32.dll")] public static extern Boolean AllocConsole(); [DllImport("kernel32.dll")] public static extern Boolean FreeConsole(); } } // example calls Ext_Console.Write("console output "); Ext_Console.WriteLine("console output"); Ext_Console.Show(true,"Title of console"); 

RichTextBox有一个快速的AppendText方法。 它可以很好地处理大文本。
我相信这是你需要的最好的。

您不能只是继续将日志项添加到WinForms控件(ListBox或RichTextBox) – 它最终会被阻塞并开始交换到磁盘。

我曾经有过这个确切的错误。 我的解决方案是偶尔剪辑显示的消息列表。 在伪代码中,这类似于:

 void AddLogMessage(String message) { list.Items.Add(message); // DO: Append message to file as needed // Clip the list if (list.count > ListMaxSize) { list.Items.RemoveRange(0, list.Count - listMinSize); } // DO: Focus the last item on the list } 

ListMaxSize应该比ListMinSize大得多,因此剪切不会经常发生。 ListMinSize是您通常需要在日志记录列表中查看的最近消息的数量。

这只是伪代码,ListBox项目集合上实际上没有RemoveRange(但是List上有)。 你可以找出确切的代码。

我在使用Win32控制台窗口的C#窗口程序(WInforms或WPF)中执行此操作。 我有一个包含一些基本Win32 API的小类,我在程序开始时创建一个控制台。 这只是一个例子:在“现实生活中”,您只需使用设置或其他东西来启用控制台。

 using System; using System.Windows.Forms; using Microsoft.Win32.SafeHandles; using System.Diagnostics; using MWin32Api; namespace WFConsole { static class Program { static private SafeFileHandle ConsoleHandle; ///  /// Initialize the Win32 console for this process. ///  static private void InitWin32Console() { if ( !K32.AllocConsole() ) { MessageBox.Show( "Cannot allocate console", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error ); return; } IntPtr handle = K32.CreateFile( "CONOUT$", // name K32.GENERIC_WRITE | K32.GENERIC_READ, // desired access K32.FILE_SHARE_WRITE | K32.FILE_SHARE_READ, // share access null, // no security attributes K32.OPEN_EXISTING, // device already exists 0, // no flags or attributes IntPtr.Zero ); // no template file. ConsoleHandle = new SafeFileHandle( handle, true ); if ( ConsoleHandle.IsInvalid ) { MessageBox.Show( "Cannot create diagnostic console", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error ); return; } // // Set the console screen buffer and window to a reasonable size // 1) set the screen buffer sizse // 2) Get the maximum window size (in terms of characters) // 3) set the window to be this size // const UInt16 conWidth = 256; const UInt16 conHeight = 5000; K32.Coord dwSize = new K32.Coord( conWidth, conHeight ); if ( !K32.SetConsoleScreenBufferSize( ConsoleHandle.DangerousGetHandle(), dwSize ) ) { MessageBox.Show( "Can't get console screen buffer information.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error ); return; } K32.Console_Screen_Buffer_Info SBInfo = new K32.Console_Screen_Buffer_Info(); if ( !K32.GetConsoleScreenBufferInfo( ConsoleHandle.DangerousGetHandle(), out SBInfo ) ) { MessageBox.Show( "Can't get console screen buffer information.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } K32.Small_Rect sr; ; sr.Left = 0; sr.Top = 0; sr.Right = 132 - 1; sr.Bottom = 51 - 1; if ( !K32.SetConsoleWindowInfo( ConsoleHandle.DangerousGetHandle(), true, ref sr ) ) { MessageBox.Show( "Can't set console screen buffer information.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error ); return; } IntPtr conHWND = K32.GetConsoleWindow(); if ( conHWND == IntPtr.Zero ) { MessageBox.Show( "Can't get console window handle.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error ); return; } if ( !U32.SetForegroundWindow( conHWND ) ) { MessageBox.Show( "Can't set console window as foreground.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error ); return; } K32.SetConsoleTitle( "Test - Console" ); Trace.Listeners.Add( new ConsoleTraceListener() ); } ///  /// The main entry point for the application. ///  [STAThread] static void Main() { InitWin32Console(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault( false ); Application.Run( new Main() ); } } } using System; using System.Runtime.InteropServices; namespace MWin32Api { #region Kernel32 Functions //-------------------------------------------------------------------------- ///  /// Functions in Kernel32.dll ///  public sealed class K32 { #region Data Structures, Types and Constants //---------------------------------------------------------------------- // Data Structures, Types and Constants // [StructLayout( LayoutKind.Sequential )] public class SecurityAttributes { public UInt32 nLength; public UIntPtr lpSecurityDescriptor; public bool bInheritHandle; } [StructLayout( LayoutKind.Sequential, Pack = 1, Size = 4 )] public struct Coord { public Coord( UInt16 tx, UInt16 ty ) { x = tx; y = ty; } public UInt16 x; public UInt16 y; } [StructLayout( LayoutKind.Sequential, Pack = 1, Size = 8 )] public struct Small_Rect { public Int16 Left; public Int16 Top; public Int16 Right; public Int16 Bottom; public Small_Rect( short tLeft, short tTop, short tRight, short tBottom ) { Left = tLeft; Top = tTop; Right = tRight; Bottom = tBottom; } } [StructLayout( LayoutKind.Sequential, Pack = 1, Size = 24 )] public struct Console_Screen_Buffer_Info { public Coord dwSize; public Coord dwCursorPosition; public UInt32 wAttributes; public Small_Rect srWindow; public Coord dwMaximumWindowSize; } public const int ZERO_HANDLE_VALUE = 0; public const int INVALID_HANDLE_VALUE = -1; #endregion #region Console Functions //---------------------------------------------------------------------- // Console Functions // [DllImport( "kernel32.dll", SetLastError = true )] public static extern bool AllocConsole(); [DllImport( "kernel32.dll", SetLastError = true )] public static extern bool SetConsoleScreenBufferSize( IntPtr hConsoleOutput, Coord dwSize ); [DllImport( "kernel32.dll", SetLastError = true )] public static extern bool GetConsoleScreenBufferInfo( IntPtr hConsoleOutput, out Console_Screen_Buffer_Info lpConsoleScreenBufferInfo ); [DllImport( "kernel32.dll", SetLastError = true )] public static extern bool SetConsoleWindowInfo( IntPtr hConsoleOutput, bool bAbsolute, ref Small_Rect lpConsoleWindow ); [DllImport( "kernel32.dll", SetLastError = true )] public static extern IntPtr GetConsoleWindow(); [DllImport( "kernel32.dll", SetLastError = true )] public static extern bool SetConsoleTitle( string Filename ); #endregion #region Create File //---------------------------------------------------------------------- // Create File // public const UInt32 CREATE_NEW = 1; public const UInt32 CREATE_ALWAYS = 2; public const UInt32 OPEN_EXISTING = 3; public const UInt32 OPEN_ALWAYS = 4; public const UInt32 TRUNCATE_EXISTING = 5; public const UInt32 FILE_SHARE_READ = 1; public const UInt32 FILE_SHARE_WRITE = 2; public const UInt32 GENERIC_WRITE = 0x40000000; public const UInt32 GENERIC_READ = 0x80000000; [DllImport( "kernel32.dll", SetLastError = true )] public static extern IntPtr CreateFile( string Filename, UInt32 DesiredAccess, UInt32 ShareMode, SecurityAttributes SecAttr, UInt32 CreationDisposition, UInt32 FlagsAndAttributes, IntPtr TemplateFile ); #endregion #region Win32 Miscelaneous //---------------------------------------------------------------------- // Miscelaneous // [DllImport( "kernel32.dll" )] public static extern bool CloseHandle( UIntPtr handle ); #endregion //---------------------------------------------------------------------- private K32() { } } #endregion //-------------------------------------------------------------------------- ///  /// Functions in User32.dll ///  #region User32 Functions public sealed class U32 { [StructLayout( LayoutKind.Sequential )] public struct Rect { public Int32 Left; public Int32 Top; public Int32 Right; public Int32 Bottom; public Rect( short tLeft, short tTop, short tRight, short tBottom ) { Left = tLeft; Top = tTop; Right = tRight; Bottom = tBottom; } } [DllImport( "user32.dll" )] public static extern bool GetWindowRect( IntPtr hWnd, [In][MarshalAs( UnmanagedType.LPStruct )]Rect lpRect ); [DllImport( "user32.dll", SetLastError = true )] public static extern bool SetForegroundWindow( IntPtr hWnd ); //---------------------------------------------------------------------- private U32() { } } // U32 class #endregion } // MWin32Api namespace 

我遇到了这个确切的挑战。 我已经用两种不同的方式解决了这个问题,工作和执行都会在很重的情况下解决。 一种方法是使用ListView。 添加一行文字是这样的:

  ListViewItem itm = new ListViewItem(); itm.Text = txt; this.listView1.Items.Add(itm); this.listView1.EnsureVisible(listView1.Items.Count - 1); 

另一种方法是在虚拟模式下使用DataGridView。 我没有那么方便的代码。 虚拟模式是你的朋友。

编辑:重新阅读,我看到你想复制/粘贴工作。 也许RichText控件执行正常 – 不知道,但如果你使用ListView或DataGrid,你必须做更多的编码才能使复制/粘贴工作。

将列表框的selectedindex设置为最后一个元素,使其滚动到底部

另外,将列表框中的项目数量限制在合理的范围内(从顶部删除,保留后面的项目),这样就不会扼杀所有的记忆

我以前用过一个文本框。 将其添加到表单,将Multipline属性设置为true,将Scrollbars设置为Vertical。 最后添加以下代码:

  private void AddConsoleComment(string comment) { textBoxConsole.Text += comment + System.Environment.NewLine; textBoxConsole.Select(textBoxConsole.Text.Length,0); textBoxConsole.ScrollToCaret(); } 

基本上它将您的评论添加到现有文本,也附加换行符。 最后选择长度为0的文本的最后一位.ScrollToCaret强制文本框向下滚动到光标所在的位置(在最后一行)

希望这可以帮助。

 public class ConsoleTextBox: TextBox { private List contents = new List(); private const int MAX = 50; public void WriteLine(string input) { if (contents.Count == MAX) contents.RemoveAt(MAX-1); contents.Insert(0, input); Rewrite(); } private void Rewrite() { var sb = new StringBuilder(); foreach (var s in contents) { sb.Append(s); sb.Append(Environment.NewLine); } this.Text = sb.ToString(); } }