ComboBox.SelectionBoxItemStringFormat not set correctly - wpf

I am using a ComboBox displaying floats (in Choices) as percent values:
<ComboBox
ItemStringFormat="P0"
IsEditable="True"
IsReadOnly="True"
SelectedItem="{Binding SelectedObject.PrePlanningStatus, Mode=TwoWay, ValidatesOnDataErrors=True, StringFormat=P0}"
ItemsSource="{Binding Choices}" />
The choices are correctly displayed with the percent symbol. However, the SelectedItem is displayed as normal float value without a percent symbol (thus with wrong format).
ItemTemplate has the same behaviour. It seems that the ComboBox.SelectionBoxItemStringFormat property is not set correctly as this value is null at runtime. However this property is readonly. What have I done wrong?

You can use a DataTemplate to specify how items are displayed in a universal way:
<ComboBox IsEditable="True" IsReadOnly="True" SelectedItem="{Binding SelectedObject.CavernDetails.PrePlanningStatus, Mode=TwoWay, ValidatesOnDataErrors=True}" ItemsSource="{Binding Choices}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding StringFormat=P0}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

I found out that using a ComboBox without IsEditable="True" IsReadOnly="True" settings the selected item is displayed within the correct format. For me these settings are also fine.

Related

Multiselect ComboBox, Force it to always display the same text

I have a multiselect combobox that works fine. Except for the text. I want it to always have the same text ("Commodity Filter") regardless of what the user has selected.
If I set iseditable to true and the text to CommodityFilter it looks fine until the user makes a selection, then it is garbage (displays object type name). How can I hard code some text there? (Actually ideally i would databind it so it can change depending on whether anything is selected, but that would be a bonus at this point)
<ComboBox IsEditable="True" Text ="Commodity Filter" ItemsSource="{Binding Path=ActiveCommodities}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}"
Width="20" />
<TextBlock Text="{Binding Commodity}"
Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I ended up creating a custom object for populating the ComboBox (which had the IsSelected property and implemented INotifyPropertyChanged) because I was creating several comboboxes to control filtering. Once I did this is was trivial to override the tostring on the customobject and pass in the appropriate text. So the xaml did not change much.
I would have preferred to overlay with a text box but that seemed to be beyond my abilities to get a polished look in a reasonable time.
<ComboBox ItemsSource="{Binding Path=ActiveFuturesMonths}"
IsEditable="True"
IsReadOnly="True"
Text="Futures Month Filter" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}"
Width="20" />
<TextBlock Text="{Binding Text}"
Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Actually the crux is in setting -
IsEditable="True"
IsReadOnly="True"
Text="Futures Month Filter"
rather than creating custom object. thanks a lot it helped.

Datagrid SelectedItem Multibinding

I need to bind the value of the SelectedItem from the datagrid to:
SelectedItem of a combo box on the same page
Property in the viewmodel
In other words: when I select a row in the datagrid the value in the combobox should change and value of the meant above property should be also set to the value of the selected item of the datagrid.
I tried to use multibinding like this:
<DataGrid.SelectedItem>
<MultiBinding Converter="{StaticResource sapConverter}" >
<Binding Path="SelectedSap" Mode="TwoWay"/>
<Binding ElementName="cbSearchCompanyName" Path="SelectedItem" Mode="OneWay"/>
</MultiBinding>
</DataGrid.SelectedItem>
the SelectedSap here is that property, that I want to update. But when I look at the values() in the converter, value(0) corresponding to the SelectedSap is always Nothing and as a result the property doesn't change as I want.
The binding with the combo works fine.
I try to test it without multibinding. I mean, I don't care about the combo, I'm just changing the value of the property. Like this:
<DataGrid.SelectedItem>
<Binding Path="SelectedSap" Mode="TwoWay"/>
</DataGrid.SelectedItem>
everything works fine.
Where is the trick and how should I implement the functionality I need?
Thank you.
I think there is another good way to accomplish your goals:
<DataGrid Name="dgResults" ItemsSource="{Binding Path=DataGridObj}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
Maybe your binding is wrong.
If you get your items in the grid, your ItemsSource is fine. Use the SelectedValue and set the SelectedValuePath to the column you want the data from.
Skip the multibinding and set the binding on the combobox to be set to the SelectedValue of the DataGrid.
<DataGrid Name="dgResults" ItemsSource="{Binding Path=DataGridObj}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding SelectedValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="ItemNoX"
Š¢hank u a lot! Both your answers gave me a hint. Actually I have to bind three controls together (imagine functionality "search item" - you have a combo "search by item.X", combo "search by item.Y" and a datagrid with items), that's why I was little confused and started with the multibinding. The things are much more easier. Here's my code that now works:
<StackPanel Orientation="Horizontal" Grid.Row="0" >
<Label Content="Search company by name:"/>
<ComboBox MinWidth="200" Width="Auto" Name="cbSearchCompanyName"
ItemsSource="{Binding CompanyList,Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True"
DisplayMemberPath="CompanyName1"
SelectedValuePath="Sap"
SelectedItem="{Binding Path=SelectedSap, Mode=TwoWay}"
SelectedValue="{Binding Path=SelectedSap.Sap, Mode=TwoWay}"/>
<Label Content="by SAP number:" />
<ComboBox MinWidth="200" Width="Auto" Style="{StaticResource marginStyle}" Name="cbSearchCompanySap"
ItemsSource="{Binding CompanyList,Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True"
DisplayMemberPath="Sap"
SelectedValuePath="Sap"
SelectedItem="{Binding Path=SelectedSap, Mode=TwoWay}"
SelectedValue="{Binding Path=SelectedSap.Sap, Mode=TwoWay}"/>
</StackPanel>
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<DataGrid x:Name="CompanyList" AutoGenerateColumns="True"
ItemsSource="{Binding CompanyList,Mode=TwoWay}"
MaxWidth="950" Height="300" Margin="0 2 0 0">
<DataGrid.SelectedItem>
<Binding Path="SelectedSap" Mode="TwoWay"/>
</DataGrid.SelectedItem>
</DataGrid>
</ScrollViewer>

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.

