Got hard. Consider a ViewModel, which consists of a list of objects, where each object defines an int value, and some of these objects also define an ints preset dictionary, packed on a "friendly" line representing this value in the user interface.
Here is an example ...
List<SomeItem> AllItems; public class SomeItem : INotifyPropertyChanged { public SomeItem(int initialValue, Dictionary<int,string> presets) { this.CurrentValue = initialValue; this.Presets = presets; } public int CurrentValue{ get; set; }
The purpose of the user interface is that if the element does not have presets, the user can enter any value that they want. However, if there are presets, we want to limit them to these values, and also display them in the user interface as friendly names from the dictionary.
Our first attempt was to use TextBox and ComboBox, changing their visibility depending on whether there were presets or not, for example ...
<ComboBox ItemsSource="{Binding Presets}" DisplayMemberPath="Key" SelectedValuePath="Value" SelectedValue="{Binding CurrentValue, Mode=TwoWay}" Visibility={Binding HasPresets, Converter=...}"> <TextBox Text="{Binding CurrentValue}" Visibility={Binding HasPresets, Converter...}" /> // Assume the inverse of the combo
... but when we use it in a DataTemplate of a list that supports virtualization, combos sometimes display spaces. I believe because when the item is reused and the DataContext changes, the SelectedValue updates to the ItemsSource, which means there is potentially no predefined value for the match, so the proposed SelectedValue value gets the value from the control and then the ItemsSource, but there is no selected value, therefore shows a space.
My next thought (and what we preferred anyway) was to use only a TextBox that displayed a preset name, but actually tied to Value, and then used the converter to work with its magic and allowed the user to enter either friendly name or actual value. If the user typed something that was not a valid value or preset, we just throw an error. If there were no presets, it will simply act as a cross-cutting indicator.
However, I am not sure how to transfer the presets to the converter. You cannot set the binding on ConverterParameter to pass them that way, and if you use multiple bindings, I’m not sure how to structure the ConvertBack call, because there I also need them to pass, not send back.
I think the correct way is to implement UiValue in the ViewModel, which we would just bind to this ...
<TextBox Text="{Binding UiValue}" />
... then move the code that would be in the converter to the implementation of the getter / setter property or just pass the value if there are no presets. However, it looks like too much logic goes into the ViewModel where it should be in the view (ala a converter or similar.) And again, maybe this is exactly the point of the ViewModel. I do not know. Thoughts are welcome.