Binding Textblock text inside a combobox grid cell - wpf

I'm using a combobox cell inside a datagridview through datatemplate.
I bind an item source and set the DisplayMemberPath , SelectedValuePath and SelectedValue proprieties on the combobox inside
Once a item in the combobox is selected I would like to show the DisplayMemberPath text on the textblock element, I just don't know how to bind it.
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Path=DataContext.PartNumbers, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
DisplayMemberPath="PartNumberDescription"
SelectedValuePath="PartNumberCodeCode"
SelectedValue="{Binding Code}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{How can I bind DisplayMemberPath here?}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
If I use the same bind of SelectedValue, it works and the value is displayed, but I would like to show the description.
<TextBlock Text="{Binding Code}"/>
<!-- It works, but I would like to show the text of the combobox, not the value -->

XAML
<TextBlock Text="{Binding Description}"/>
ViewModel
public string Description {
get {
return PartNumbers.SingleOrDefault(x => x.PartNumberCodeCode == Code)?.PartNumberDescription;
}
}
On property change of Code, notify property change of Description

Related

WPF ComboBox ItemTemplate binding to a string collection

I have a combobox in wpf which is bound to a List<string>. All works well, but now for some reason I need to bind to an item template. The XAML for the combo box is
<ComboBox ItemsSource="{Binding Tracks}" SelectedItem="{Binding SelectedTrack}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding **WhatShouldBeHere**}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
If my data source is a custom collection then binding is easy, I should just pass the property name from custom collection, but as the binding source is a list of string, what should the binding property be?.
It should be
<TextBlock Text="{Binding}"/>
which is equivalent to
<TextBlock Text="{Binding Path=.}"/>
See the Remarks section on the Binding.Path MSDN page for further details.

Silverlight ComboBox SelectionChanged event fires twice

I'm using a ComboBox within a DataGrid. I am using this DataGrid for both "Add" and "Edit". When I change the value of ComboBox in code during "Edit", the SelectionChanged Event gets fired twice. 1st time it assigns the proper value, then 2nd time null is assigned to ComboBox which clears the data I had set previously!!
I can't figure out what exactly I'm doing wrong.
Here's the XAML snippet where I bind the ComboBox to model.
<sdk:DataGridTextColumn x:Name="SlNo" Binding="{Binding SlNo}" Header="sl.no" IsReadOnly="True"/>
<sdk:DataGridTemplateColumn Header="Activity Type">
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox x:Name="ActivityTypeCombo" IsDropDownOpen="True"
ItemsSource="{Binding AvailableActivityTypes}"
SelectionChanged="ActivityTypeSelectionChanged"
SelectedItem="{Binding SelectedActivityType, Mode=TwoWay}"
SelectedValue="{Binding Path=Description, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
The Code Snippet where I set the value is:
foreach (var claimDetailViewModel in Claims)
{
claimDetailViewModel.SelectedActivityType =
_autoFillModel.ActivityTypes.SingleOrDefault(at => at.Id == climDetailViewModel.ActivityTypeId);
}
ClaimDetailsGrid.ItemsSource = Claims;
It seems to me that using both SelectedItem and SelectedValue to control selection will not work properly.
When SelectedItem is set this will most likely trigger SelectedValue to be evaluated, and because there is no SelectedValuePath set it will result in SelectedItem=null.
Try removing the SelectedValue binding.

How to make binding between comboBox item tag

I define Dictionary that contain int as key and string as value Dictionary<int, string >. I made binding between ComboBoxItem and this Dictionary:
<ComboBox ItemsSource="{Binding myDictionary}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}" Tag="{Binding Value}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Now, i want to make a Binding between the item selected tag. How can i do it ?
I know how to do it in case i want to have the SelectedValue of the combo (get the text selected in the combo) ==> but i need the tag and not the text ... :(
You can bind to SelectedItem.Tag
Example:
<TextBlock Text="{Binding ElementName=cmbDictionary,Path=SelectedItem.Tag}"/>

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}"/>

binding combox in wpf datagrid

I have a list that I populate in the init of my viewmodel:
ListOfEmployees = new List<EmployeeBO>(employeeRepository.GetEmployees(true, true));
I am trying to get a combobox in a datagrid to populate from this list.
<DataGridTemplateColumn Header="U/M" MinWidth="145">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox Name="cboUnitMeasure"
ItemsSource="{Binding Path=ListOfUnitMeasures}"
DisplayMemberPath="UnitMeasureDescription" SelectedValuePath="UnitMeasureValue"
SelectedValue="{Binding UnitMeasureValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Width="140" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding UnitMeasureDescription}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
When the dg loads, the cell template displays the UnitMeasureDescription value, but when I click on the cell to edit, there are no items in the combobox. On the other hand, when I use a static resource from an xml file as the itemsource-using the same property names-the combobox contains the items:
<DataGridTemplateColumn Header="U/M" MinWidth="145">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox Name="cboUnitMeasure"
ItemsSource="{Binding Source={StaticResource UnitMeasureData}}"
DisplayMemberPath="UnitMeasureDescription" SelectedValuePath="UnitMeasureValue"
SelectedValue="{Binding UnitMeasureValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Width="140" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding UnitMeasureDescription}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I put a breakpoint just after populating ListOfEmployees in my vm and it contains items. I also verified the property names in the DisplayMemberPath and SelectedValuePath are correct. Not sure what I am doing wrong here.
Is "ListOfUnitMeasures" a property on the VM or a property of an EmployeeBO? Ok, assuming that the DataGrid's ItemsSource is set to the List<EmployeeBO> and that there's another list on the VM called "ListUnitOfMeasures", here's my explanation:
The DataContext of each row in the DataGrid will be equal to the elements in the DataGrid's ItemsSource. In your case, each row will use an EmployeeBO as its DataContext. And since the "ListOfUnitMeasures" isn't a property of Employee BO, the Binding on the ComboBox will not work and thus won't display anything.
One possible solution is change the Binding on your ComboBox to use a RelativeSource pointing back to the parent DataGrid as follows:
<ComboBox Name="cboUnitMeasure"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.ListOfUnitMeasures}"/>

Resources