New to Telerik UI for WinForms? Download free 30-day trial

How to Display Code Snippets in Chat Messages

Environment

Product Version Product Author
2023.3.1010 RadChat for WinForms Desislava Yordanova

Description

This tutorial demonstrates a sample approach how to display C# code snippets in chat messages by using RadSyntaxEditor.

Code snippet in a Chat Message

how-to-display-code-snippet-in-chat-messages

Depending on the code snippet's language, the proper tagger has to be used to observe the respective syntax highlighting.

Since RadSyntaxEditor is a heavy control with rich functionalities, it is used as a control, and can not be used as an element. However, it is important to note that using controls in visual chat elements may slow down the scrolling and will cause visual glitches as the controls don't support clipping. Please have this in mind as side effects in case you decide to use the demo.

Solution

To achieve the above result, follow the steps:

1. Create a derivative of TextMessageItemElement and replace its ChatMessageBubbleElement with a custom one which hosts a read-only RadSyntaxEditor. Thus, you will benefit from the code formatting that RadSyntaxEditor offers.

2. Then, create a custom Chat Factory and integrate the custom message element as follows:

        public RadForm1()
        {
            InitializeComponent();

            this.radChat1.Author = new Author(Properties.Resources.bot, "Nancy");

            this.radChat1.ChatElement.ChatFactory = new CustomChatFactory();

            Author author2 = new Author(Properties.Resources.bot, "Andrew");
            ChatTextMessage message1 = new ChatTextMessage("Hello", author2, DateTime.Now.AddHours(1));
            this.radChat1.AddMessage(message1);
            ChatTextMessage message2 = new ChatTextMessage("Hi, could you please share the code snippet?", this.radChat1.Author, DateTime.Now.AddHours(1).AddMinutes(10));
            this.radChat1.AddMessage(message2);
            ChatTextMessage message3 = new ChatTextMessage("We would like to announce that in the R2 2018 release " +
                                                           "we introduced Conversational UI", author2, DateTime.Now.AddHours(3));
            this.radChat1.AddMessage(message3);
            string code1 = @"    public class ChatTextMessage : ChatMessage
    {
        private string message;

        public ChatTextMessage(string message, Author author, DateTime timeStamp)
            : this(message, author, timeStamp, null)
        { }

        public ChatTextMessage(string message, Author author, DateTime timeStamp, object userData)
            : base(author, timeStamp, userData)
        {
            this.message = message;
        }

        public string Message
        {
            get { return message; }
            set
            {
                if (this.message != value)
                {
                    this.message = value;

                    this.OnPropertyChanged(""Message"");
                }
            }
        }
    }";
            CodeSnippetChatMessage message4 = new CodeSnippetChatMessage(code1, author2, DateTime.Now.AddHours(3));
            this.radChat1.AddMessage(message4);

            ChatTextMessage message5 = new ChatTextMessage("I used another message type:", this.radChat1.Author, DateTime.Now.AddHours(3));
            this.radChat1.AddMessage(message5);

            string code2 = @"    public class MediaMessageDataItem : BaseChatDataItem
    {
        private ChatMediaMessage message;

        public MediaMessageDataItem(ChatMediaMessage message)
            : base(message)
        {
            this.message = message;
        }

        public ChatMediaMessage MediaMessage
        {
            get { return this.message; }
        }
    }";
            CodeSnippetChatMessage message6 = new CodeSnippetChatMessage(code2, this.radChat1.Author, DateTime.Now.AddHours(3));
            this.radChat1.AddMessage(message6);
        }

        public class CodeSnippetChatMessage : ChatTextMessage
        {
            public CodeSnippetChatMessage(string message, Author author, DateTime timeStamp) : base(message, author, timeStamp)
            {
            }


        }
        public class CustomChatFactory : ChatFactory
        {
            public override BaseChatDataItem CreateDataItem(ChatMessage message)
            {
                CodeSnippetChatMessage codeSnippetMessage = message as CodeSnippetChatMessage;
                if (codeSnippetMessage != null)
                {
                    return new CodeSnippetDataItem(codeSnippetMessage);
                }
                return base.CreateDataItem(message);
            }
            public override BaseChatItemElement CreateItemElement(BaseChatDataItem item)
            {
                if (item.GetType() == typeof(CodeSnippetDataItem))
                {
                    return new CodeMessageItemElement();
                }
                return base.CreateItemElement(item);
            }
        }

        public class CodeSnippetDataItem : TextMessageDataItem
        {
            public CodeSnippetDataItem(ChatTextMessage message) : base(message)
            {
            }
        }

        public class CodeMessageItemElement : TextMessageItemElement
        {
            protected override Type ThemeEffectiveType
            {
                get
                {
                    return typeof(TextMessageItemElement);
                }
            }

            public override bool IsCompatible(BaseChatDataItem data, object context)
            {
                return data.GetType() == typeof(CodeSnippetDataItem);
            }

            protected override LightVisualElement CreateMainMessageElement()
            {
                return new CustomChatMessageBubbleElement();
            }

            public override void Synchronize()
            {
                base.Synchronize();
                CustomChatMessageBubbleElement bubble = this.MainMessageElement as CustomChatMessageBubbleElement;
                bubble.DrawText = false;
                bubble.CodeElement.Document = new Telerik.WinForms.SyntaxEditor.Core.Text.TextDocument(bubble.Text);
                bubble.CodeElement.Text = bubble.Text;
            }
        }

        public class CustomChatMessageBubbleElement : ChatMessageBubbleElement
        {
            protected override Type ThemeEffectiveType
            {
                get
                {
                    return typeof(ChatMessageBubbleElement);
                }
            }

            RadHostItem host;
            RadSyntaxEditor  codeElement;

            public RadSyntaxEditor  CodeElement
            {
                get
                {
                    return this.codeElement;
                }
            }

            protected override void CreateChildElements()
            {
                 base.CreateChildElements();
                codeElement = new RadSyntaxEditor ();
                codeElement.IsWordWrapEnabled = true;
                codeElement.SyntaxEditorElement.IsReadOnly = true;
                CSharpTagger currentLanguageTagger = new CSharpTagger(codeElement.SyntaxEditorElement);
                codeElement.TaggersRegistry.RegisterTagger(currentLanguageTagger);
                host = new RadHostItem(codeElement);
                codeElement.Dock = DockStyle.Fill;
                host.MinSize = new Size(400, 200);
                this.Children.Add(host);
            }



            public override string Text
            {
                get
                {
                    return this.codeElement.Text;
                }
                set
                {
                    base.Text = value;
                    this.codeElement.Text = value;
                }
            }
        }      

    Public Sub New()
        InitializeComponent()
        Me.RadChat1.Author = New Author(My.Resources.bot, "Nancy")
        Me.RadChat1.ChatElement.ChatFactory = New CustomChatFactory()
        Dim author2 As Author = New Author(My.Resources.bot, "Andrew")
        Dim message1 As ChatTextMessage = New ChatTextMessage("Hello", author2, DateTime.Now.AddHours(1))
        Me.RadChat1.AddMessage(message1)
        Dim message2 As ChatTextMessage = New ChatTextMessage("Hi, could you please share the code snippet?", Me.RadChat1.Author, DateTime.Now.AddHours(1).AddMinutes(10))
        Me.RadChat1.AddMessage(message2)
        Dim message3 As ChatTextMessage = New ChatTextMessage("We would like to announce that in the R2 2018 release " & "we introduced Conversational UI", author2, DateTime.Now.AddHours(3))
        Me.RadChat1.AddMessage(message3)
        Dim code1 As String = "    public class ChatTextMessage : ChatMessage
    {
        private string message;

        public ChatTextMessage(string message, Author author, DateTime timeStamp)
            : this(message, author, timeStamp, null)
        { }

        public ChatTextMessage(string message, Author author, DateTime timeStamp, object userData)
            : base(author, timeStamp, userData)
        {
            this.message = message;
        }

        public string Message
        {
            get { return message; }
            set
            {
                if (this.message != value)
                {
                    this.message = value;

                    this.OnPropertyChanged(""Message"");
                }
            }
        }
    }"
        Dim message4 As CodeSnippetChatMessage = New CodeSnippetChatMessage(code1, author2, DateTime.Now.AddHours(3))
        Me.RadChat1.AddMessage(message4)
        Dim message5 As ChatTextMessage = New ChatTextMessage("I used another message type:", Me.RadChat1.Author, DateTime.Now.AddHours(3))
        Me.RadChat1.AddMessage(message5)
        Dim code2 As String = "    public class MediaMessageDataItem : BaseChatDataItem
    {
        private ChatMediaMessage message;

        public MediaMessageDataItem(ChatMediaMessage message)
            : base(message)
        {
            this.message = message;
        }

        public ChatMediaMessage MediaMessage
        {
            get { return this.message; }
        }
    }"
        Dim message6 As CodeSnippetChatMessage = New CodeSnippetChatMessage(code2, Me.RadChat1.Author, DateTime.Now.AddHours(3))
        Me.RadChat1.AddMessage(message6)
    End Sub

    Public Class CodeSnippetChatMessage
        Inherits ChatTextMessage

        Public Sub New(ByVal message As String, ByVal author As Author, ByVal timeStamp As DateTime)
            MyBase.New(message, author, timeStamp)
        End Sub
    End Class

    Public Class CustomChatFactory
        Inherits ChatFactory

        Public Overrides Function CreateDataItem(ByVal message As ChatMessage) As BaseChatDataItem
            Dim codeSnippetMessage As CodeSnippetChatMessage = TryCast(message, CodeSnippetChatMessage)

            If codeSnippetMessage IsNot Nothing Then
                Return New CodeSnippetDataItem(codeSnippetMessage)
            End If

            Return MyBase.CreateDataItem(message)
        End Function

        Public Overrides Function CreateItemElement(ByVal item As BaseChatDataItem) As BaseChatItemElement
            If item.[GetType]() = GetType(CodeSnippetDataItem) Then
                Return New CodeMessageItemElement()
            End If

            Return MyBase.CreateItemElement(item)
        End Function
    End Class

    Public Class CodeSnippetDataItem
        Inherits TextMessageDataItem

        Public Sub New(ByVal message As ChatTextMessage)
            MyBase.New(message)
        End Sub
    End Class

    Public Class CodeMessageItemElement
        Inherits TextMessageItemElement

        Protected Overrides ReadOnly Property ThemeEffectiveType As Type
            Get
                Return GetType(TextMessageItemElement)
            End Get
        End Property

        Public Overrides Function IsCompatible(ByVal data As BaseChatDataItem, ByVal context As Object) As Boolean
            Return data.[GetType]() = GetType(CodeSnippetDataItem)
        End Function

        Protected Overrides Function CreateMainMessageElement() As LightVisualElement
            Return New CustomChatMessageBubbleElement()
        End Function

        Public Overrides Sub Synchronize()
            MyBase.Synchronize()
            Dim bubble As CustomChatMessageBubbleElement = TryCast(Me.MainMessageElement, CustomChatMessageBubbleElement)
            bubble.DrawText = False
            bubble.CodeElement.Document = New Telerik.WinForms.SyntaxEditor.Core.Text.TextDocument(bubble.Text)
            bubble.CodeElement.Text = bubble.Text
        End Sub
    End Class

    Public Class CustomChatMessageBubbleElement
        Inherits ChatMessageBubbleElement

        Protected Overrides ReadOnly Property ThemeEffectiveType As Type
            Get
                Return GetType(ChatMessageBubbleElement)
            End Get
        End Property

        Private host As RadHostItem
        Private _codeElement As RadSyntaxEditor

        Public ReadOnly Property CodeElement As RadSyntaxEditor
            Get
                Return Me._codeElement
            End Get
        End Property

        Protected Overrides Sub CreateChildElements()
            MyBase.CreateChildElements()
            _codeElement = New RadSyntaxEditor()
            _codeElement.IsWordWrapEnabled = True
            _codeElement.SyntaxEditorElement.IsReadOnly = True
            Dim currentLanguageTagger As CSharpTagger = New CSharpTagger(_codeElement.SyntaxEditorElement)
            _codeElement.TaggersRegistry.RegisterTagger(currentLanguageTagger)
            host = New RadHostItem(_codeElement)
            _codeElement.Dock = DockStyle.Fill
            host.MinSize = New Size(400, 200)
            Me.Children.Add(host)
        End Sub

        Public Overrides Property Text As String
            Get
                Return Me._codeElement.Text
            End Get
            Set(ByVal value As String)
                MyBase.Text = value
                Me._codeElement.Text = value
            End Set
        End Property
    End Class

See Also

In this article