How to set the text of the comboBox items with a converter? - wpf

I have a converter that binds to a collection which elements are of type MyType. I would like to set the text that it is sound withd a converter. So to start with a quick test, I am trying first to set the text of all elements with the text "Hello".
I am trying to use this code:
<ComboBox
DisplayMemberPath="MyProperty"
SelectedItem="{Binding Path=MySelectedItem, Mode=TwoWay}"
ItemsSource="{MyViewModelCollection}">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="TextBlock.Text" Value="Hello"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
However, it is shown the value of the property set in DisplayMemeberPath.
So for the first step I would like to know how to set to all items the text "Hello" but my idea it is to use a converter that takes the comboBoxItem, and according to some checks, returns the text that I want to show.
Thanks.

Instead of DisplayMemberPath, set the ItemTemplate property to an appropriate DataTemplate with a TextBlock:
<ComboBox ItemsSource="{Binding MyViewModelCollection}"
SelectedItem="{Binding MySelectedItem}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding MyProperty,
Converter={StaticResource MyConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Related

How to set ComboBoxItem properties when using ItemsSource and template bindings?

When I created combobox manually it was no problem, but since the combobox is now populated automatically thanks to setting ItemsSource I don't know how to set the properties of each ComboBoxItem. Currently I need to set Selected action for each item (one global value) and Tag (different value per each item).
Currently I only define how the combobox item looks like:
<ComboBox ItemsSource="{Binding Path=Modules}"
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Height="16" Width="16" Source="{Binding ObjectData.ImageSource}" />
<Label Content="{Binding ObjectData.Label}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
How to set the properties I mentioned? Note: those properties are of ComboBoxItem.
Modules is the property of my window class:
public ObservableCollection<SelectableObject<Module>> Modules { get; private set; }
You can set ComboBoxItem properties in the ItemContainerStyle of the ComboBox:
<ComboBox ...>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Tag" Value="{Binding TagValue}"/>
<Setter Property="IsSelected" Value="{Binding Selected}"/>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
When the "global" Selected property is not in the item view model, you'll have set the binding source explicitly, e.g. by RelativeSource/FindAncestor.

How to display too long text properly in WPF ComboBox

I have a ComboBox that shows text of various lengths. For texts that are not long there is not a problem. For the texts longer than the width of ComboBox I would like to trim the text and add "..." (an ellipsis) at the end to show them properly. The bottom line is that I don't want to change the width of the ComboBox. Does anyone know how to do this?
Use a custom ItemTemplate for your ComboBox, which makes use of a TextBlock with the TextTrimming property set to CharacterEllipsis.
Example:
<ComboBox ItemsSource="..." SelectedValuePath="...">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding ...}"
TextTrimming="CharacterEllipsis" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The answer, as Ross said, is to implement a custom ItemTemplate. However, to make it work properly, you need to do the binding properly.
A note on this method: You cannot set both the DisplayMemberPath and the ItemTemplate, it must be one or the other.
So, for the general case where the display member is the item (such as for a string), you can use binding with no properties to bind to the DataContext of the template:
<ComboBox ItemsSource="..." SelectedValuePath="...">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding }" TextTrimming="CharacterEllipsis" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Or, you can put it in a style.
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding }" TextTrimming="CharacterEllipsis" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
For the case where you want to bind to a specific property of the object, similar to how you would use the DisplayMemberPath property, replace the binding with the binding that you would use to a property on the object that you are binding. So, replace the fourth line in my first example with something like this:
<TextBlock Text="{Binding MyDisplayMemberProperty}" TextTrimming="CharacterEllipsis" />
The binding is in the context of a single item of the type bound to your ComboBox. To make this more explicit, you can do the following:
<DataTemplate DataType="{x:Type namespace:MyItemType}">
<!-- My DataTemplate stuff here -->
</DataTemplate>
This will give you hints for the properties on the object while you are writing code inside the DataTemplate.
You can use TextTrimming CharacterEllipsis or WordEllipsis for the textblocks in your combobox.
Also works with a more complex DataTemplate; however, I had to resort to a DockPanel instead of the standard WrapPanel.
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<AccessText DockPanel.Dock="Left" Text="{Binding Icon}"/>
<TextBlock Text="{Binding Name}" TextTrimming="CharacterEllipsis" />
</DockPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Binding in listbox with textblocks not working

