Formatting text in a TextBlock - c#

How do I achieve formatting of a text inside a TextBlock control in my WPF application?
e.g.: I would like to have certain words in bold, others in italic, and some in different colors, like this example:
The reason behind my question is this actual problem:
lblcolorfrom.Content = "Colour From: " + colourChange.ElementAt(3).Value.ToUpper();
I would like the second part of the string to be bold, and I know that I could use two controls (Labels, TextBlocks, etc.) but I'd rather not, due the vast amount of controls already in use.

You need to use Inlines:
<TextBlock.Inlines>
<Run FontWeight="Bold" FontSize="14" Text="This is WPF TextBlock Example. " />
<Run FontStyle="Italic" Foreground="Red" Text="This is red text. " />
</TextBlock.Inlines>
With binding:
<TextBlock.Inlines>
<Run FontWeight="Bold" FontSize="14" Text="{Binding BoldText}" />
<Run FontStyle="Italic" Foreground="Red" Text="{Binding ItalicText}" />
</TextBlock.Inlines>
You can also bind the other properties:
<TextBlock.Inlines>
<Run FontWeight="{Binding Weight}"
FontSize="{Binding Size}"
Text="{Binding LineOne}" />
<Run FontStyle="{Binding Style}"
Foreground="Binding Colour}"
Text="{Binding LineTwo}" />
</TextBlock.Inlines>
You can bind through converters if you have bold as a boolean (say).

You can do this in XAML easily enough:
<TextBlock>
Hello <Bold>my</Bold> faithful <Underline>computer</Underline>.<Italic>You rock!</Italic>
</TextBlock>

There are various Inline elements that can help you, for the simplest formatting options you can use Bold, Italic and Underline:
<TextBlock>
Sample text with <Bold>bold</Bold>, <Italic>italic</Italic> and <Underline>underlined</Underline> words.
</TextBlock>
I think it is worth noting, that those elements are in fact just shorthands for Span elements with various properties set (i.e.: for Bold, the FontWeight property is set to FontWeights.Bold).
This brings us to our next option: the aforementioned Span element.
You can achieve the same effects with this element as above, but you are granted even more possibilities; you can set (among others) the Foreground or the Background properties:
<TextBlock>
Sample text with <Span FontWeight="Bold">bold</Span>, <Span FontStyle="Italic">italic</Span> and <Span TextDecorations="Underline">underlined</Span> words. <Span Foreground="Blue">Coloring</Span> <Span Foreground="Red">is</Span> <Span Background="Cyan">also</Span> <Span Foreground="Silver">possible</Span>.
</TextBlock>
The Span element may also contain other elements like this:
<TextBlock>
<Span FontStyle="Italic">Italic <Span Background="Yellow">text</Span> with some <Span Foreground="Blue">coloring</Span>.</Span>
</TextBlock>
There is another element, which is quite similar to Span, it is called Run. The Run cannot contain other inline elements while the Span can, but you can easily bind a variable to the Run's Text property:
<TextBlock>
Username: <Run FontWeight="Bold" Text="{Binding UserName}"/>
</TextBlock>
Also, you can do the whole formatting from code-behind if you prefer:
TextBlock tb = new TextBlock();
tb.Inlines.Add("Sample text with ");
tb.Inlines.Add(new Run("bold") { FontWeight = FontWeights.Bold });
tb.Inlines.Add(", ");
tb.Inlines.Add(new Run("italic ") { FontStyle = FontStyles.Italic });
tb.Inlines.Add("and ");
tb.Inlines.Add(new Run("underlined") { TextDecorations = TextDecorations.Underline });
tb.Inlines.Add("words.");

Check out this example from Charles Petzolds Bool Application = Code + markup
//----------------------------------------------
// FormatTheText.cs (c) 2006 by Charles Petzold
//----------------------------------------------
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Documents;
namespace Petzold.FormatTheText
{
class FormatTheText : Window
{
[STAThread]
public static void Main()
{
Application app = new Application();
app.Run(new FormatTheText());
}
public FormatTheText()
{
Title = "Format the Text";
TextBlock txt = new TextBlock();
txt.FontSize = 32; // 24 points
txt.Inlines.Add("This is some ");
txt.Inlines.Add(new Italic(new Run("italic")));
txt.Inlines.Add(" text, and this is some ");
txt.Inlines.Add(new Bold(new Run("bold")));
txt.Inlines.Add(" text, and let's cap it off with some ");
txt.Inlines.Add(new Bold(new Italic (new Run("bold italic"))));
txt.Inlines.Add(" text.");
txt.TextWrapping = TextWrapping.Wrap;
Content = txt;
}
}
}

