设为首页 加入收藏

TOP

【WPF】实现类似QQ聊天消息的界面(一)
2019-09-17 18:02:12 】 浏览:64
Tags:WPF 实现 类似 聊天 消息 界面

最近公司有个项目,是要求实现类似 QQ 聊天这种功能的。

如下图

Snipaste_2019-02-19_19-33-22

这没啥难的,稍微复杂的也就表情的解析而已。

表情在传输过程中的实现参考了新浪微博,采用半角中括号代表表情的方式。例如:“abc[doge]def”就会显示 abc,然后一个2018new_doge02_org,再 def。

于是动手就干。

 

创建一个模板控件来进行封装,我就叫它 ChatMessageControl,有一个属性 Text,表示消息内容。内部使用一个 TextBlock 来实现。

于是博主三下五除二就写出了以下代码:

C#

[TemplatePart(Name = TextBlockTemplateName, Type = typeof(TextBlock))]
public class ChatMessageControl : Control
{
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register(nameof(Text), typeof(string), typeof(ChatMessageControl), new PropertyMetadata(default(string), OnTextChanged));

    private const string TextBlockTemplateName = "PART_TextBlock";

    private static readonly Dictionary<string, string> Emotions = new Dictionary<string, string>
    {
        ["doge"] = "pack://application:,,,/WpfQQChat;component/Images/doge.png",
        ["喵喵"] = "pack://application:,,,/WpfQQChat;component/Images/喵喵.png"
    };

    private TextBlock _textBlock;

    static ChatMessageControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ChatMessageControl), new FrameworkPropertyMetadata(typeof(ChatMessageControl)));
    }

    public string Text
    {
        get => (string)GetValue(TextProperty);
        set => SetValue(TextProperty, value);
    }

    public override void OnApplyTemplate()
    {
        _textBlock = (TextBlock)GetTemplateChild(TextBlockTemplateName);

        UpdateVisual();
    }

    private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var obj = (ChatMessageControl)d;

        obj.UpdateVisual();
    }

    private void UpdateVisual()
    {
        if (_textBlock == null)
        {
            return;
        }

        _textBlock.Inlines.Clear();

        var buffer = new StringBuilder();
        foreach (var c in Text)
        {
            switch (c)
            {
                case '[':
                    _textBlock.Inlines.Add(buffer.ToString());
                    buffer.Clear();
                    buffer.Append(c);
                    break;

                case ']':
                    var current = buffer.ToString();
                    if (current.StartsWith("["))
                    {
                        var emotionName = current.Substring(1);
                        if (Emotions.ContainsKey(emotionName))
                        {
                            var image = new Image
                            {
                                Width = 16,
                                Height = 16,
                                Source = new BitmapImage(new Uri(Emotions[emotionName]))
                            };
                            _textBlock.Inlines.Add(new InlineUIContainer(image));

                            buffer.Clear();
                            continue;
                        }
                    }

                    buffer.Append(c);
                    _textBlock.Inlines.Add(buffer.ToString());
                    buffer.Clear();
                    break;

                default:
                    buffer.Append(c);
                    break;
            }
        }

        _textBlock.Inlines.Add(buffer.ToString());
    }
}

因为这篇博文只是个演示,这里博主就只放两个表情好了,并且耦合在这个控件里。

XAML

<Style TargetType="local:ChatMessageControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:ChatMessageControl">
                <TextBlock x:Name="PART_TextBlock"
                           TextWrapping="Wrap" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

没啥好说的,就是包了一层而已。

效果:

Snipaste_2019-02-19_20-11-40

自我感觉良好,于是乎博主就提交代码,发了个版本到测试环境了。

 

但是,第二天,测试却给博主提了个 bug。消息无法选择、复制。

17686

在 UWP 里,TextBlock 控件是有 IsTextSelectionEnabled 属性的,然而 WPF 并没有。这下头大了,于是博主去查了一下 StackOverflow,大佬们回答都是说用一个 IsReadOnly 为 True 的 TextBox 来实现。因为我这里包含了表情,所以用 RichTextBox 来实现吧。不管行不行,先试试再说。

在原来的代码上修改一下,反正表情解析一样的,但这里博主为了方便写 blog,就新开一个控件好了。

C#

首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇纸壳CMS替换默认实现 下一篇针对 Ocelot 网关的性能测试

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目