Search code examples
c#wpflistbox

How to create and present ListboxItems with partially italic text


Here i want to style listbox items in a way, that a little italic comment is added to some items, like shown in the image below.

enter image description here

What brought me close was using a List of Inline Items as a ItemsSource and add there some System.Windows.Documents.Run Objects. I face two challenges with this approach:

  • As of now i was not able to instantiate the Run object in a way to only style part of the text. So either it is all italic or bold or nothing. Its also a mystery how these types could possibly be combined.
  • Using List<Inline> as a ItemsSource overwrites the hover over and item select mechanism of the Listbox. It is not possible anymore to select an item with a single click and hovering over effect is just flickering.

How can i solve these two issues? Or does someboy know a better way?

Here is the small working example:

XAML

<Window x:Class="SandboxListbox.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:ListBoxWithColoredItems"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>
<Grid>
    <ListBox 
        ItemsSource="{Binding Path=Items}"
        SelectedItem="{Binding Path=SelectedItem}"/>
</Grid>
</Window>

ViewModel

namespace SandboxListBox
{
public class ViewModel : INotifyPropertyChanged
{
    public ViewModel()
    {
        Run italic = new Run("Italic text") { FontStyle = FontStyles.Italic };
        Run reg2 = new Run("Another Regular Tet");
        Run reg = new Run("Regular Tet");
        Run fontfamily = new Run("Another font") { FontFamily  = new FontFamily("Comic Sans MS, Verdana") };
        Run big = new Run(" - big") { FontSize = 20 };
        Items =  new List<Inline> { reg, fontfamily,  reg2, italic };
    }
    private List<Inline> items;
    public List<Inline> Items
    {
        get => items;
        set
        {
            items = value;
            OnPropertyChanged();
        }
    }

    private Inline selecteditem;
    public Inline SelectedItem
    {
        get => selecteditem;
        set
        {
            selecteditem = value;
            OnPropertyChanged();
        }
    }
    // INotifyPropertyChanged implementation left out
}}

Solution

  • You would do the text formatting in the ItemTemplate of the ListBox, like

    <ListBox ItemsSource="{Binding Items}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock>
                    <Run Text="{Binding Text, Mode=OneWay}"/>
                    <Run Text="{Binding Comment, Mode=OneWay}" FontStyle="Italic"/>
                </TextBlock>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    

    Use the above with a view model like this:

    public class Item
    {
        public string Text { get; set; }
        public string Comment { get; set; }
    }
    
    public class ViewModel
    {
        public ObservableCollection<Item> Items { get; }
            = new ObservableCollection<Item>();
    }
    

    For details see Data Templating Overview.