c# - 编写此控件的最佳方法?

标签 c# .net wpf winforms controls

对于我正在编写的聊天客户端,我想创建以下控件:

enter image description here

它应该由三个用户可调整大小的列组成,其中可以显示任意文本,但仍彼此对齐(正如您在 Jeff 所说的那样)。

我已经有一个自定义的 RichTextBox 可以显示预格式化的文本并自动滚动到底部,但是我将如何创建一个具有可调整大小的列的文本框让我感到困惑(我对创建相当新我自己的控件)。

关于寻找什么或一般想法的任何指示?任何帮助表示赞赏!

最佳答案

好的。忘记winforms。它无用、已弃用、丑陋,不允许自定义并且是 Slow as Hell由于缺乏 UI 虚拟化和硬件渲染。

这是我对您所描述内容的看法:

<Window x:Class="MiscSamples.ThreeColumnChatSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MiscSamples"
        Title="ThreeColumnChatSample" Height="300" Width="300">
    <Window.Resources>
        <local:FlowDocumentToXamlConverter x:Key="DocumentConverter"/>
    </Window.Resources>
    <ListView ItemsSource="{Binding}" ScrollViewer.HorizontalScrollBarVisibility="Hidden">
        <ListView.View>
            <GridView>
                <GridView.Columns>
                    <GridViewColumn DisplayMemberBinding="{Binding DateTime}"/>
                    <GridViewColumn DisplayMemberBinding="{Binding Sender}"/>
                    <GridViewColumn>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <FlowDocumentScrollViewer Document="{Binding Content, Converter={StaticResource DocumentConverter}}"
                                                          VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView.Columns>
            </GridView>
        </ListView.View>
    </ListView>
</Window>

代码隐藏:

 public partial class ThreeColumnChatSample : Window
    {
        public ObservableCollection<ChatEntry> LogEntries { get; set; }

        private string TestData = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum";
        private List<string> words;
        private int maxword;
        public Random random { get; set; }

        public ThreeColumnChatSample()
        {
            InitializeComponent();

            random = new Random();
            words = TestData.Split(' ').ToList();
            maxword = words.Count - 1;

            DataContext = LogEntries = new ObservableCollection<ChatEntry>();
            Enumerable.Range(0, 100)
                      .ToList()
                      .ForEach(x => LogEntries.Add(GetRandomEntry()));
        }

        private ChatEntry GetRandomEntry()
        {
            return new ChatEntry()
                {
                    DateTime = DateTime.Now,
                    Sender = words[random.Next(0, maxword)],
                    Content = GetFlowDocumentString(string.Join(" ",Enumerable.Range(5, random.Next(10, 50)).Select(x => words[random.Next(0, maxword)])))
                };
        }

        private string GetFlowDocumentString(string text)
        {
            return "<FlowDocument xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" +
                   "   <Paragraph>" +
                   "     <Run Text='" + text + "'/>" +
                   "   </Paragraph>" +
                   "</FlowDocument>";
        }
    }

数据项:

public class ChatEntry:PropertyChangedBase
{
    public DateTime DateTime { get; set; }

    private string _content;
    public string Content
    {
        get { return _content; }
        set
        {
            _content = value;
            OnPropertyChanged("Content");
        }
    }

    public string Sender { get; set; }
}

PropertyChangedBase(MVVM 辅助类):

public class PropertyChangedBase:INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        Application.Current.Dispatcher.BeginInvoke((Action) (() =>
                                                                 {
                                                                     PropertyChangedEventHandler handler = PropertyChanged;
                                                                     if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
                                                                 }));
    }
}

结果:

enter image description here

  • 我使用了来自 this postFlowDocumentToXAMLConverter
  • 第三列中的丰富内容显示在 FlowDocumentViewer 中,但您可以将其更改为使用链接帖子中的可绑定(bind) RichTextBox
  • 您可以通过点击并拖动标题边缘来调整列的大小。
  • WPF 具有内置的 UI 虚拟化,这意味着如果有很多行,您的应用程序不会严重延迟。
  • 您可以实现描述的解决方案 here在调整包含窗口大小时调整最后一列的大小,从而实现自动换行和分辨率独立性。
  • 请注意,大部分代码隐藏实际上是支持示例的样板(生成随机条目等)。删除它,这将是一个非常干净的解决方案。
  • WPF 摇滚。只需将我的代码(连同链接帖子中的 Converter)复制并粘贴到 File -> New Project -> WPF Application 中,然后亲自查看结果。

