ViewModel 与 ValueConverter 的复合绑定

发布于 2024-11-27 02:56:55 字数 1624 浏览 0 评论 0原文

我已经使用 MVVM(或我自己的风格)有一段时间了,但仍有一个方面我想澄清。

(免责声明:我以前见过一些与此类似的问题,但我还没有看到任何考虑到专门针对视图更改数据类型的优点/缺点的内容。如果我错过了一些大的东西,我很抱歉,但每个第三个网页似乎都有“MVVM “在某处=p)

问题: 用户需要选择活动的持续时间。他们可以选择默认活动长度(也称为 BlockSize)的倍数,或者对于那些“特殊场合”,我们允许他们否决这一点并从 5 分钟间隔中进行选择。

对于 UI,我们决定使用组合框和切换按钮。默认情况下,组合框将显示 { "1 Block", "2 Blocks", "3 Blocks" },或者当选中“自定义持续时间”切换按钮时:{ "5 Minutes", "10 Minutes", "15 Minutes" }

3 个可能的解决方案: 理想情况下(为了可扩展性)ViewModel 将公开一个 ObservableCollection来将其 Combobox 绑定到。但在这种情况下,我应该如何添加后缀“Block/s”或“Minute/s”。单次使用的多值转换器可以工作,但这会创建大量样板代码,而且我不太喜欢必须实施多少类型检查,因为它们不是强类型的。

另一种方法是使用 ViewModel 公开 ObservableCollection,但在这种情况下,我必须编写一个方法来转换 "3 Blocks" => new TimeSpan( 0, 3 * BlockSize, 0 ) 这听起来比 MultiConverter 更有吸引力。在本例中,这是一个相当简单的 1-1 映射,但即便如此,我还是想创建 4 个属性和 4 个属性。一种控制它的方法。这看起来像是为单个绑定管理的代码分配。

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 ) { }

另一种方法是拥有多个组合框,这些组合框的显示/隐藏取决于是否选择“自定义持续时间”切换按钮。两者都可以使用内置的 StringFormat 来附加“块”或“分钟”,而我可以将可用持续时间保留为 TimeSpan 格式。到目前为止,这是我倾向于支持的解决方案,但是虽然简单的实现满足了所有要求,但它的可扩展性不是很好,

例如,要添加对多元化(块与块)的控制,我需要一个 ValueConverter,在这种情况下,解决方案 1 更具可扩展性、可读性和效率(视觉元素的一半),同时用更少的代码实现相同的目标(好吧,所以这有点像玩具问题,但这是我经历的一个相当典型的场景)

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

够运 2024-12-04 02:56:55

我喜欢另一种解决方案,那就是在持续时间内公开视图模型的 ObservableCollection 。将您的 TimeSpan 包装在一个小 ViewModel 类中,该类通过 ToString() 或属性发出其名称(例如“1 block”)。

这意味着您的视图模型正在代码中生成文本,这是最简单的解决方案。如果您想纯粹地将面向用户的文本保留在 XAML 中,那么您可以针对这种类型编写一个 DataTemplate,但您仍然需要处理复数等。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文