web-dev-qa-db-ja.com

WPFでコンソールをエミュレートする方法は?

私はこの問題に対するいくつかの正しい方向のヒントまたは準備ができた解決策が欲しいのですが、私はかなり行き詰まっています(私は初心者/中級者です):

アプリケーションにSSHを実装しようとしています。 SSHバックエンドは正常に動作しますが、フロントエンドでスタックしています。コンソールをエミュレートするための適切なソリューションを提供するWPF-Combinationは何ですか?完全なターミナルエミュレーションは別として、コンソールのように見えるものに単にreadline/writelineを作成できれば幸いです:-)

私の最善のアプローチは、単一文字の80x50グリッドであり、4000個の単一セルになり、それは完全にやり過ぎのように感じます。

別のアイデアは、コンソールを作ることでした-Appl。別のプロジェクトのwpfウィンドウにバインドされています。しかし...それは可能であり、どのようにですか?

13
FrankyTheDumb

emulateコンソールが必要な場合は、次のようにします。コマンドを処理して結果を自分で出力する必要があることに注意してください。

page.xaml

<Window x:Class="ConsoleEmulation.MainWindow"
    xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
    Title="MainWindow" MinHeight="350" MinWidth="525" Height="350" Width="525">
    <Grid>
        <ScrollViewer Name="Scroller" Margin="0" Background="Black">
            <StackPanel>
                <ItemsControl ItemsSource="{Binding ConsoleOutput, Mode=OneWay}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Path=.}" Foreground="White" FontFamily="Consolas"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
                <TextBox Text="{Binding ConsoleInput, Mode=TwoWay}" Background="Black" Foreground="White" FontFamily="Consolas" Name="InputBlock" BorderBrush="{x:Null}" SelectionBrush="{x:Null}" />
            </StackPanel>
        </ScrollViewer>
    </Grid>
</Window>

page.xaml.cs

public partial class MainWindow : Window
{
    ConsoleContent dc = new ConsoleContent();

    public MainWindow()
    {
        InitializeComponent();
        DataContext = dc;
        Loaded += MainWindow_Loaded;
    }

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        InputBlock.KeyDown += InputBlock_KeyDown;
        InputBlock.Focus();
    }

    void InputBlock_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            dc.ConsoleInput = InputBlock.Text;
            dc.RunCommand();
            InputBlock.Focus();
            Scroller.ScrollToBottom();
        }
    }
}

public class ConsoleContent : INotifyPropertyChanged
{
    string consoleInput = string.Empty;
    ObservableCollection<string> consoleOutput = new ObservableCollection<string>() { "Console Emulation Sample..." };

    public string ConsoleInput
    {
        get
        {
            return consoleInput;
        }
        set
        {
            consoleInput = value;
            OnPropertyChanged("ConsoleInput");
        }
    }

    public ObservableCollection<string> ConsoleOutput
    {
        get
        {
            return consoleOutput;
        }
        set
        {
            consoleOutput = value;
            OnPropertyChanged("ConsoleOutput");
        }
    }

    public void RunCommand()
    {
        ConsoleOutput.Add(ConsoleInput);
        // do your stuff here.
        ConsoleInput = String.Empty;
    }


    public event PropertyChangedEventHandler PropertyChanged;
    void OnPropertyChanged(string propertyName)
    {
        if (null != PropertyChanged)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
34
ZombieSheep

AllocConsoleを使用して、アプリケーションからコンソールウィンドウを表示できることをご存知ですか?

これは、「デュアルモード」アプリケーションを作成する簡単な方法であり、コンソールまたはWindowsフォームアプリケーションにすることができます。

[DllImport("kernel32")]
static extern bool AllocConsole();

または、これを使用できます:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="30"/>
    </Grid.RowDefinitions>
    <TextBlock Text="Console contents..." HorizontalAlignment="Stretch" VerticalAlignment="Stretch" x:Name="ConsoleTextBlock"/>
    <DockPanel Grid.Row="1">
        <TextBox/>
    </DockPanel>
</Grid>

見栄えを良くするには、TextBlockをListBoxに置き換え、それに応じてItemTemplateのスタイルを設定します。

3
animaonline

自分でやったことはありませんが、「時間があればやる」というプロジェクトのひとつです。したがって、私はまだ既存の実装を探しています:-P

とにかくいくつかの考え:

ビジュアル(省略記号、テキストブロックなど)を使用するアプローチは、おそらく良いアイデアではありません。 200x100の文字が必要な場合は、どうなるか考えてみてください。たぶんバックバッファーですら。すべてをメモリに保持して描画する....信じられないほど遅くなります。

したがって、より良い(または正しい)アプローチは「自分を描く」ことです。 WPFはバックバッファーされており、任意のビットマップを表示したくないため、最も適切なアプローチは、新しいUserControlを作成し、そのPaint-Methodをオーバーライドすることです。 Controlから派生することをお勧めしますが、UserControlにコンテンツがある場合があるため、内部に接続インジケーターアイコンのようなものを表示できます。

アーキテクチャに関しては、コンソールのバッファモデルを保持するdependecyプロパティBufferConsoleBuffer)を作成することをお勧めします。別のDPは、左上の位置を保持します場所long)。表示を開始する場所を決定します(後ろを振り返っている間)。コンソールモデルは、_char[]_と_Color[]_(1次元)を含むクラスを作成します。改行と_\n_文字を使用して行を作成します(これはコンソールの文字であるため)。次に、コントロールのサイズを変更すると、バッファーを再割り当てしなくても、コントロールがリフローします。さまざまなサイズの** ConsoleBuffer **を操作できます(文字の後ろのルックの数が異なります)。

ConsoleBuffer.Write(string s)は、何かを行うためのメソッドです。

行を表すために配列_char[][]_の配列を保持することをお勧めします...しかし、それはプログラミング中に見つけること次第です。

0
Robetto