0

I need to have parts of the string in bold. Since TextBlock does not support having parts of the text in bold, I moved to using RichTextBox. Now, I want my RichTextBox to limit to a single line and if the contents are longer to fit in single line, it should use character ellipsis to truncate the string. Following is my ViewModel bound to RichTextBox,

    public class SearchSuggestionViewModel : BindableBase, IComparable<SearchSuggestionViewModel>
    {
    private Suggestion _suggestion;
    private string m_DocumentXaml = string.Empty;


    public SearchSuggestionViewModel(Suggestion suggestion)
        {
        _suggestion = suggestion;
        if (string.IsNullOrEmpty(suggestion.Text))
            return;
        string searchText = _suggestion.Text;
        FlowDocument document = new FlowDocument();
        Paragraph paragraph = new Paragraph();
        Run run = new Run();

        while (searchText.Contains("<b>"))
            {
            string initialText = searchText.Substring(0, searchText.IndexOf("<b>"));
            run.Text = initialText;
            paragraph.Inlines.Add(run);
            searchText = searchText.Substring(searchText.IndexOf("<b>") + "<b>".Length);
            string boldText = searchText;
            if (searchText.Contains("</b>"))
                boldText = searchText.Substring(0, searchText.IndexOf("</b>"));
            run = new Run();
            run.FontWeight = FontWeights.Bold;
            run.Text = boldText;
            paragraph.Inlines.Add(run);

            searchText = searchText.Substring(searchText.IndexOf("</b>") + "</b>".Length);
            }
        run = new Run();
        run.Text = searchText;
        paragraph.Inlines.Add(run);
        document.Blocks.Add(paragraph);
        DocumentXaml = XamlWriter.Save(document);
        }

    public string Id
        {
        get
            {
            return _suggestion.Id;
            }
        }


    public string SearchSuggestionText
        {
        get
            {
            if (!string.IsNullOrWhiteSpace( _suggestion.Text))
                return _suggestion.Text.Replace("<b>", "").Replace("</b>", "");
            return string.Empty;
            }
        }



    /// <summary>
    /// The text from the FsRichTextBox, as a XAML markup string.
    /// </summary>
    public string DocumentXaml
        {
        get
            {
            return m_DocumentXaml;
            }

        set
            {
            SetProperty(ref m_DocumentXaml, value, nameof(DocumentXaml));
            }
        }


    public override int GetHashCode()
        {
        return base.GetHashCode();
        }

    public override bool Equals(object obj)
        {
        if (!(obj is SearchSuggestionViewModel))
            return false;
        SearchSuggestionViewModel otherTag = (SearchSuggestionViewModel)obj;
        if (SearchSuggestionText.Equals(otherTag.SearchSuggestionText))
            return true;
        return false;
        }

    public int CompareTo(SearchSuggestionViewModel compareSearchSuggestionViewModel)
        {
        // A null value means that this object is greater.
        if (compareSearchSuggestionViewModel == null)
            return 1;

        else
            return this.SearchSuggestionText.CompareTo(compareSearchSuggestionViewModel.SearchSuggestionText);
        }
    public override string ToString()
        {
        return _suggestion.Text;
        }
    }

Any suggestions on how can I achieve to have character ellipsis before the line end.

Regards, Umar

Umar
  • 478
  • 3
  • 10
  • *"Since TextBlock does not support having parts of the text in bold"*: TextBlock has a [TextBlock.Inlines](https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.textblock.inlines?view=windowsdesktop-5.0) property which you can use to compose the displayed text of Run and Paragraph elements similar to a RichTextBox. So, if text input is not your concern you can use some more complex text formatting with the TextBlock too. – BionicCode Nov 17 '21 at 18:55
  • @Umar: See [Formatting text in a TextBlock](https://stackoverflow.com/questions/5263055/formatting-text-in-a-textblock/5263094) – Jackdaw Nov 17 '21 at 19:03

2 Answers2

1

You should be able to use a TextBlock with text wrapping and text trimming:

<TextBlock TextWrapping="WrapWithOverflow" TextTrimming="CharacterEllipsis">
    <Run>This text is</Run>
    <Run FontWeight="Bold" Text="partly bold"/>
    <Run>and wraps.</Run>
</TextBlock>

If you are creating the TextBlock programmatically, it has an Inlines property to which you can add the Run elements.

mm8
  • 163,881
  • 10
  • 57
  • 88
0

With thanks to mm8, his answer gave me a direction. But I needed a solution to bind to a collection from the ViewModel. And the number of bold areas in string are decided at runtime. So, I have created a dependency property. And following worked for me. Sharing for benefit of others in future.

TextBlockExtensions.cs

public class TextBlockExtensions
{
    public static IEnumerable<Inline> GetBindableInlines(DependencyObject obj)
    {
        return (IEnumerable<Inline>)obj.GetValue(BindableInlinesProperty);
    }

    public static void SetBindableInlines(DependencyObject obj, IEnumerable<Inline> value)
    {
        obj.SetValue(BindableInlinesProperty, value);
    }

    public static readonly DependencyProperty BindableInlinesProperty =
        DependencyProperty.RegisterAttached("BindableInlines", typeof(IEnumerable<Inline>), typeof(TextBlockExtensions), new PropertyMetadata(null, OnBindableInlinesChanged));

    private static void OnBindableInlinesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var Target = d as TextBlock;

        if (Target != null)
        {
            Target.Inlines.Clear();
            Target.Inlines.AddRange((System.Collections.IEnumerable)e.NewValue);
        }
    }
}

In my ViewModel, I added,

    private ObservableCollection<Inline> _searchSuggestionInlines;
    public ObservableCollection<Inline> SearchSuggestionInlines
    {
        get
        {
            return _searchSuggestionInlines;
        }
        set
        {
            SetProperty(ref _searchSuggestionInlines, value, nameof(SearchSuggestionInlines));
        }
    }

    //To populate the inlines
    public SearchSuggestionViewModel(Suggestion suggestion)
        {
        _suggestion = suggestion;
        if (string.IsNullOrEmpty(suggestion.Text))
            return;
        string searchText = _suggestion.Text;

        ObservableCollection<Inline> inlines = new ObservableCollection<Inline>();

        Run run = new();
        while (searchText.Contains("<b>"))
            {
            string initialText = searchText.Substring(0, searchText.IndexOf("<b>"));
            run.Text = initialText;
            inlines.Add(run);
            searchText = searchText.Substring(searchText.IndexOf("<b>") + "<b>".Length);
            string boldText = searchText;
            if (searchText.Contains("</b>"))
                boldText = searchText.Substring(0, searchText.IndexOf("</b>"));
            run = new Run
            {
                FontWeight = FontWeights.Bold,
                Text = boldText
            };
            inlines.Add(run);
            searchText = searchText.Substring(searchText.IndexOf("</b>") + "</b>".Length);
            }
        run = new Run
        {
            Text = searchText
        };
        inlines.Add(run);
        SearchSuggestionInlines = inlines;
        }

While the following was added to View,

<TextBlock controls:TextBlockExtensions.BindableInlines="{Binding Path=SearchSuggestionInlines, Mode=OneWay}" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" ToolTip="{Binding Path=SearchSuggestionText, Mode=OneWay}"/>

Regards,
Umar

Umar
  • 478
  • 3
  • 10