2008-10-31 4 views
13

Mam program, który monitoruje komunikaty debugowania i próbowałem używać TextBox i dołączał do niego wiadomości, ale nie skaluje się bardzo dobrze i zwalnia sposób, gdy liczba wiadomości jest duża. Następnie wypróbowałem ListBox, ale przewijanie było przyciągane do góry podczas dołączania nowych wiadomości. Nie pozwala również na wycinanie i wklejanie, tak jak w przypadku pola tekstowego.Jak dodać konsolę jak element do programu aC# winforms

Jaki jest lepszy sposób na implementację elementu konsoli podobnego do osadzonego w oknie WinForm.

Edycja: Chciałbym móc osadzić okno wyjściowe, takie jak wizualne studio, ale ponieważ nie mogę wymyślić prostego sposobu, tutaj są dwa rozwiązania, których używam. Oprócz korzystania z RichTextBox, który działa, ale musisz wyczyścić go co jakiś czas. Używam konsoli, którą wybieram. Oto mała klasa, którą napisałem, aby sobie z tym poradzić.


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


Odpowiedz

10

RichTextBox ma metodę AppendText, która jest szybka. I może dobrze obsługiwać duży tekst.
Uważam, że jest to najlepsze rozwiązanie dla tego, czego potrzebujesz.

+1

Duży nie jest nieskończony. Jeśli użyjesz go do logowania/debugowania wiadomości, w końcu się ugrzęźnie i zacznie wyrzucać. – dbkk

+2

Okno konsoli również nie jest nieskończone. –

+1

Jest nieskończony w tym sensie, że stary tekst "wypada" z góry, a wyjście do okna nie jest wolniejsze, ponieważ więcej jest rezultatem. Myślę, że to właśnie ma na myśli @Ovidiu. –

1

ustawić selectedIndex listbox do ostatniego elementu, aby przewinąć do dołu

również ograniczyć liczbę elementów w polu listy do czegoś rozsądnego (usuwanie z góry, trzymaj później pozycji), więc nie przeżuwać całą swoją pamięć

1

Poprzednio użyłem pola tekstowego. Dodaj go do formularza, ustaw właściwość Multipline na wartość true, paski przewijania do pionu. I wreszcie dodać następujący kod:

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

Zasadniczo jego dodając swój komentarz do istniejącego tekstu, również dołączenie wysuw. I na koniec zaznaczenie ostatniego fragmentu tekstu o długości = 0. ScrollToCaret zmusza pole tekstowe do przewijania w dół do miejsca, w którym znajduje się kursor (w ostatniej linii)

Mam nadzieję, że to pomoże.

+0

Będę wolniejszy z każdym dodanym wierszem. –

+1

Zrobiłem to. Wydajność jest okropna. –

+3

Osoby, które głosowały na ten temat, w rzeczywistości go nie wypróbowały. –

2

Miałem to dokładne wyzwanie. Rozwiązałem to na dwa różne sposoby, zarówno podczas pracy, jak i pracy pod dużym obciążeniem. Jednym ze sposobów jest ListView. Dodanie wiersza tekstu jest następujące:

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

Innym sposobem jest DataGridView w trybie wirtualnym. Nie mam tego kodu jako poręcznego. Tryb wirtualny jest twoim przyjacielem.

EDYCJA: ponowne czytanie, widzę, że chcesz skopiować/wkleić do pracy. Być może sterowanie RichText wykonuje ok - nie wiem, ale jeśli korzystasz z ListView lub DataGrid, będziesz musiał zrobić więcej kodu, aby skopiować/wkleić do pracy.

4

Robię to w moich programach C# okna (WInforms lub WPF) za pomocą okna konsoli Win32. Mam małą klasę, która otacza niektóre podstawowe interfejsy API Win32, cienkie Tworzę konsolę, gdy program się zaczyna. To tylko przykład: w "prawdziwym życiu" używałbyś ustawienia lub innej rzeczy, aby włączyć konsolę tylko wtedy, gdy jej potrzebujesz.

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

Nie można po prostu trzymać dodawania elementów do rejestrowania kontroli WinForms (ListBox lub RichTextBox) - to w końcu zapychać i rozpoczęcie wymiany na dysku.

Miałem dokładnie ten błąd w jednym punkcie.Rozwiązaniem, które miałem, było od czasu do czasu obcinanie listy wyświetlanych wiadomości. W Pseudokod, to jest coś takiego:

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 powinien być znacznie większy niż ListMinSize, więc obcinanie nie zdarza się zbyt często. ListMinSize to liczba ostatnich wiadomości, które normalnie trzeba przejrzeć na liście rejestrowania.

To tylko pseudokod, w rzeczywistości nie ma RemoveRange w kolekcji przedmiotów ListBox (ale jest na liście). Możesz znaleźć dokładny kod.

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