Display images in TextBlock (WPF) - wpf

I'm working on a simple chat application. Currently the messages are binded to a custom-styled listbox like this (simplified XAML):
<ListBox ItemsSource="{Binding MessageCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now I would like to be able to put images (like graphical smileys) into the displayed message text. Is there any way to achieve this using TextBlock (or any other standart component) or do I need to use some special control for this?
Thanks in advance

Just use the InlineUIContainer.
<TextBlock TextWrapping="Wrap">
<Run>Some text.</Run>
<InlineUIContainer>
<Image Source="http://sstatic.net/stackoverflow/img/apple-touch-icon.png" Height="20"></Image>
</InlineUIContainer>
<Run>Some more text.</Run>
</TextBlock>

The Content of a TextBlock is always just a series of Inlines, so you should use the InlineUIContainer. You can insert this container as one of the Inlines in your TextBlock wherever you want an Image to appear, alternating with text Runs. You could parse a message and at the same time keep adding the tokens (either text or images) that you find to the Inlines collection of the TextBlock.

If you want the Images actually inside the text (like an emoticon), then you are going to have to do some work. This sounds like one of the few times I would actually want a User Control, the point of which would be one that scans the Text looking for emoticon values and building a Data Template on the fly.
Remember that anything you can do in XAML you can do in code, so the code I'm thinking of would follow this general idea:
Scan text for emoticon values and
create a list of values for data
elements.
Create a DockPanel.
Foreach element in the List, add
either a TextBlock or an Image
(based on value).
Set this.Content to the DockPanel.
I think something like this is actually what you are looking for, but if you want just an Image, then the ValueConverter suggestion would work.

You could use a value converter to convert the text to another type which has a list of segments which are composed of either text or the smiley face (in the order in which they appear).
Then, you can use a data template to bind to that new type and display the text and smiley faces appropriately.

I also encountered this problem recently and I overcome this by
Creating an ListBox ItemTemplate containing an ItemsControl that has a WrapPanel in the ItemsPanelTemplate and then binding my string to the ItemsSource of the ItemsControl with a IValueConverter that houses all the logic.
Split out your words and query/search your emoticons strings, hyperlinks etc and create your TextBlock, Image, Hyperlink, Button elements and set your values and event handles.
In the function create a List<UIElement> and populate the List with the controls you have generated and return the List as the object in the Convert function of the IValueConverter.
Because you have the WrapPanel in there you get your wrapping done.

Use the Image element instead of the TextBlock and use a Converter to map the text value to the smile image.
<ListBox ItemsSource="{Binding MessageCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Text, Converter={StaticResource MyImageConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>

Related

Vertical alignment of Label inside TextBlock is wrong

In a WPF popup window that I display to the user I want to put a text that is bound to a variable inside a block of text.
I've tried to do so by putting a <Label> inside a <TextBlock>.
However, this leads to alignment problems, see markup and picture below.
I wonder how I can get the text in the Label to align vertically with the text in the TextBlock, or if there is a better solution?
I could put the entire text in a variable, but then I don't know how to get <Bold> formatting for part of the text.
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap"
VerticalAlignment="Top" Height="99" Width="339" Grid.RowSpan="3">
The user is <Bold>responsible</Bold> for
<Bold><Label Content="00" Padding="0"/></Bold> vehicles
</TextBlock>
You can use the Run inline element for data binding text.
Starting in the .NET Framework 4, the Text property of the Run object is a dependency property, which means that you can bind the Text property to a data source.
It also has properties for styling the text, like FontWeight, no need for <Bold> here.
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap"
VerticalAlignment="Top" Height="99" Width="339" Grid.RowSpan="3">
The user is <Bold>responsible</Bold> for
<Run Text="{Binding YourProperty}" FontWeight="Bold"/> vehicles
</TextBlock>
The result looks like this:
As a general note on the issue: The elements inside TextBlock should be inline elements. Here is an article that has links to other inline elements that could be useful to you, like span that allows for grouping other inline elements and applying styles or to them.
How to: Manipulate Flow Content Elements through the Inlines Property
WPF has two fundamental base types, UIElement and ContentElement. While a TextBlock and Label are UIElements, the content of TextBlock are inlines, which are ContentElements. That is why the alignment is off, they serve very different purposes. You can read up more on the differences and concepts here:
Base Element APIs in WPF Classes
UIElement
UIElement is a base class for WPF core level implementations building on Windows Presentation Foundation (WPF) elements and basic presentation characteristics.
ContentElement
Provides a WPF core-level base class for content elements. Content elements are designed for flow-style presentation, using an intuitive markup-oriented layout model and a deliberately simple object model.
Flow Document Overview

Difference between preview and running program