a good site, with good explanations:
http://www.wpf-tutorial.com/basic-controls/the-textblock-control-inline-formatting/
here the author gives you good examples for what you are looking for! Overal the site is great for research material plus it covers a great deal of options you have in WPF
Edit
There are different methods to format the text. for a basic formatting (the easiest in my opinion):
<TextBlock Margin="10" TextWrapping="Wrap">
TextBlock with <Bold>bold</Bold>, <Italic>italic</Italic> and <Underline>underlined</Underline> text.
</TextBlock>
Example 1 shows basic formatting with Bold Itallic and underscored text.
Following includes the SPAN method, with this you van highlight text:
<TextBlock Margin="10" TextWrapping="Wrap">
This <Span FontWeight="Bold">is</Span> a
<Span Background="Silver" Foreground="Maroon">TextBlock</Span>
with <Span TextDecorations="Underline">several</Span>
<Span FontStyle="Italic">Span</Span> elements,
<Span Foreground="Blue">
using a <Bold>variety</Bold> of <Italic>styles</Italic>
</Span>.
</TextBlock>
Example 2 shows the span function and the different possibilities with it.
For a detailed explanation check the site!
Examples

This is my solution....
<TextBlock TextWrapping="Wrap" Style="{DynamicResource InstructionStyle}">
<Run Text="This wizard will take you through the purge process in the correct order." FontWeight="Bold"></Run>
<LineBreak></LineBreak>
<Run Text="To Begin, select" FontStyle="Italic"></Run>
<Run x:Name="InstructionSection" Text="'REPLACED AT RUNTIME'" FontWeight="Bold"></Run>
<Run Text="from the menu." FontStyle="Italic"></Run>
</TextBlock>
I am learning... so if anyone has thaughts on the above solution please share! :)

Related

Having links in a binding

I'm binding text in LocalizedResources (for various languages) and I need to have links inside that text. Unfortunately when I bind it like this I can't have links in it:
<TextBlock TextWrapping="Wrap" Text="{Binding Path=LocalizedResources.AboutText, Source={StaticResource LocalizedStrings}}"/>
So I basically want to make some of the words in that block of text links. Is that possible?
EDIT: I need something like this
Except I'm binding this text from LocalizedResources as I said
As Romasz suggested, you should use RichTextBox instead of a TextBlock. You can solve the binding there with the following code:
<RichTextBox>
<Paragraph>
<Run Text="{Binding Path=LineFormatted}" />
</Paragraph>
</RichTextBox>
For the hyperlinks, you can go through the whole text using C# and than separate the link from the text (you can use something like --- and than you know that whenever a --- appears in the text, you have a hyperlink).
Once you have a text and a hyperlink, you can add them to the RichTextBox using the following code:
Run myRun = new Run();
myRun.Text = "Displaying text with ";
Hyperlink link = new Hyperlink();
link.Inlines.Add("hyperlink");
link.NavigateUri = new Uri("https://stackoverflow.com/");
link.TargetName = "_blank";
link.Foreground = new SolidColorBrush(Colors.Blue);
Paragraph myParagraph = new Paragraph();
myParagraph.Inlines.Add(myRun);
myParagraph.Inlines.Add(link);
myRun = new Run();
myRun.Text = " and with some text after the link.";
myParagraph.Inlines.Add(myRun);
rtb.Blocks.Add(myParagraph);
Where rtb is the name of my RichTextBox.
Straightforward solution would be as follows:
Split your text into few chunks, for example
. before link 1
. link 1
. before link 2
. link 2
. after link 2
create textblock for every before/after link section and add hyperlink button for links.
You should be able to blend thgether these parts, so that user won't distinguish text and links
Hope this helps
That is the code:
<TextBlock FontSize="20">
<Run x:Uid="one" />
<Run x:Uid="link_description" Foreground="BlueViolet">
<iact:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:OpenHyperlinkAction x:Uid="link" />
</core:EventTriggerBehavior>
</iact:Interaction.Behaviors>
</Run>
<Run x:Uid="two" />
</TextBlock>
Or:
<TextBlock FontSize="20">
<Run x:Uid="one" />
<Hyperlink x:Uid="link" Foreground="BlueViolet" >
<Run x:Uid="link_description" />
</Hyperlink>
<Run x:Uid="two" />
</TextBlock>

