2008-10-31 10 views
13

Tengo un programa que supervisa los mensajes de depuración y he intentado usar un TextBox y he anexado los mensajes, pero no escala muy bien y disminuye la velocidad cuando la cantidad de mensajes se hace grande Luego probé un ListBox, pero el desplazamiento estaba llegando a la cima al agregar nuevos mensajes. Tampoco permite cortar y pegar como lo hace el cuadro de texto.Cómo agrego un elemento similar a la consola al programa aC# winforms

¿Cuál es una mejor manera de implementar un elemento parecido a una consola incrustado en una ventana de winforms.

Edit: Todavía me gustaría poder incrustar una ventana de salida como Visual Studio pero como no puedo encontrar una manera fácil aquí están las dos soluciones que uso. Además de usar el RichTextBox que funciona, pero tiene que borrarlo de vez en cuando. Yo uso una consola que pinvoke. Aquí hay una pequeña clase de contenedor que escribí para manejar esto.


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


Respuesta

10

RichTextBox tiene un método AppendText que es rápido. Y puede manejar bien el texto grande.
Creo que es lo mejor para lo que necesita.

+1

Grande no es infinito. Si se usa para registrar/depurar mensajes, eventualmente se estancará y comenzará a agitarse. – dbkk

+2

La ventana de la consola tampoco es infinita. –

+1

Es infinito en el sentido de que el texto antiguo "se cae" en la parte superior y la salida a la ventana no se vuelve más lenta a medida que se genera más. Creo que esto es lo que @Ovidiu tiene en mente. –

1

establecer el selectedIndex del cuadro de lista hasta el último elemento para que sea desplazarse hasta la parte inferior

también, limitar el número de elementos en el cuadro de lista a algo razonable (eliminar desde la parte superior, a mantener la tarde elementos) para que no mastique toda su memoria

1

He utilizado anteriormente un cuadro de texto. Agréguelo a su formulario, establezca la propiedad Multipline en true, Scrollbars en Vertical. Y, por último agregar la siguiente código:

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

esencialmente su añadiendo su comentario al texto existente, también se añade un avance de línea. Y, finalmente, seleccionando el último bit de texto de longitud = 0. ScrollToCaret obliga al cuadro de texto a desplazarse hacia abajo donde se encuentra el cursor (en la última línea)

Espero que esto ayude.

+0

Seré más lento con cada línea agregada. –

+1

He hecho esto. El rendimiento es terrible. –

+3

Las personas que han votado esto en realidad no lo han intentado. –

2

He tenido este desafío exacto. Lo he resuelto de dos maneras diferentes, tanto el trabajo como el desempeño lo harán bajo una gran carga. Una forma es con un ListView. Agregar una línea de texto es la siguiente:

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

La otra forma es con un DataGridView en modo virtual. No tengo ese código tan útil. El modo virtual es tu amigo.

EDIT: relectura, veo que quieres copiar/pegar para trabajar. Tal vez el control de RichText funciona bien, no lo sé, pero si usa ListView o DataGrid, tendría que hacer más codificación para que Copiar/Pegar funcione.

4

Lo hago en mis programas de ventana C# (WInforms o WPF) usando una ventana de consola Win32. Tengo una clase pequeña que engloba algunas API básicas de Win32. Debo crear una consola cuando comienza el programa. Esto es solo un ejemplo: en la "vida real" usaría una configuración u otra cosa para habilitar solo la consola cuando la necesitara.

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 
5

No se puede seguir añadiendo elementos de registro a un control de Windows Forms (ListBox o RichTextBox) - con el tiempo se tapan y comenzar a intercambiar en el disco.

Tuve este error exacto en un punto.La solución que tuve fue recortar la lista de mensajes mostrados ocasionalmente. En pseudocódigo, esto es algo como:

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 debe ser sustancialmente más grande que ListMinSize, por lo que el recorte no sucede muy a menudo. ListMinSize es la cantidad de mensajes recientes que normalmente debería revisar en su lista de registro.

Esto es solo un pseudocódigo, en realidad no hay RemoveRange en la colección de elementos de ListBox (pero hay en la lista). Puedes descubrir el código exacto.

1
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(); 
    } 
} 
Cuestiones relacionadas