The exact question is -- why Convert is used instead of ConvertBack, and why really ConvertBack is needed here in the first place?
Problem
Below is EXAMPLE of my problem, I tried to simplify things. This is plain WPF, no 3rd party libraries, the problem itself is classic master-detail with a little twist.
I have listbox (list of cities) and datagrid (which lists all my friends + telephones). When I select listbox, datagrid should refresh and show the guys from the selected city. The datagrid looks like this:
Name | Phone | PhoneType
Mark | 76447 | cellphone
...
The key issue here is PhoneType column. Each cell should be a combobox filled with predefined phone types (fetched from database). And the twist lies with the db structure. It is something like this:
typeID | PhoneType | PhoneDescription
1 | cellphone | NULL
2 | neighbour | call only in case of emergency
In my datagrid combobox PhoneType should be displayed BUT if PhoneDescription is present, it should be used instead of plain PhoneType.
End of the problem. You with me?
Implementation
In order to have combobox in datagrid I have to use template column with combobox inside, instead of combobox datagrid column (here is why: WPF Datagrid ComboBox DataBinding). So here it is my combobox:
<ComboBox ItemsSource="{Binding Path=PhoneTypesList,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Ok, here (above) I define what should be listed in Combobox dropdown list. A list of PhoneType records (in database sense).
SelectedValuePath="typeID"
SelectedValue="{Binding Path=typeID}">
Combobox should be aware of the current value of phone type of my friend so here (above) is how I bind those values -- on one hand I set that typeID from PhoneType record should be used for matching, and on the other hand I set that typeID from Friend record should be used. This way WPF knows which record of phone types should be used as current value.
(If you are not 100% familiar with this binding, here is nice explanation: Difference between SelectedItem, SelectedValue and SelectedValuePath )
Btw. typeID is used twice because in general I prefer using exactly the same name for related fields (foreign key fields) in database.
I have the list, I have the matching done, now -- for displaying I cannot use just a DisplayMemberPath because I need something more dynamic.
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource PhoneTypeConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
So to display PhoneType, I get entire record and choose appropriate string.
[ValueConversion(typeof(PhoneTypeRecord), typeof(string))]
public class PhoneTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value == null)
return null;
var record = ((PhoneTypeRecord)value); // crash!
return record.PhoneDescription ?? record.PhoneType;
}
// we don't do any conversion back
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return null;
}
}
Error
I compile entire application, I run it, I click on any city. It works as expected, great.
Then I click on ANOTHER city. And the application thinks a bit, and then before city listbox or datagrid is refreshed it crashes with exception (see the marked line above) saying:
Unable to cast object of type 'System.String' to type 'MyBuilderApp.PhoneTypeRecord'.
And with this I have no clue what is going on. Why string is passed to Convert??? It looks more like ConvertBack.