Sorry for my bad headline but I couldn't figure out a better one. Heck, I don't even know how to properly ask my question. But here it comes.
I have a custom control in WPF, let's call it Cell. This control does have a few dependency properties, one Text to show in the Cell and a few "decorative" properties for background color, foreground color and so on. If I use this Cell control stand-alone, everything works fine so far.
Then I have another custom control inheriting from ItemsControl, let's call it Field. This control should show a text and some Cells. it has some "decorative" properties for the text part as well. The information about showing Cells is given to the control by a DataTemplate, something like
<DataTemplate x:Key="CellTemplate"
DataType="...">
<ns:Cell Text="{Binding Text}"
Background="{Binding ...}" />
</DataTemplate>
...
<ns:Field ItemsSource="{Binding Cells}"
ItemTemplate="{StaticResource CellTemplate}" />
If I use this Field control stand-alone, everything works fine so far.
Now I want to show several Field controls at once. So I put an ItemsControl on my window and gave an ItemTemplate again, something like:
<DataTemplate x:Key="CellTemplate"
DataType="...">
<ns:Cell Text="{Binding Text}"
Background="{Binding ...}" />
</DataTemplate>
<DataTemplate x:Key="FieldTemplate"
DataType="...">
<ns:Field ItemsSource="{Binding Cells}"
ItemTemplate="{StaticResource CellTemplate}"
FieldText="{Binding Text}"
TextBackground="{Binding ...}" />
</DataTemplate>
<ItemsControl ItemsSource="{Binding Fields}"
ItemTemplate="{StaticResource FieldTemplate}" />
As long as I preview my WPF window everything works fine so far. Changing the values of some "decorative" properties at Cell level or at Field level is immediately shown in the preview.
But if I run my program it seems that all "decorative" properties at Cell level are ignored. I can see all my Fields with their respective texts and I can see every Cell in every Field with their respective texts. But all Cells are plain white. All set colors are not shown.
Snoop tells me, that every color is set to Transparent by the ParentTemplate.
Visual Studio doesn't show me any exceptions or any binding errors. So I'm kind of stuck at where or how I can find the error and fix it.
So I wanted to ask you, if you may have a hint for me.
Does this contruction with a DataTemplate containing a DataTemplate and both DataTemplates bind to it's DataContext work?
Or does it have something to do with maybe re-using Brush objects that shouldn't be re-used?
But why is it working in the preview?

Add multi-line text inside ListBox

I have this ListBox:
<ListBox Name="lbAFiles" HorizontalAlignment="Left" Height="100" Margin="318,61,0,0"
VerticalAlignment="Top" Width="481">
<ListBoxItem>
<TextBlock>Line1<LineBreak/>Line2</TextBlock>
</ListBoxItem>
</ListBox>
I have my own object with several properties that i want to fill inside my ListBox instead of the line:
<TextBlock>Line1<LineBreak/>Line2</TextBlock>
How can i do that with code behind ?
lbAFiles.Items.Add("Line 1");
lbAFiles.Items.Add("Line 2");
That said, it would be better to use MVVM and a ViewModel, but you asked how to do it in code behind.
Whatever the object you want to add there, you could pull the properties into the string you're adding.
Another (better) option would be to add this object directly to the ListBox, and use a DataTemplate to give it a "look" (otherwise you will just get the ToString value of that object).
And a much better option is to use data binding.

Variable Binding-Path