Use StringFormat to add a string to a WPF XAML binding

I have a WPF 4 application that contains a TextBlock which has a one-way binding to an integer value (in this case, a temperature in degrees Celsius). The XAML looks like this:
<TextBlock x:Name="textBlockTemperature">
<Run Text="{Binding CelsiusTemp, Mode=OneWay}"/></TextBlock>
This works fine for displaying the actual temperature value but I'd like to format this value so it includes °C instead of just the number (30°C instead of just 30). I've been reading about StringFormat and I've seen several generic examples like this:
// format the bound value as a currency
<TextBlock Text="{Binding Amount, StringFormat={}{0:C}}" />
and
// preface the bound value with a string and format it as a currency
<TextBlock Text="{Binding Amount, StringFormat=Amount: {0:C}}"/>
Unfortunately, none of the examples I've seen have appended a string to the bound value as I'm trying to do. I'm sure it's got to be something simple but I'm not having any luck finding it. Can anyone explain to me how to do that?
Your first example is effectively what you need:
<TextBlock Text="{Binding CelsiusTemp, StringFormat={}{0}°C}" />
Here's an alternative that works well for readability if you have the Binding in the middle of the string or multiple bindings:
<TextBlock>
<Run Text="Temperature is "/>
<Run Text="{Binding CelsiusTemp}"/>
<Run Text="°C"/>
</TextBlock>
<!-- displays: 0°C (32°F)-->
<TextBlock>
<Run Text="{Binding CelsiusTemp}"/>
<Run Text="°C"/>
<Run Text=" ("/>
<Run Text="{Binding Fahrenheit}"/>
<Run Text="°F)"/>
</TextBlock>
Please note that using StringFormat in Bindings only seems to work for "text" properties. Using this for Label.Content will not work
In xaml
<TextBlock Text="{Binding CelsiusTemp}" />
In ViewModel, this way setting the value also works:
public string CelsiusTemp
{
get { return string.Format("{0}°C", _CelsiusTemp); }
set
{
value = value.Replace("°C", "");
_CelsiusTemp = value;
}
}

Why TextElemt class is not derived from Control class?

Consider this Xaml:
<RichTextBox>
<Document>
<Paragraph>
<Run Text="FirstWord " />
<Run Text="SecondWord"/>
</Paragraph >
<Paragraph>
<Run Text="FirstWord " />
<Run Text="SecondWord"/>
</Paragraph >
</Document>
</RichTextBox >
Now, assume that for some reasons I use something like this:
<StackPanel Orientation = "Vertical">
<StackPanel Orientation = "Horizontal"> // as a Paragraph
<TextBlock Text="FirstWord " />
<TextBlock Text="SecondWord"/>
</StackPanel>
<StackPanel Orientation = "Horizontal"> // another Paragraph
<TextBlock Text="FirstWord " />
<TextBlock Text="SecondWord"/>
</StackPanel>
</StackPanel>
A Run or a Paragraph or other TextElements are not Controls and therefore there is no ArrangeOverride or MesureOverride methods for them. My question is why they are not controls? is it because of performance issues or there is not much difference between measuring size, arranging and rendering of a TextElement and a Control? In other words, what is the benefits of the first Xaml over the second one (apart from its appearance)?
Thanks.
They are not controls because they are not separate from the rest of the document. What you are setting inside the document are not child controls, but the content of the document. The rendering is done completely inside the RichTextBox.
The main difference between first and second example is that RichTextBox allows you to create flowing text with parts of text having different formatting. While it is possible to do it by using normal TextBlocks, it would be hard to fine-tune for things like line-breaks and dynamic resizing.

Code equivalent of XAML snippet

