Search code examples
c#wpfbindingmultilinetext-coloring

How to color text in a multiline based control


Environment: WPF, C#

What do I want?: I'd like to display a message log in which the different type of messages (info, warning, error) are shown with a different style.

enter image description here

What's the problem?: Based on the architecture I have given I need to do this with bindings. Currently there is a collection with LogMessages which contain a MessageType (enum). This collection is what I can use to create my binding.

In countless examples I've seen solutions using Run or direct access onto a Rich Text Control. I can't do that.

So is there a way to create this effect using bindings and the WPF given controls? Maybe using Converter? Preferably in a way which allows to just select the text like you do with a regular textbox.

Grateful for any advice

Thanks, Vader

Solution

Even though I wished to implement a kind of overall textbox in which the user could select the different messages just like how you would in a browser, I ended up doing it with a style trigger just like Dairon below suggested.

I'll now make it so that the user can select multiple messages and copy those as string into the cache.

    <ListBox ItemsSource="{Binding Messages}" BorderBrush="Black"

                HorizontalAlignment="Stretch" Margin="10,70,10,0" Height="107" VerticalAlignment="Top">
        <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid Margin="4">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>

                <TextBlock Text="{Binding Time}" FontWeight="DemiBold" />
                <TextBlock Grid.Column="1" Text="{Binding Message}">
                    <TextBlock.Style>
                        <Style TargetType="{x:Type TextBlock}">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Path=MsgType}" Value="Info">
                                    <Setter Property="Foreground" Value="DarkGray" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Path=MsgType}" Value="Warning">
                                    <Setter Property="Foreground" Value="DarkOrange" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Path=MsgType}" Value="Error">
                                    <Setter Property="Foreground" Value="DarkRed" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </TextBlock.Style>
                </TextBlock>

            </Grid>
        </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

Solution

  • I have used the example here, by Federico Berasategui. The example is perfect, you should start from here. And to handle different colors I changed his code to this :

    LogEntry class :

    public class LogEntry
    {
        public LogEntry(DateTime dateTime, int index, string message, Color textColor)
        {
            DateTime = dateTime;
            Index = index;
            Message = message;
            TextColor = new SolidColorBrush(textColor);
            TextColor.Freeze();
        }
    
        public DateTime DateTime { get; set; }
    
        public int Index { get; set; }
    
        public string Message { get; set; }
    
        public SolidColorBrush TextColor { get; set; }
    }
    

    XAML part for LogEntry display :

    <DataTemplate DataType="{x:Type local:LogEntry}">
        <Grid IsSharedSizeScope="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="Index" Width="Auto"/>
                <ColumnDefinition SharedSizeGroup="Date" Width="Auto"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
    
            <TextBlock Text="{Binding DateTime}" Grid.Column="0"
                       FontWeight="Bold" Margin="5,0,5,0"/>
    
            <TextBlock Text="{Binding Index}" Grid.Column="1"
                       FontWeight="Bold" Margin="0,0,2,0" />
    
            <TextBlock Text="{Binding Message}" Grid.Column="2"
                       TextWrapping="Wrap" Foreground="{Binding TextColor}"/>
        </Grid>
    </DataTemplate>
    

    I just added a TextColor property to the LogEntry class and bound it to the Foreground property of the TextBlock.

    Feel free to delete CollapsibleLogEntry parts from the example if you don't need multi-level logs. And of course you can delete DateTime and Index if you just want to handle text messages in your log. You could add your Enum too if you want a MessageType.

    The example is rather complete, but you can make very simple if you don't want all the "extras".