I need to display a memory dump for a technical application. Each Byte (Cell) should be defined via a DataTemplate to show additional information (highlight via setting Background color, individual Tooltip etc). I made the following attempt:
<DataTemplate x:Key="HexNumberTemplate">
<Grid>
[...]
<TextBlock>
<TextBlock.Text>
<Binding Path="Cell[0].Value">
<Binding.Converter>
[...]
</Binding.Converter>
</Binding>
</TextBlock.Text>
</TextBlock>
</Grid>
</DataTemplate>
The final result should look like this:
My problem is the fix coded Binding path. 'Cell' is a list of objects that holds all necessary information to display the cell. Using this approach, I need to define 16 times the same DataTemplate with Cell[0] to Cell[15]. I definitely want to avoid this!
I read an approach defining the DataTemplate in source code where I assemble the XAML in a string and call Markup.XamlReader.Load(MemoryStreamOfTheString). But here I lose the comfort of the Visual Studio IDE.
Is it possible to define the DataTemplate in XAML and make the indexer of the Cell-Object a parameter?
You should do like you have read: create templates dynamically, by loading them with XamlReader. In order to have comfort of XAML editor, you can define your template in a separate xaml file like this:
<DataTemplate
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid DataContext="{Binding Current_Cell}">
<!--Your template controls goes here.-->
</Grid>
</DataTemplate>
Then set type of this file to Resource, and load it into string and simply replace Current_Cell with each individual cell numbers before you load template from string when you construct your view.
By setting DataContext of Grid you help yourself to use other binding inside the template (context is already set to the current cell, and you don't need to replace it everywhere).
I was in a the same situation recently, only difference was, that my grid had totally dynamic columns (loaded from server), so I didn't even have the opportunity to create 16 templates :)
Try it with ListBoxes.
The outer ListBox includes the rows which are ListBoxes too, each of them binded to a List object. And you can create the DataTemplate of the ListBoxItems.
<DataTemplate x:Key="innerListBoxItem">
[...]
<TextBlock Text="{Binding Value}" />
[...]
<DataTemplate>
<DataTemplate x:key="outerListBoxItem">
<Grid>
<ListBox ItemTemplate="{StaticResource innerListBoxItem}" ItemCollection="{Binding Cells}"/>
</Grid>
<DataTemplate>
and whereever you want to put this control:
<ListBox ItemTemplate="{StaticResource outerListBoxItem}" ItemCollection={Binding CellsList}"/>
code behind:
public class ListOfCells { public List<Cell> Cells {get; set; } }
public List<ListOfCells> CellsList {get; private set; }
You can try and use the Attached Behavior pattern. You can bind an attached property to the column number, and the attached behavior will bind the text to the required cell given the column number.
I would suggest to use a single column DataGrid with custom Header and Cell templates.
You grid won't benefit from indivudual cells resizing, will it? Your header is going to have a fixed number of columns, you cell template can be implemented as a subclass of ListControl - we just need to change StackPanel's orientation from vertical to horizontal. Then, your bound object will be a collection of bytes, which is easy as your cell control is derived from ListControl.
Please let us know if that makes sense.

Can I databind to properties of a custom class (instantiated in xaml) that then forms the content of a templated listboxitem

Any help on this really appreciated. In summary I'm trying to databind to properties of a custom class instantiated in xaml that then forms the content of a templated listboxitem (phew!).
I have a simple c# class called MenuItem. It has two properties:
- Heading
- Icon
Concentrating on just one of those menu items (i.e. to provide a simple example of where I am stuck) If I do this (with the values hard coded) it works fine:
<ListBox>
<ListBoxItem ContentTemplate="{StaticResource MenuItemTemplate}">
<myclasses:MenuItem Heading="News" IconImage="News.png"/>
</ListBoxItem>
</Listbox>
Where MenuItemTemplate is an appropriate DataTemplate in the resources section binding each property) containing lines such as:
<TextBlock x:Name="tbHeading" Text="{Binding Heading}">
Wheareas when I try to use binding to set the Heading property it falls over (AG_E_PARSER_BAD_PROPERTY_VALUE error)- e.g.:
<ListBox>
<ListBoxItem ContentTemplate="{StaticResource MenuItemTemplate}">
<myclasses:MenuItem Heading="{Binding NewsHeading, Mode=OneWay}" Icon="News.png"/>
</ListBoxItem>
<Listbox>
I've wondered if it is because I'm doing some kind of double binding (i.e. the template is binding to a value on the MenuItem class that needs to be bound) and that's not possible? I've tried having the properties declared as dependency properties but no difference (although I only learned about those today so I may be missing something).
I know I could set the menuitem objects up in the view model, and bind from there, but I would like to understand why the above doesn't work (as for my purposes there are advantages in constructing the menu items in the xaml).
Thank you!!!!
Ian
thanks for sticking with this. I agree the listbox might not be needed - but even if I reduce it to just one item in a contentcontrol:
<ContentControl ContentTemplate="{StaticResource MenuItemTemplate}">
<myclasses:MenuItem Heading="{Binding NewsHeading, Mode=OneWay}" IconImage="News.png"/>
</ContentControl>
I still have the same problem - which is that I can get databinding to work within the content of a contentcontrol (prior to it being presented by the datatemplate referred to in ContentTemplate) using purely xaml.
I.e. the above bit of xaml doesn't work - it throws an error on the bit that binds the NewsHeading:
Heading="{Binding NewsHeading, Mode=OneWay}
So I am trying to understand whether what I'm doing is impossible, or whether it is but I'm doing it wrong.
Thanks.
Assuming that you have multiple MenuItem classes (because you're putting them in a listbox and ti wouldn't make sense to do that if you just had one). You need to bind the collection to the ItemsSource property of the ListBox.
Somehting like this:
<ListBox ItemsSource="{Binding MyMenuItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Heading}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note that the above assumes you've set the DataContext on the page to an object with a property called MyMenuItems which is a collection of your MenuItem objects.
To see a full example of this, look at the default code created when you create a new "Windows Phone Databound Application".
Edit:
Based on your comments, it seems that a ListBox is not the most appropriate solution to your needs. A ListBox is designed/intended to take a collection of items and display them in a list.
If you have a number of different objects which you know about at design time and simply wish to have them one on top of another (giving the appearance of a list) you could simply put them inside a ScrollViewer and/or a StackPanel (or other appropriate container). Plus, you would still be able to databind if you did it this way.

Resources