I want to know the code equivalent of the part that is inside the TextBlock:
<TextBlock>
Hello
<Run Background="Red">S</Run>
<Run Background="Blue">O</Run>
</TextBlock>
The reason is that I have a converter that returns the TextBox content, but I'm not sure what type to return from the converter. I tried some collection types, that contain the string and the 2 Run instances but that wouldnt work.
Also I noticed that the following wouldnt work:
<TextBlock>
<TextBlock.Text> <--- Added this
Hello
<Run Background="Red">S</Run>
<Run Background="Blue">O</Run>
</TextBlock.Text>
</TextBlock>
So my second question is to which property do I have to bind my converter result?
Firstly, you can add Run blocks via the InLines property, e.g.
TextBlock txtBlock = new TextBlock();
txtBlock.Inlines.Add(new Run { Text = "S", Background = Brushes.Red });
txtBlock.Inlines.Add(new Run { Text = "O", Background = Brushes.Blue });
Secondly, you cannot add via "TextBlock.Text" as this is expecting a string, not a collection of Runs.
Try this:
<Label>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Hello"/>
<TextBlock Background="Red" Text="S"/>
<TextBlock Background="Blue" Text="O"/>
</StackPanel>
</Label>
Add your converter to the binding of each textblocks.I think its more flexible than using the Run

WPF: Cannot set properties on property elements weirdness

private TextBlock _caption = new TextBlock();
public TextBlock Caption
{
get { return _caption; }
set { _caption = value; }
}
<l:CustomPanel>
<l:CustomPanel.Caption Text="Caption text" FontSize="18" Foreground="White" />
</l:CustomPanel>
Gives me the following error:
Cannot set properties on property elements.
If I use:
<l:CustomPanel>
<l:CustomPanel.Caption>
<TextBlock Text="Caption text" FontSize="18" Foreground="White" />
</l:CustomPanel.Caption>
</l:CustomPanel>
My TextBlock shows up fine but it's nested inside another TextBlock like so, it even seems to add itself outside of the Caption property:
<l:CustomPanel>
<l:CustomPanel.Caption>
<TextBlock>
<InlineUIContainer>
<TextBlock Text="Caption text" FontSize="18" Foreground="White" />
</InlineUIContainer>
</TextBlock>
</l:CustomPanel.Caption>
<TextBlock>
<InlineUIContainer>
<TextBlock Text="Caption text" FontSize="18" Foreground="White" />
</InlineUIContainer>
</TextBlock>
</l:CustomPanel>
As you might have already guessed, what i'd like my code to do is to set my Caption property from XAML on a custom panel, if this is possible.
I've also tried the same code with a DependencyProperty to no avail.
So, anyone that can help me with this problem?
I can explain what is going wrong and how to fix it.
First,
<l:CustomPanel>
<l:CustomPanel.Caption Text="Caption text" FontSize="18" Foreground="White" />
is a simple syntax error. The <l:CustomPanel.Caption> syntax does not accept XML attributes - the property value must be within the element.
This is proper property element syntax:
<l:CustomPanel>
<l:CustomPanel.Caption>
<TextBlock Text="Caption text" FontSize="18" Foreground="White" />
</l:CustomPanel.Caption>
</l:CustomPanel>
but:
Property element syntax works only with DependencyProperties (so it didn't work with your CLR property) and
Property element syntax always honors the ContentPropertyAttribute of the property type
Since TextBlock has a [ContentPropertyAttribute("Inlines")], the property element syntax is trying to add the TextBlock to the Inlines collection.
The solution is simple: Declare your property as a DependencyProperty of type UIElement instead of type TextBlock. This has the additional advantage of not restricting the display of content to just a TextBlock. If you really do want to restrict it to just a TextBlock, you can use a validation callback.
public UIElement Content { get { ...
public static readonly DependencyProperty ContentProperty = ...
Just got a non-ideal workaround from a colleague of mine. It involves declaring the Caption property as a resource like:
<Page.Resources>
<TextBlock x:Key="test" Text="Caption text" FontSize="18" Foreground="White" />
</Page.Resources>
<l:CustomPanel Caption="{StaticResource test}" />
I'd still like to know why I can't use the two previous options, so if anyone knows please answer. :)
It seems that you can get this error (in Silverlight 4 and 5 at least) if you specify a namespace on the element. For example:
<Path>
<MapLayer.Position xmlns="clr-namespace:Microsoft.Maps.MapControl">
...
In this case MapLayer.Position is an attached property. It seems that the Silverlight parser requires the namespace to be defined using a prefix:
<Path xmlns:map="clr-namespace:Microsoft.Maps.MapControl">
<map:MapLayer.Position>
...

Categories

Resources