4

遇到了一个艰难的。考虑一个由对象列表组成的 ViewModel,其中每个对象定义一个 int 值,并且其中一些对象还定义了一个 Presets 字典,该字典键控在 UI 中表示该值的“友好”字符串上。

这是一个例子......

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; } // Shortened for readability. Assume this property supports INPC
    public Dictionary<int,string> Presets{ get; private set; }
}

UI 的目标是如果项目没有预设,用户可以输入他们想要的任何 int 值。但是,如果有预设,我们希望将它们限制为这些值,并将它们作为字典中的友好名称显示在 UI 中。

我们的第一次尝试是使用 TextBox 和 ComboBox,根据是否有预设来修改它们的可见性,就像这样......

<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

...但是当我们在支持虚拟化的列表的 DataTemplate 中使用它时,组合偶尔会显示空白。我相信是因为当项目被重用并且 DataContext 发生变化时, SelectedValue 在 ItemsSource 之前更新,这意味着可能没有预设值可以匹配,因此提议的 SelectedValue 值被控件抛出,然后ItemsSource 更新,但没有选择值所以它显示空白。

我的下一个想法(无论如何我们更喜欢)是只使用一个显示预设名称但实际上绑定到 Value 的 TextBox,然后使用转换器来发挥它的魔力,让用户输入友好名称或实际值. 如果用户输入的内容不是有效值或预设值,我们只会抛出错误。如果没有预设,它只会充当值的传递。

但是,我不确定如何将预设传递给转换器。您不能在 ConverterParameter 上设置绑定以以这种方式传递它们,如果您使用多重绑定,那么我不确定如何构造“ConvertBack”调用,因为我也需要传入它们,而不是送回去了。

我认为正确的方法是在 ViewModel 中实现 UiValue ,我们只需像这样绑定到它......

<TextBox Text="{Binding UiValue}" />

...然后将转换器中的代码移动到该属性的 getter/setter 实现,或者如果没有预设,则只是传递给 Value。但是,这似乎在 ViewModel 中出现了太多的逻辑,它应该在 View 中(ala 转换器或类似的)。再说一次,也许这正是 ViewModel 的重点。我不知道。欢迎提出想法。

4

2 回答 2

2

就个人而言,我会按照您的建议将“转换器代码”放入属性中......我认为将代码放入其中没有任何问题。事实上,它可能比在 a 中更好,Converter因为这样您也可以轻松地对其进行测试。

对不起,这不是一个答案,但我觉得你的问题至少值得一个。

于 2013-09-11T09:47:54.583 回答
1

ViewModel我喜欢你的问题,因为它说明了WPF中存在 a 背后的思维方式。有时它们似乎是不可避免的。

转换器被设计为无状态的,这就是为什么很难传递上下文变量,如presets. ViewModel是一个层,其职责是Model为绑定目的做准备。“模型”的作用是处理逻辑。因此, aViewModel可以详细处理 a 的行为(逻辑)View。这正是你想要的。大多数时候我发现自己根本不需要转换器。

有时感觉视图逻辑应该在 中更自然View,但ViewModel似乎是多余的。但是,当该逻辑位于其中时,ViewModel通常更容易进行自动测试。我根本不怕把这样的东西放进ViewModel去。通常这是最简单(也是正确)的方法。

拥有UiValue财产ViewModel并在那里处理转换:

public string UiValue{ get{/*...*/} set{/*...*/} }

换句话说,在 WPF 中没有干净的方法来替换您绑定到的属性。例如,如果你想拥有

<TextBox Text="{Binding IntValue}" />

在某些时候更改为:

<TextBox Text="{Binding PresetValue}" />

你被困住了。这不是事情的完成方式。最好有一个恒定的绑定,比如

<TextBox Text="{Binding UiValue}" />

并处理UiValue属性背后的逻辑。

ComboBox另一种可能的方法(而不是使用and的可见性TextBox)是拥有一个 DataTemplateSelector,它将决定是否应该为SomeItem. 如果presets为 null 或为空,则选择基于 TextBox 的DataTemplate,否则采用 ComboBox。如果我没记错的话,您必须FrameworkElement.DataContext从选择器中调查属性才能找到上下文 ( presets)。

考虑到您对方法的疑问ConvertBack,最常见的是valueBinding.DoNothing在您不需要在任何方向进行转换的情况下返回。

于 2013-09-11T09:56:59.020 回答