# Displaying tweet from Twitter API using TextBlock Inlines

I'm fetching tweets from the Twitter API and display them in a TextBlock using multiple Inline elements so that I can highlight (and link) entities embedded in the tweet: hashtags, urls and user_mentions. media entities are currently ignored.

Tweets also usually contains emojis. From the API they are returned as two unicode characters. Even though they are displayed properly in the TextBlock, due to the nature of C#, the fact that they consist of (at least) two characters, the indices in the tweets are "off" when trying to locate the entities in the tweet because the indices count emojis as one character, and C# counts them as two.

Because I'm using MVVM, and there's no way of binding to the inlines in a TextBlock, I have created a dependency property that I can bind to. Through an IValueConverter I can bind a tweet to a TextBlock, and the converter creates inlines, which the dependency property puts into the TextBlock.

I have managed to display tweets correctly, but there are quite a few checks and balances to handle when looping through the entities and creating the inlines, so I would really appreciate some input on how (if?) this can be handled more efficiently.

So I present to you, my ValueConverter

public class TweetToTextblockConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
Tweet tweet = value as Tweet;
// Tweet is my wrapper class for tweets from Twitter API.
// It contains the same property values as the API object, but in a
// more properly named fashion

if (tweet == null) return value;

List<Inline> textList = new List<Inline>();

if (tweet.Entities.Any())
{
// Unicode emojis take up two characters in a string, and are counted as two,
// so combine all unicode characters to one character, and get old vs. new
// indices, and use this as a lookup when adding entities
int[] textIndices = StringInfo.ParseCombiningCharacters(tweet.Text);

int prevIndex = 0;

foreach (Entity entity in tweet.Entities.OrderBy(e => e.StartIndex))
{
// Display text between the current and previous entity
// (or start of tweet text)
int length = textIndices[entity.StartIndex] - prevIndex;
if (length < 0) length = 0;
prevIndex = prevIndex > tweet.Text.Length ? tweet.Text.Length : prevIndex;
string subText = tweet.Text.Substring(prevIndex, length);

{
NavigateUri = entity.Uri,
ToolTip = entity.Tooltip,
BaselineAlignment = BaselineAlignment.Center
};

// Save the end index of the entity so that the next run through the
// loop can display the text between this entity and the next one
if (entity.EndIndex > textIndices.Length)
{
prevIndex = textIndices.Length - 1;
}
else
{
prevIndex = textIndices[entity.EndIndex - 1] + 1;
}
}

// Add the last text that will not be added by the above loop
int finalLength = tweet.Text.Length - prevIndex;
if (finalLength < 0) finalLength = 0;
prevIndex = prevIndex > tweet.Text.Length ? tweet.Text.Length : prevIndex;
string finalSubText = tweet.Text.Substring(prevIndex, finalLength);
}
else
{
}

return textList;
}

{
// This will open inline hyperlinks in the user's default browser
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}


There are two code blocks that are basically identical. This

int length = textIndices[entity.StartIndex] - prevIndex;
if (length < 0) length = 0;
prevIndex = prevIndex > tweet.Text.Length ? tweet.Text.Length : prevIndex;
string subText = tweet.Text.Substring(prevIndex, length);


and that

int finalLength = tweet.Text.Length - prevIndex;
if (finalLength < 0) finalLength = 0;
prevIndex = prevIndex > tweet.Text.Length ? tweet.Text.Length : prevIndex;
string finalSubText = tweet.Text.Substring(prevIndex, finalLength);


You should try to encapsulate this logic otherwise if you need to change anything later you'll need to think to do it twice.

Alternatively you can attach the item that you process outside the loop to the entity collection and let the loop do the same job for it too:

tweet.Entities.OrderBy(e => e.StartIndex).Concat(new [] { Entity { ... } })


this way you wouldn't have to copy/paste the repeated logic. And if it doesn't have a link or an URL then it's easier to add an if to check it then duplicating the code.