Accessing control between DataGridCells, dynamic cascading ComboBoxes

I have a DataGrid that two of its columns are ComboBoxes (one contains few but not this is the problem).
I want, that when the user changes the first Combo's value, the ComboBox in the other column should bind to a property of its (this property is a collection). Say the First ComboBox is Category, I want that when the user changes its value, the other CB is populated with the values of (first combo's selected category).Vendors.
How should I do it, I don't use MVVM, just simple WPF.
I don't know what should be the right way to implement it, I hope I started it right.
I think, if I could get the other ComboBox (which is located in a different DataGridCell) from the first's SelectionChangeHandler that would be the best, because then I can reset its source on each selection change of the first one.
Note that I have the capability of reaching the current (the first's) DataGridCell, I am just looking for an efficient way to access the right DataGridCell sibling and then get its child (second) combo.
Also note that the selected category should vary from row to row, and the second ComboBox should depend on this row's category.
I actually tried to implement it so that the CollectionViewSource.Source is bound to the current item (i.e. the row's DataContext) but it doesn't seem to work.
I prefer to set the second combo's CollectionViewSource (VendorsCollection) thru an Action trigger or handler at the 1st ComboBox's SelectionChange.
The other ComboBoxes in that field don't seem to make a problem as they're all bound to each other, I might use CollectionViewSource.Filter, anyway it's not a problem to access them as they are simple siblings, not like the first one which is a distant cousin located deep in another DataGridCell.
Here is what is what I tried so far:
<DataGrid>
<DataGrid.Resources>
<CollectionViewSource x:Key="CategoriesCollection" Source="{Binding Context.CategoriesList, Source={x:Static Application.Current}, IsAsync=True}" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Category">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock DataContext="{Binding Category}" Text="{Binding Title}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<!--This is the first ComboBox-->
<ComboBox
IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Source={StaticResource CategoriesCollection}}"
DisplayMemberPath="Title"
SelectionChanged="cbCategories_SelectionChanged"
SelectedItem="{Binding Category}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Style">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock DataContext="{Binding Finish.Style.Vendor}" Text="{Binding Contact.Title}"/>
<TextBlock DataContext="{Binding Finish.Style}" Text="{Binding Title}"/>
<TextBlock Text="{Binding Finish.Title}"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel>
<StackPanel.Resources>
<!--I want, that when the user selects a value in the first ComboBox,
the VendorsCollection below should be populated with the selected Category.Vendors,
or alternatively current row's data item.Category.Vendors,
I just donno how to access current row from these resources.-->
<CollectionViewSource x:Key="VendorsCollection" Source="{Binding Vendors, Source={StaticResource CategoriesCollection}}" />
<CollectionViewSource x:Key="StylesCollection" Source="{Binding Styles, Source={StaticResource VendorsCollection}}" />
<CollectionViewSource x:Key="FinishesCollection" Source="{Binding Finishes, Source={StaticResource StylesCollection}}" />
</StackPanel.Resources>
<ComboBox
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource VendorsCollection}}"
SelectedItem="{Binding Finish.Style.Vendor}"
DisplayMemberPath="Contact.Title"/>
<ComboBox
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource StylesCollection}}"
SelectedItem="{Binding Finish.Style}"
DisplayMemberPath="Title"/>
<ComboBox
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource FinishesCollection}}"
SelectedItem="{Binding Finish}"
DisplayMemberPath="Title"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I just came across your questions. Did you get your problem resolved? I think your question is similar to this one I got. Hope the solution there helps you too.

WPF DataGrid - Problem Customizing a ComboBox

I have a Datagrid with a combo box bound column that works great as follows:
<tk:DataGridComboBoxColumn SelectedValueBinding="{Binding DefaultLocationID}"
SelectedValuePath="LocationID"
DisplayMemberPath="LocationName"
Header="Default Location"
ItemsSource="{Binding Source={StaticResource CustomerLocations}}">
</tk:DataGridComboBoxColumn>
Ultimately I want to customize the dropdown to show 'ID' and 'Name' together so a TemplateColumn seems to be the way to go. Starting small, I can't get the following simple example to work which should replace the standard DataGridComboBoxColumn. The dropdown appears fine, but when I 'select' a dropdown item, it does not accept and goes back to the previous value. I've tried variations on 'Mode=TwoWay' but no luck. Do I need a 'CellEditingTemplate' ?
DefaultLocationID is the foreign-key field being edited, while 'LocationID' is a column in 'CustomerLocations'.
<tk:DataGridTemplateColumn Header="Default Location">
<tk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox VerticalAlignment="Top"
SelectedItem="{Binding LocationID, Mode=TwoWay}"
SelectedValue="{Binding DefaultLocationID, Mode=TwoWay}"
ItemsSource="{Binding Source={StaticResource CustomerLocations}}"
SelectedValuePath="LocationID"
DisplayMemberPath="LocationName" >
</ComboBox>
</DataTemplate>
</tk:DataGridTemplateColumn.CellTemplate>
Thanks!!
Can you post the relevant parts of your CustomerLocations resource? Can you also post the type of class that your grid is binding to?
Try removing the SelectedValuePath, DisplayMemberPath and SelectedValue from the ComboBox.
If you want to display multiple pieces of data in your combobox see the below XAML
<ComboBox ...>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding LocationId}"} />
<TextBlock Text=" - "/>
<TextBlock Text="{Binding LocationName}"} />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Resources