Как добавить консольный элемент в программу winforms c # - PullRequest
13 голосов
/ 31 октября 2008

У меня есть программа, которая отслеживает отладочные сообщения, и я попытался использовать TextBox и добавил к нему сообщения, но он не очень хорошо масштабируется и замедляется, когда количество сообщений становится большим. Затем я попробовал ListBox, но при добавлении новых сообщений прокрутка шла вверх. Он также не позволяет вырезать и вставлять, как текстовое поле.

Как лучше реализовать консольный элемент, встроенный в окно winforms.

Edit: Я все еще хотел бы иметь возможность встраивать окно вывода, как в 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");


Ответы [ 7 ]

11 голосов
/ 31 октября 2008

RichTextBox имеет быстрый метод AppendText. И он может хорошо обрабатывать большой текст.
Я считаю, что это лучшее, что вам нужно.

5 голосов
/ 31 октября 2008

Нельзя просто добавлять элементы ведения журнала в элемент управления 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). Вы можете выяснить точный код.

5 голосов
/ 31 октября 2008

Я делаю это в своих программах на C # (WInforms или WPF), используя консольное окно Win32. У меня есть небольшой класс, который обёртывает некоторые базовые интерфейсы 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
3 голосов
/ 31 октября 2008

У меня был именно этот вызов. Я решил это двумя различными способами: и работать, и выполнять волю под большой нагрузкой. Один из способов заключается в ListView. Добавление строки текста выглядит так:

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

Другой способ - использовать DataGridView в виртуальном режиме. У меня нет такого кода под рукой. Виртуальный режим - твой друг.

РЕДАКТИРОВАТЬ: перечитывание, я вижу, вы хотите, чтобы копирование / вставка работала. Может быть, элемент управления RichText работает нормально - не знаю, но если вы используете ListView или DataGrid, вам придется делать больше кодирования, чтобы заставить Copy / Paste работать.

2 голосов
/ 19 февраля 2013
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();
    }
}
1 голос
/ 31 октября 2008

Я ранее использовал текстовое поле. Добавьте его в форму, установите для свойства Multipline значение true, для полос прокрутки - значение Vertical. И, наконец, добавьте следующий код:

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

По сути, это добавление вашего комментария к существующему тексту, а также добавление перевода строки. И, наконец, выбор последнего бита текста длины = 0. ScrollToCaret заставляет текстовое поле прокручиваться вниз до места, где находится курсор (в последней строке)

Надеюсь, это поможет.

1 голос
/ 31 октября 2008

установить выбранный индекс списка в последний элемент, чтобы прокрутить его вниз

также ограничьте количество элементов в списке до чего-то разумного (удалите сверху, сохраните более поздние элементы), чтобы не занимать всю свою память

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...