ViewModel vs ValueConverter for compound bindings
I've been using MVVM (or my own flavour thereof) for some time now, but there's still one aspect of it which I would like clarified.
(disclaimer: I've seen some questions similar to this before, but I haven't yet seen anything considering the advantages/disadvantages in changing datatypes specifically for the view. Apologies if I missed something big but every 3rd webpage seems to have "MVVM" on it somewhere =p)
The Issue: Users need to select a duration for an activity. they can either choose a multiple of the default activity length (aka BlockSize), or for those "special occasions", we allow them to overrule this and select from 5 minute intervals.
for the UI, we decided on using a combobox and togglebutton for this. by default, the combobox would display { "1 Block", "2 Blocks", "3 Blocks" }, or when the "Custom Duration" ToggleButton is checked: { "5 Minutes", "10 Minutes", "15 Minutes" }
3 Possible Solutions:
Ideally (for extensibility) the ViewModel would expose an ObservableCollection<TimeSpan>
to bind it's Combobox to. But in this case how should I suffix "Block/s" or "Minute/s". A single use multi-value converter would work, but that creates allot of boilerplate code and I'm not a big fan of how much type-checking you have to implement since they're not strongly typed.
An alternative is to use the ViewModel to expose an ObservableCollection<string>
, but in this case I have to write a method to convert "3 Blocks" => new TimeSpan( 0, 3 * BlockSize, 0 )
which sounds more appealing then a MultiConverter. In this case it's a fairly simple 1-1 mapping, but even so, I'd be tempted to create 4 properties & a method to control it.. Which seems like allot of code to manage for a single binding.
private Dictionary<string, TimeSpan> selectedDurationMap { get; set; }
private ObservableCollection<string> availableCustomDurations { get; set; }
private ObservableCollection<string> availableBlockDurations { get; set; }
public ObservableCollection<string> AvailableDurations { get; private set; }
private fillDurationLists( TimeSpan maximumDuration, TimeSpan blockDuration ) { }
another would be to have multiple ComboBox's, which are shown/hidden dependant on whether the "Custom Duration" ToggleButton is selected. Both could use the inbuilt StringFormat to append "Blocks" or "Minutes", While I can keep my Available Durations in TimeSpan format. This is the kind of solution I've tended to favor so far, but while the simple implementation ticks all the boxes, it isn't very extensible
for example, to add control over pluralisation (block vs blocks) I would need a ValueConverter, in this case, solution 1 is more extensible, readable and efficient (half the visual elements) while achieving the same goal with less code (ok, so it's a little开发者_运维技巧 bit of a toy problem, but this is a fairly typical scenario I experience)
There is another solution which I would favour, and that's to expose an ObservableCollection
of view models for duration. Wrap your TimeSpan
in a little ViewModel class that emits its name (e.g. "1 block") via ToString()
or a property.
This means your view model is generating the text in code which is the easiest solution. If you want to be pure about keeping your user-facing text in the XAML, then you could write a DataTemplate against this type, but you'll still have to handle pluralisation etc.
精彩评论