0

I'm using Caliburn Micro, and have a ListBox that is working to display a list of items I have bound to it from my ViewModel.

However, I am now looking into how to dynamically format each ListItem i.e. in my case, each string can have up to N values that are between two tokens, and I need these to be formatted to bold.

I'm able to parse the text into a list, and then mark which word or chunk of words need to be formatted and which do not, however I am not sure how to show this in my ListBox. From looking around, I see that some people recommend using a converter that would parse the text string?

My XAML at the moment for the listbox looks like - this was working when there was only a single keyword I needed to format, but have since found out there can be multiple occurrences in a single string so am unable to predict the structure anymore.

<ListBox ItemsSource="{Binding HighlightList}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock>
                <Run Text="{Binding Path=mStartText}"/>
                <Run Text="{Binding Path=mBoldText}"/>
                <Run Text="{Binding Path=mEndText}"/>
            </TextBlock>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Has anyone else been able to achieve something similar? Thanks in advance for any hints!

EDIT:

So, for further clarity lets say I have two strings:

String 1 - "This is a Test string"

String 2 - "This is is also a Test string

What I have to do is display the above strings in the ListBox with the strings that are wrapped in EM tags (Italics in the example above) to be bold and red font colour.

What I have done now is create a list of objects that will have two variables each - the string content, and string type (highlighted or not highlighted) and parse each string and break it down so that I mark which parts of it can be printed as is, and which need to be printed in the highlighted format.

ViewModel:

    public ObservableCollection<HighlightItem> HighlightList
    {
        get
        {
            return _highlightList;
        }
        set
        {
            _highlightList = value;
            NotifyOfPropertyChange(() => HighlightList);
        }
    }

public void ParseHighlights()
        {
            ObservableCollection<HighlightItem> hlList = new ObservableCollection<HighlightItem>();

            string START_HL_TOKEN = "<em>";
            string END_HL_TOKEN = "</em>";

            if (_documentSnippets.Count > 0)
            {
                foreach (var hlItem in _documentSnippets.Snippets["text"])
                {
                    // Remove tab characters
                    string temp = hlItem.Replace("\t", "");

                    // Remove multiple newline characters and replace with a single one
                    Regex regex = new Regex("(\\n){2,}");

                    string clean_temp = regex.Replace(temp, "\n");
                    string startText = "", hlText = "", endText = "";
                    int start_idx = 0;
                    int end_idx = 0;
                    List<string> stringTokenized = new List<string>();

// THIS BELOW IS PART OF MY NEW APPROACH
                    while(start_idx != clean_temp.Length)
                    {
                        end_idx = clean_temp.IndexOf(START_HL_TOKEN, start_idx);

                        // Highlight token is at the start
                        if(end_idx == 0)
                        {
                            start_idx = end_idx;
                            end_idx = clean_temp.IndexOf(END_HL_TOKEN, start_idx);
                            stringTokenized.Add(clean_temp.Substring(start_idx + 4, end_idx - start_idx));

                            // Move pointer to start of next section, keeping in mind the length of the END token
                            start_idx = end_idx + 4;
                        }
                        else if(end_idx == -1)
                        {
                            end_idx = clean_temp.Length;
                            stringTokenized.Add(clean_temp.Substring(start_idx, end_idx - start_idx));
                            start_idx = end_idx;
                        }
                        else
                        {
                            stringTokenized.Add(clean_temp.Substring(start_idx, end_idx - start_idx));

                            start_idx = end_idx;
                            end_idx = clean_temp.IndexOf(END_HL_TOKEN, start_idx);
                            stringTokenized.Add(clean_temp.Substring(start_idx + 4, end_idx - start_idx));

                            // Move pointer to start of next section
                            start_idx = end_idx + 4;
                        }
                    }
// END OF MY NEW APPROACH

                    // Token is at the beginning of the snippet
                    if (hlItem.IndexOf(START_HL_TOKEN) == 0)
                    {
                        hlText = clean_temp.Substring(clean_temp.IndexOf(START_HL_TOKEN) + START_HL_TOKEN.Length,
                                                   clean_temp.IndexOf(END_HL_TOKEN) - START_HL_TOKEN.Length);

                        endText = clean_temp.Substring(clean_temp.IndexOf(END_HL_TOKEN) + END_HL_TOKEN.Length,
                                                   clean_temp.Length - (clean_temp.IndexOf(END_HL_TOKEN) + END_HL_TOKEN.Length));
                    }
                    // Token is at the end of the snippet
                    else if (clean_temp.IndexOf(END_HL_TOKEN) + END_HL_TOKEN.Length == clean_temp.Length)
                    {
                        startText = clean_temp.Substring(0,
                                                     clean_temp.Length - clean_temp.IndexOf(START_HL_TOKEN));
                        hlText = clean_temp.Substring(clean_temp.IndexOf(START_HL_TOKEN),
                                                  clean_temp.Length - clean_temp.IndexOf(START_HL_TOKEN));
                    }
                    // Token is in the middle of the snippet
                    else
                    {
                        startText = clean_temp.Substring(0,
                                                     clean_temp.IndexOf(START_HL_TOKEN));

                        hlText = clean_temp.Substring(clean_temp.IndexOf(START_HL_TOKEN) + START_HL_TOKEN.Length,
                                                  clean_temp.IndexOf(END_HL_TOKEN) - clean_temp.IndexOf(START_HL_TOKEN) - (END_HL_TOKEN.Length - 1));

                        endText = clean_temp.Substring(clean_temp.IndexOf(END_HL_TOKEN) + END_HL_TOKEN.Length,
                                                   clean_temp.Length - clean_temp.IndexOf(END_HL_TOKEN) - END_HL_TOKEN.Length);
                    }

                    HighlightItem snippet = new HighlightItem(startText, hlText, endText);
                    hlList.Add(snippet);
                }

                HighlightList = hlList;
            }

        }

So in the model above, I have my old approach where I assumed there would only be one tag in the string but when I realised there are more than 1 sometimes, I started a new approach of building a list representation of the string, marking which parts need to printed normally and which need to be printed highlighted.

Also to my naming standards, only started WPF and C# a month or so ago, so not quite privy to all the naming conventions yet, sorry!

chaylins
  • 215
  • 1
  • 11
  • You could use rtf/html instead of plain text for each item. Or have some logic in the view which dynamically generate collection of Run elements. Have you been [here](https://stackoverflow.com/q/5582893/1997232) already? – Sinatr Apr 28 '20 at 15:04
  • 1
    When you say N values, do you mean more properties? Code example of your model would also help us. Also, why do you name your Properties like fields? – XAMlMAX Apr 28 '20 at 15:39
  • Hi, have added an update with more info - hope its not too messy with my two approach combined at the moment! I will look into the RTF control as well! – chaylins Apr 28 '20 at 16:18

1 Answers1

1

Using a custom attached property did the trick for me, following the hint from Sinatr, I found the following article: WPF TextBlock formatting bold from string property

Following that I was able to provide strings with markup embedded in them and they would be printed appropriately! Thanks for the help!

chaylins
  • 215
  • 1
  • 11