I have the following xaml code:
<ListBox Foreground="{Binding MyColor, Converter={local:ColorConverter}}" ItemsSource="{Binding LogCollection, Mode=TwoWay}" Grid.Row="1">
</ListBox>
This changes the foreground color for the entire listbox, so I modified the code in this way:
<ListBox ItemsSource="{Binding LogCollection, Mode=TwoWay}" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="{Binding MyColor, Converter={local:ColorConverter}}" Text="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In this way I wanted to set the foreground for an item instead for the entire listbox, but it is not working. How do I find the right datacontext ? MyColor is a property on my MainViewModel.
LATER EDIT WITH THE SOLUTION
Jens's answer was the one that showed me where I was wrong. Instead of storing simple message log strings in the ObservableCollection, I created a new class (LogItems) which contains a Message and a Color members. Now the LogCollection is typeof LogItems instead of strings.
I populate the listbox with the following code in my viewmodel:
LogItems logitem = new LogItems(myMessage, myColor);
LogCollection.Insert(0, logitem);
And the view has the following form. Also it doesn't require anymore to use RelativeSource, because the datacontext is the same.
<ListBox ItemsSource="{Binding LogCollection, Mode=TwoWay}" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="{Binding Path=Color, Converter={local:ColorConverter}}" Text="{Binding Path=Message}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thank you all for your answers which lead me to this solution.
The DataContext of generated container in a listbox is automatically set to the corresponding item, therefore your Binding does not find the Property MyColor. You need to use a RelativeSource binding to bind to the DataContext of the containing list:
<TextBlock Foreground="{Binding DataContext.MyColor,
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBox}},
Converter={local:ColorConverter}}"
Text="{Binding}"/>

Lookup-id-control in WPF DataGrid

I am working with DataGrid. One column displays text, but the data behind this contains only an id. This id must somehow be converted to a string.
I need something like a combobox with the properties ItemsSource, DisplayMemberPath, SelectedValue and SelectedValuePath. But instead of a button displayed, there must be only a text. Is there some control for that?
That works (would like to exchange combobox with something that looks like textbox):
<DataGridTemplateColumn Header="Leistungsart" MinWidth="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={StaticResource ResourceKey=viewModel}, Path=Leistungsarten}"
DisplayMemberPath="Bezeichnung"
SelectedValue="{Binding Path=BDELeistungsartID, Mode=OneWay, Converter={StaticResource ResourceKey=NullableInt2IntConverter}}"
SelectedValuePath="BDELeistungsartID"
IsEnabled="false"
IsEditable="False"
Height="35">
</ComboBox>
</DataTemplate>
Thanks a lot for your aswer. Yes, that with Template property worked for me:
<ComboBox ItemsSource="{Binding Source={StaticResource ResourceKey=viewModel}, Path=Leistungsarten}"
DisplayMemberPath="Bezeichnung"
SelectedValue="{Binding Path=BDELeistungsartID, Mode=OneWay, Converter={StaticResource ResourceKey=NullableInt2IntConverter}}"
SelectedValuePath="BDELeistungsartID">
<ComboBox.Template>
<ControlTemplate>
<Label Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedItem.Bezeichnung}"
Margin="0,0,0,0" Padding="0,0,0,0"/>
</ControlTemplate>
</ComboBox.Template>
</ComboBox>
You could use a ComboBox, but overwrite the Template property to show just a Label. You'll have to recreate the Click events too.
Easiest way would be to use a tool like Snoop or Blend and see what the default ComboBox template looks like, and modify that to what you want.

Display image in combobox column in datagrid

I'd like to have a combobox in a datagrid to show a list of actual images, instead of text.
I can make this work by manually building a combobox, but cant seem to do this via binding (which is about the only way the datagrid can be used).
I also tried a template column, but got the same results- listing of text showing the name of the image class. Any samples demonstrating this?
Nest as many templates as you need, if your ComboBox shows the class name just set ComboBox.ItemTemplate to do something with your class. Here i assume that MyImageList consists of ImageSource objects (needs some more sizing specifications):
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding MyImageList}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Alternatively you could porbably use a DataGridComboBoxColumn as well, just use the CellStyle to set up a DataTemplate which can display your images:
<DataGridComboBoxColumn ItemsSource="{Binding MyImageList}">
<DataGridComboBoxColumn.CellStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.CellStyle>
</DataGridComboBoxColumn>

Resources