Data-bound WPF ComboBox not displaying selected value - c#

I have a ComboBox, bound to a DataTable. The ComboBox displays a list of values, pulled from the "wellId" column of the DataTable. The ComboBox is also styled so that I can insert a custom item into the list simply by adding a dummy row to the DataTable with the wellId field set to "(settings)".
<ComboBox IsEditable="True" Name="comboWell" ItemsSource="{Binding}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Content" Value="{Binding wellId}" />
<Style.Triggers>
<DataTrigger Binding="{Binding wellId}" Value="(settings)">
<Setter Property="Content" Value="Customize..." />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
For the most part, this works great. It shows the list, and all items (including the dummy items) are selectable in the drop-down list.
However, after selecting an item from the list, whether it is a real item or a dummy item, the ComboBox doesn't show the selected item properly. Rather than showing the same value displayed in the drop-down list (the "wellId" column from the DataTable), it instead just displays the string "System.Data.DataRowView". No matter what I select, it always displays the same thing.
If I specifically set the DisplayMemberPath on the ComboBox to "wellId", then it displays the selected item properly. However, this messes up all of the other styling I have applied, resulting in the drop-down list being filled with blank entries.
How do I get the ComboBox to display the selected item properly?

Change your ComboBox to set the ItemTemplate instead of the ItemContainerStyle, and remove IsEditable=True. If IsEditable=True then the SelectedItem will get displayed in a TextBox, and if a TextBox.Text is bound to an item, it will display the .ToString() of that item
<ComboBox Name="comboWell" ItemsSource="{Binding }">
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="Content" Value="{Binding wellId}" />
<Style.Triggers>
<DataTrigger Binding="{Binding wellId}" Value="(settings)">
<Setter Property="Content" Value="Customize..." />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Related

How to set bold in ComboBox only in presentation line, not in whole drop down list (WPF)?

I have in my DataGrid ComboBox implementation. Also I have a IsColorCalibFolderBold property by this property ComboBox know when it should represent data in bold style and when no.
So, my property
public bool IsColorCalibFolderBold
{
get { return _IsColorCalibFolderBold; }
set
{
_IsColorCalibFolderBold = value;
OnPropertyChanged();
}
}
and my .xalm
...
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
x:Name="Cb_color_calibration"
SelectionChanged="Cb_color_calibration_SelectionChanged"
ItemsSource="{Binding Path=ColorCalibrationFolders}"
SelectedItem="{Binding Path=SelectedColorCalibrationFolder}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=UIRepresentation}" />
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IsColorCalibFolderBold}"
Value="True">
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
...
And all is works fine here
Text is really bold when property set true and otherwise when false, but problem is here
When I open drop down list, so all items also bold... I need that only representation is bold, but dropdown list itself not.
How to make it?
I believe you need to set the ComboBox.ItemContainerStyle, so as part of the ComboBox definition:
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="FontWeight" Value="Normal"/>
</Style>
</ComboBox.ItemContainerStyle>

StackPanel visibility depending on Combobox selection

I want to have a StackPanel who's visibility should be depending on a Combobox selection. Unfortunatly the XAML below does not work.
I found a solution with a new property which will be set on the PropertyChanged event of the Combobox selection, though I would prefer a strict XAML solution for this.
Any hints on how to solve this?
<StackPanel>
<Label>Picture in Picture function</Label>
<ComboBox Name="cbPictureInPicture" ItemsSource="{Binding Path=PictureInPictureCodeList, Mode=OneWay}" DisplayMemberPath="CodeText"
SelectedValuePath="CodeID" SelectedValue="{Binding Path=PictureInPicture, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cbPictureInPicture, Path=IsSelected.CodeText}" Value="Yes">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Label>Picture in Picture is used</Label>
(...)
</StackPanel>
you may perhaps rewrite the same as
<DataTrigger Binding="{Binding ElementName=cbPictureInPicture, Path=SelectedItem.CodeText}" Value="Yes">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
assuming the combobox is bound to a collection whose item has CodeText property. so SelectedItem.CodeText will point to the same.
additionally it may not be required to set <Setter Property="Visibility" Value="Visible" /> as it is the default value. it does not have any effect in this case just some extra line of code which can be removed.
You can also use a converter and bind directly to the PictureInPicture property:
<StackPanel Visibility="{Binding PictureInPicture, Converter={StaticResource myVisibilityConverter}}"/>
<Label>Picture in Picture is used</Label>
(...)
</StackPanel>
Create flags and pass this flag in stackpanel visibility converter.
On the basis of flag in converter make decision stackpanel visible/hide whatever
Set this flat in comboBox selection change event if selected value as per your requirement.

Datagrid and Listview with same itemssource

