I have a combo box marked up as follows.
<ComboBox SelectedIndex="{Binding Path=Bonkey}">
<ComboBoxItem Content="Monkey" />
<ComboBoxItem Content="Donkey" />
</ComboBox>
I'm binding to an object that has Bonkey field types as integer, according to the declaration below.
class Thingy
{
public int Bonkey {get; set; }
...
}
Although it works great and as supposed to, there's a programmatically technical issue keeping me up at night. The indexes generated in the manual mark up are 0 and 1. However, I know that the values of the integers will be 1 and 2. (I.e. Monkey is indexed by 0 when related to combo box item but it's actual value in the object used as data source is 1. Analogously, Monkey has index 1 in the items of the combo box but it corresponds to 2 in the object.)
My intermediate solution was to knock off 1 in the constructor right before setting the data context, followed by a kick up by 1 when the view is being disposed. It's working but I can't really be proud, so to speak.
public SomeDialog(Thingy thingy)
{
InitializeComponent();
thingy.Bonkey--;
DataContext = thingy;
}
...
private void Cancel_Click(object sender, RoutedEventArgs eventArgs)
{
DialogResult = false;
DataContext.Bonkey++;
Close();
}
...
private void Submit_Click(object sender, RoutedEventArgs eventArgs)
{
DataContext.Bonkey++;
...
}
How can I do that more, well... unshamely?
There are a number of questions involving offsets and IValueConverter
implementations. But browsing through them, I don't see one that addresses the specific scenario of binding with an offset; many of the questions involve people who already have the converter working but are having other problems, and others involve scenarios that are in one way or another more complex than this one.
So, here's a very simple offset converter implementation:
class OffsetValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int offset = int.Parse((string)parameter);
return (int)value - offset;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int offset = int.Parse((string)parameter);
return (int)value + offset;
}
}
Used like this:
<ComboBox SelectedIndex="{Binding OffsetValue,
Converter={StaticResource offsetValueConverter1},
ConverterParameter=1}"/>
Where, of course, you have a resource declared to make an instance of the converter available, e.g.:
<Window.Resources>
<l:OffsetValueConverter x:Key="offsetValueConverter1"/>
</Window.Resources>
There are other options for implementation, such as giving the converter instance itself a property to set to control the offset, or specifying the offset as an actual int
value so it didn't have to be parsed, but these approaches have their own limitations, such as not being able to reuse the same instance for different offsets, or requiring a more verbose declaration in XAML, respectively. I think the above strikes a good balance between convenience and efficiency.
See also related questions:
How can I bind one property to another property, offset by a specific amount?
Applying transforms from DataTemplates in WPF
There are others, but these seem the most closely related to your own scenario.