编辑:

根据@KingKing 的要求,我修改了示例以模拟聊天客户端。

我从上面链接的 CodeProject 帖子中添加了对 FsRichTextBox.dll 的引用。

<Window x:Class="MiscSamples.ThreeColumnChatSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MiscSamples"
        xmlns:rtb="clr-namespace:FsWpfControls.FsRichTextBox;assembly=FsRichTextBox"
        Title="ThreeColumnChatSample" WindowState="Maximized">
    <Window.Resources>
        <local:FlowDocumentToXamlConverter x:Key="DocumentConverter"/>
    </Window.Resources>
    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="300"/>
        </Grid.RowDefinitions>

        <ListView ItemsSource="{Binding ChatEntries}" ScrollViewer.HorizontalScrollBarVisibility="Hidden"
                  x:Name="ListView">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn DisplayMemberBinding="{Binding DateTime}"/>
                        <GridViewColumn DisplayMemberBinding="{Binding Sender}"/>
                        <GridViewColumn>
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <FlowDocumentScrollViewer Document="{Binding Content, Converter={StaticResource DocumentConverter}}"
                                                          VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>

        <GridSplitter Height="3" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>

        <DockPanel Grid.Row="1">
            <Button Content="Send" DockPanel.Dock="Right" VerticalAlignment="Bottom" Margin="2"
                    Click="Send_Click"/>

            <rtb:FsRichTextBox Document="{Binding UserInput,Converter={StaticResource DocumentConverter}, Mode=TwoWay}"
                           DockPanel.Dock="Bottom" Height="300" x:Name="InputBox"/>
        </DockPanel>
    </Grid>
</Window>

代码隐藏:

public partial class ThreeColumnChatSample : Window
{
    public ChatViewModel ViewModel { get; set; }

    public ThreeColumnChatSample()
    {
        InitializeComponent();

        DataContext = ViewModel = new ChatViewModel();
    }

    private void Send_Click(object sender, RoutedEventArgs e)
    {
        InputBox.UpdateDocumentBindings();

        var entry = ViewModel.AddEntry();

        ListView.ScrollIntoView(entry);
    }
}

View 模型:

public class ChatViewModel:PropertyChangedBase
{
    public ObservableCollection<ChatEntry> ChatEntries { get; set; }
    private string _userInput;
    public string UserInput
    {
        get { return _userInput; }
        set
        {
            _userInput = value;
            OnPropertyChanged("UserInput");
        }
    }

    public string NickName { get; set; }

    public ChatViewModel()
    {
        ChatEntries = new ObservableCollection<ChatEntry>();
        NickName = "John Doe";
    }

    public ChatEntry AddEntry()
    {
        var entry = new ChatEntry {DateTime = DateTime.Now, Sender = NickName};
        entry.Content = UserInput;

        ChatEntries.Add(entry);

        UserInput = null;

        return entry;
    }
}

结果:

enter image description here

关于c# - 编写此控件的最佳方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17391587/

相关文章:

javascript - 从 Jquery load() 编辑的内容回发

c# - UWP 应用程序在 Visual Studio Debug模式下工作,但发布版本不会运行,缺少 DLL (C#)

c# - 如何使用 C# 发送 SNMP 陷阱?

.net - 在 .NET 中读取 UTF8 编码的命令行 (C#)

c# - 是否可以窥视堆栈的表面以下?

c# - 正则表达式拆分和过滤器构造

.net - VB6 COM+ 调用 .Net COM DLL

WPF XAML 样式库

wpf - 将 MouseBindings 添加到数据绑定(bind) WPF ListView 中的项目

c# - WPF自定义效果动画以编程方式