I have a WPF application with a DataGrid and ListView that share the same ObservableCollection ItemsSource. When the DataGrid's CanUserAddRows property is True it causes the ListView to display the extra item that the DataGrid uses to add new rows.
How can I get the extra row from the DataGrid to not show in the ListView?
I tried using a trigger on the ListView's DataTemplate and checking if the items Id was empty or 0
`<ListView.ItemTemplate>
<DataTemplate>
<Label Margin="-2,0,0,0" Name="CategoryLabel" >
<TextBlock TextWrapping="Wrap" Text="{Binding categoryName}" Height="46"></TextBlock>
</Label>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding categoryId}" Value="0" > <!-- also tried Value="" -->
<Setter TargetName="CategoryLabel" Property="Visibility" Value="Hidden" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListView.ItemTemplate>`
I just posted an answer to a problem of changing the template using a data template selector
Change View with its ViewModel based on a ViewModel Property
Possibly just because I have recently looked at this but I wonder if it might be possible to use the same technique here.
Have one template for where the category has a value,then another blank template for values without a category. The important part is you do the test in code rather than XAML so easier to inspect.
You can solve your problem without any modification of your ViewModel or code behind. You can do well without explicitly defining CollectionView's of any kind. Just add to your view's XAML one more (or only) DataTrigger that triggers on the NewItemPlaceholder item of the default view of ListView ItemsSource's collection. Have this trigger to set the UIElement.Visibility attached property to "Hidden". Place it within ItemContainerStyle style triggers. Like this:
<ListView
ItemsSource="{Binding ...}"
>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
...
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding}"
Value="{x:Static CollectionView.NewItemPlaceholder}">
<Setter Property="UIElement.Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="..." Value="{Binding ...}" />
...
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Label Margin="..." Name="...">
<TextBlock TextWrapping="Wrap"
Text="{Binding ...}" />
</Label>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>

Updating Values in the Control Template of the ComboBox

I implemented a column in a data grid that containes comboboxes. In order to display a text box in stead of a combobox when a list containes only one value, I used the solution from this post:
How to hide combobox toggle button if there is only one item?
However, when that one value in the list is changed, it is not updated in the text box. I have, of course, implemented INotifyPropertyChanged and it works as long as I have more than one item in the list (in other words, when the combobox is shown) but the value in the TextBlock is never updated.
Edit:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="CList" ItemsSource="{Binding Values, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Path=SelectedValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="0" BorderBrush="Transparent"
Background="Transparent">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}" >
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Items.Count, ElementName=CList}" Value="1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBlock Text="{Binding Items[0], ElementName=CList}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
I can see you are binding to the item itself, instead of any property in it
so perhaps you many need to bind to the respective property of your data item
eg
<TextBlock Text="{Binding Items[0].MyProperty, ElementName=CList}" />
assuming your intended property is MyProperty
Note if there is no underlying property then you'll have to remove the item and add new one again to list in order to update the text block, in this scenario INotifyPropertyChanged will also not work

how to write ParentListBox.SelectedIndex in xaml?

I have a ListBox having ComboBoxes as ListItems. The ComboBox in each ListItem is created using ListBox's ItemTemplate.
Now, suppose I have 5 ListItems. i.e. 5 ComboBoxes.
For the 1st ListItem i.e. 1st ComboBox I would like to have all the Items from database as ItemsSource.
For the 2nd ListItem i.e. 1st ComboBox I would like to have all the Items from database as ItemsSource.
For the 3rd ListItem i.e. 1st ComboBox I would like to have only the selected Items from above Comboboxes as ItemsSource.
For the 4th ListItem i.e. 1st ComboBox I would like to have only the selected Items from above Comboboxes as ItemsSource.
For the 5th ListItem i.e. 1st ComboBox I would like to have only the selected Items from above Comboboxes as ItemsSource.
So, I think I have to use different DataSource for different ComboBoxes.
And for that to happen I have the following starting code:
<ComboBox....>
<ComboBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="ParentListBox.SelectedIndex" Value="0">
<Setter Property="DataSource" Value="{Binding Path=ListCorrespondingToValue1}"/>
</DataTrigger>
<DataTrigger Binding="ParentListBox.SelectedIndex" Value="Non-0">
<Setter Property="DataSource" Value="{Binding Path=ListCorrespondingToValue2}"/>
</DataTrigger>
</Style.Triggers>
</Style>
<ComboBox.Style>
</ComboBox>
Now, my question is what should I use insead of ParentListBox.SelectedIndex in the above code and what should I replace Non-0 with?
You don't use a DataTrigger for the non zero part... you just set it as a Setter in the Style, so that becomes the default value and then the DataTrigger changes the DataSource property only when the value is 0. Try this:
<ComboBox....>
<ComboBox.Style>
<Style>
<Setter Property="DataSource" Value="{Binding
Path=ListCorrespondingToValue2}"/>
<Style.Triggers>
<DataTrigger Binding="ParentListBox.SelectedIndex" Value="0">
<Setter Property="DataSource" Value="{Binding
Path=ListCorrespondingToValue1}"/>
</DataTrigger>
</Style.Triggers>
</Style>
<ComboBox.Style>
</ComboBox>

Categories