【问题标题】:How do I add a console like element to a c# winforms program如何将类似控制台的元素添加到 c# winforms 程序
【发布时间】:2008-10-31 01:07:28
【问题描述】:

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

有什么更好的方法来实现嵌入在 winforms 窗口中的类似控制台的元素。

编辑: 我仍然希望能够嵌入像 Visual Studio 这样的输出窗口,但由于我无法找到一种简单的方法,因此我使用了两种解决方案。 除了使用有效的 RichTextBox 之外,您还必须不时清除它。我使用我 pinvoke 的控制台。这是我为处理此问题而编写的一个小包装类。


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");


【问题讨论】:

    标签: c# .net winforms


    【解决方案1】:

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

    【讨论】:

    • 大不是无限的。如果用于记录/调试消息,它最终会陷入困境并开始颠簸。
    • 控制台窗口也不是无限的。
    • 它是无限的,因为旧文本“掉出”顶部并且输出到窗口不会因为输出更多而变慢。我认为这就是@Ovidiu 的想法。
    • 这种“失败”行为也可以应用于放入 RichTextBox 中的内容。
    【解决方案2】:

    您不能只将日志项添加到 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 上有)。您可以找出确切的代码。

    【讨论】:

      【解决方案3】:

      我使用 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;
      
              /// <summary>
              /// Initialize the Win32 console for this process.
              /// </summary>
              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() );
              }
      
              /// <summary>
              /// The main entry point for the application.
              /// </summary>
              [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
      
          //--------------------------------------------------------------------------
          /// <summary>
          /// Functions in Kernel32.dll
          /// </summary>
          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
      
          //--------------------------------------------------------------------------
          /// <summary>
          /// Functions in User32.dll
          /// </summary>
          #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
      

      【讨论】:

        【解决方案4】:

        我遇到了这个确切的挑战。我已经解决了两种不同的方法,工作和执行都将在重负载下进行。一种方法是使用 ListView。添加一行文字是这样的:

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

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

        编辑:重新阅读,我看到你想要复制/粘贴工作。也许 RichText 控件执行得还不错 - 不知道,但如果您使用 ListView 或 DataGrid,则必须进行更多编码才能使复制/粘贴工作。

        【讨论】:

          【解决方案5】:
          public class ConsoleTextBox: TextBox
          {
              private List<string> contents = new List<string>();
              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();
              }
          }
          

          【讨论】:

            【解决方案6】:

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

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

            【讨论】:

              【解决方案7】:

              我以前使用过文本框。将其添加到您的表单中,将 Multipline 属性设置为 true,将滚动条设置为 Vertical。 最后添加以下代码:

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

              本质上是将您的评论添加到现有文本中,同时添加换行符。最后选择长度 = 0 的文本的最后一位。ScrollToCaret 强制文本框向下滚动到光标所在的位置(在最后一行)

              希望这会有所帮助。

              【讨论】:

              • 每增加一行,我就会变慢。
              • RichTextBox 是为大量文本设计的,TextBox 不是。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-03-03
              • 1970-01-01
              相关资源
              最近更新 更多