15

我想要一些正确方向的提示,甚至是解决这个问题的现成解决方案,但我很困惑(我只是初学者/中级):

我正在尝试在我的应用程序中实现 SSH。SSH 后端工作正常等,但我被困在前端。什么 WPF 组合会为我提供模拟控制台的适当解决方案?抛开一个完整的终端仿真,我很乐意简单地将 readline/writeline 变成一个看起来像控制台的东西:-)

我目前最好的方法是一个 80x50 的单个字符网格,产生 4000 个单个单元格,这感觉就像一个过度杀伤力。

另一个想法是制作一个控制台应用程序。绑定到另一个项目中的 wpf 窗口。但是......这甚至可能吗?如何?

4

3 回答 3

37

鉴于您想模拟控制台,我会这样做。请注意,您必须自己处理命令并输出结果。

页面.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>

页面.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));
    }
}
于 2013-02-19T12:27:05.400 回答
4

您是否知道可以使用 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 的样式。

于 2013-02-19T11:35:59.733 回答
1

我自己没有做过,但这是我的“如果有时间我会做”的项目之一。因此,我仍在寻找现有的实现:-P

无论如何,一些想法:

使用视觉效果(即椭圆、文本块)的方法可能不是一个好主意。想想如果你想要 200x100 字符会发生什么。甚至可能是一个后备缓冲区。将它全部保存在内存中+绘制它......这将非常慢。

因此,更好(甚至是正确)的方法是“画自己”。由于 WPF 是后缓冲的,并且您不想显示任意位图,因此最可能的方法是创建一个新的 UserControl 并覆盖它的 Paint-Method。您可能更喜欢从 Control 派生,但 UserControl 可能有 Content,因此您可以在里面显示类似连接指示器图标的东西。

架构方面,我建议创建一个包含控制台缓冲区模型的依赖属性Buffer ( )。ConsoleBuffer另一个 DP 将持有左上角位置( long)。它决定了从哪里开始显示(当你往后看的时候)。控制台模型我将创建一个包含 achar[]和 a Color[](一维)的类。使用换行符和\n字符来制作线条(因为这是控制台的字符)。然后,如果您调整控件的大小,它将重新流动而无需重新分配缓冲区。您可以使用不同大小的 **ConsoleBuffer**s(用于不同数量的后视字符)。

ConsoleBuffer.Write(string s)是你做事的方法。

Maybe it is advisable to hold arrays of arrays char[][] to represent lines.... but that is up to finding out while programming.

于 2014-08-20T16:49:03.020 回答