Different font sizes/styles within the same Label/TextBlock

Jellio
02-07-2011, 10:18 AM
I'd like to know if it's possible to have a Label/TextBlock that has two or more different font-sizes/styles/weights within the same label/block.

Something like this:

This is an example of a string


I'm doing this in WPF, if that makes any difference to the solution......

PlausiblyDamp
02-07-2011, 11:21 AM
Something like

<TextBlock>Some text <Run FontSize="42">More text</Run></TextBlock>

do the trick?

AtmaWeapon
02-07-2011, 11:27 AM
I was going to say you can't until you mentioned WPF.

In Windows Forms, it's impossible with the built-in controls save for RichTextBox. The only solution is to either jam multiple controls together or use custom painting.

In WPF, your options are less limited. Label is a content control, which means it can host arbitrary visual content. It happens to have special handling for strings where it auto-wraps them with TextBlock elements. TextBlock isn't really a content control, but there's two ways to put text inside of it. You can use the Text property, which applies the formatting properties of the TextBlock to the entirety of the text. You can also use the Inlines property, which lets you provide formatted text.

"Inlines" refer to elements derived from System.Windows.Documents.Inline (http://msdn.microsoft.com/en-us/library/system.windows.documents.inline.aspx). The ones you'll probably use the most are LineBreak, Run, and Span. The difference is subtle, but it's easiest to say that spans contain multiple runs, and runs are the basic unit of formatting. There's always at least one Span in something that displays text, and all Spans contain one Run.

Enough talk, time to show off. This should please you:

<StackPanel>
<Label>
<TextBlock TextWrapping="Wrap">
<Run FontSize="20">This is at font size 20.</Run>
<Run FontStyle="Italic">This is italic at default size.</Run>
<Run>You can't change formatting within a run.</Run>
</TextBlock>
</Label>
<TextBlock TextWrapping="Wrap">
This text <Bold>has inline bold</Bold> and other <Span FontSize="20">formatting applied.</Span>
You can even <Span FontFamily="Arial">change fonts mid-message.</Span> Technically there is
one span here that contains ~7 child spans.
</TextBlock>
</StackPanel>

Good thing you picked WPF!

Jellio
02-07-2011, 01:15 PM
I was going to say you can't until you mentioned WPF.

In Windows Forms, it's impossible with the built-in controls save for RichTextBox. The only solution is to either jam multiple controls together or use custom painting.

In WPF, your options are less limited. Label is a content control, which means it can host arbitrary visual content. It happens to have special handling for strings where it auto-wraps them with TextBlock elements. TextBlock isn't really a content control, but there's two ways to put text inside of it. You can use the Text property, which applies the formatting properties of the TextBlock to the entirety of the text. You can also use the Inlines property, which lets you provide formatted text.

"Inlines" refer to elements derived from System.Windows.Documents.Inline (http://msdn.microsoft.com/en-us/library/system.windows.documents.inline.aspx). The ones you'll probably use the most are LineBreak, Run, and Span. The difference is subtle, but it's easiest to say that spans contain multiple runs, and runs are the basic unit of formatting. There's always at least one Span in something that displays text, and all Spans contain one Run.

Enough talk, time to show off. This should please you:

<StackPanel>
<Label>
<TextBlock TextWrapping="Wrap">
<Run FontSize="20">This is at font size 20.</Run>
<Run FontStyle="Italic">This is italic at default size.</Run>
<Run>You can't change formatting within a run.</Run>
</TextBlock>
</Label>
<TextBlock TextWrapping="Wrap">
This text <Bold>has inline bold</Bold> and other <Span FontSize="20">formatting applied.</Span>
You can even <Span FontFamily="Arial">change fonts mid-message.</Span> Technically there is
one span here that contains ~7 child spans.
</TextBlock>
</StackPanel>

Good thing you picked WPF!

Thanks for the answer. However, even though I'm using WPF, the TextBlocks/Labels that I would need to use this feature with will be added to my app at run-time, so XAML won't do. I've actually not bothered to learn XAML at all, and have done everything in the code-behind. So, if it's not too much of a hassle, would you care to give me some insight how to do this in vb code?

AtmaWeapon
02-07-2011, 03:17 PM
XAML isn't magic, it's an XML-like syntax for creating objects and setting properties. For example:

<StackPanel>
<TextBlock>Hello</TextBlock>
</StackPanel>

Is equivalent to:

Dim pnl As New StackPanel()
Dim txt As New TextBlock()
txt.Inlines.Add("Hello")
pnl.Children.Add(txt)

Why add an implicit Run to Inlines rather than just set Text? If you look at the TextBlock documentation (http://msdn.microsoft.com/en-us/library/system.windows.controls.textblock.aspx), you'll see ContentPropertyAttribute used to tell XAML that Inlines is the content property for the TextBlock class.

So how do you work with it from code? Well, it ought to be easy. For a type to be usable from XAML it generally has to have a constructorless parameter and properties that can either be directly set or collections with an Add() method. The rest is just looking at documentation.


Dim txt As New TextBlock()
txt.Inlines.Add("This text isn't formatted.")
txt.Inlines.Add(New LineBreak())
txt.Inlines.Add(New Bold(New Run("this is bold...")))
txt.Inlines.Add(New Italic(New Run("this is italic!")))
Dim arialRun As New Run() With {.FontFamily = New FontFamily("arial")}
arialRun.Text = "I'm a run in Arial!"
txt.Inlines.Add(New LineBreak())
txt.Inlines.Add(arialRun)

I have kind of an evil plan; is there anything predictable about how you want to format these runs? An ItemsControl with a good ItemTemplate might do the job if so. If it's not predictable, working with it from code's a bit tedious.

Jellio
02-08-2011, 08:35 AM
XAML isn't magic, it's an XML-like syntax for creating objects and setting properties. For example:

<StackPanel>
<TextBlock>Hello</TextBlock>
</StackPanel>

Is equivalent to:

Dim pnl As New StackPanel()
Dim txt As New TextBlock()
txt.Inlines.Add("Hello")
pnl.Children.Add(txt)

Why add an implicit Run to Inlines rather than just set Text? If you look at the TextBlock documentation (http://msdn.microsoft.com/en-us/library/system.windows.controls.textblock.aspx), you'll see ContentPropertyAttribute used to tell XAML that Inlines is the content property for the TextBlock class.

So how do you work with it from code? Well, it ought to be easy. For a type to be usable from XAML it generally has to have a constructorless parameter and properties that can either be directly set or collections with an Add() method. The rest is just looking at documentation.


Dim txt As New TextBlock()
txt.Inlines.Add("This text isn't formatted.")
txt.Inlines.Add(New LineBreak())
txt.Inlines.Add(New Bold(New Run("this is bold...")))
txt.Inlines.Add(New Italic(New Run("this is italic!")))
Dim arialRun As New Run() With {.FontFamily = New FontFamily("arial")}
arialRun.Text = "I'm a run in Arial!"
txt.Inlines.Add(New LineBreak())
txt.Inlines.Add(arialRun)



Thanks!

I have kind of an evil plan; is there anything predictable about how you want to format these runs? An ItemsControl with a good ItemTemplate might do the job if so. If it's not predictable, working with it from code's a bit tedious.

I'm doing a football management game, and the game will have a "News" page that shows news stories about what is happening in the game-universe. I need the different styles just to make the stories look more pleasing to the eye, for instance bolding/italicising player/team names or having headlines in the middle of the story that are larger than the rest of the text.

Now that I know about this Inlines stuff, it should be easy to figure out how to do it, or actually, I have already figured out how to do it.....

Thanks for the help!


I have another, probably more difficult question: I'd love it if the user was able to click on the players name in the news story in order to get to that players profile page, just like you would do on a website. Is it in any way possible to do, so that the app would understand when the mouse is on the text "Peter Peterson", and when it's on the text "John Johnson"? This isn't a must feature for the game, as I can just add links to all the players covered in the story at the bottom of the page, but it would be cool if you could do it like you do on websites.

AtmaWeapon
02-08-2011, 12:20 PM
Are you digging in the documentation at all?

If you're writing a navigation application (which is probably for the best if you want the features you're talking about), you can use the Hyperlink (http://msdn.microsoft.com/en-us/library/system.windows.documents.hyperlink.aspx) class; it's a Span that serves as a hyperlink in a navigation application. I've had bad luck with trying to use these outside of navigation applications, but you might try handling the Click event to see what happens.

Span itself doesn't have a Click event, but it does support MouseLeftButtonUp and the various other mouse events, I'd be willing to bet you could set up an event handler on your spans/runs.

I'm not sold on an ItemsControl approach just yet, but I can sort of see a way it would work. It'd require some extra effort in formatting the news text, but it wouldn't be tough to pull off. I've got a lot of irons in the fire but I'll see what I can do because it sounds fun.

EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum