绑定到值的正确方法是什么,同时显示也在该项上定义的预设列表中的友好名称



有一个艰难的。 考虑一个由对象列表组成的 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 中将它们显示为字典中的友好名称。

我们的第一个尝试是使用文本框和组合框,根据是否有预设来修改它们的可见性,就像这样......

<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 的文本框,然后使用转换器来发挥其魔力,并让用户键入友好名称或实际值。 如果用户键入的内容不是有效值或预设,我们只会抛出错误。 如果没有预设,它将仅充当值的传递。

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

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

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

。然后将转换器中的代码移动到该属性的 getter/setter 实现,或者如果没有预设,则只是传递到 Value。 但是,这似乎在视图模型中有太多的逻辑,而它应该在视图中(ala 转换器或类似)。 话又说回来,也许这正是ViewModel的重点。 我不知道。 欢迎思考。

就个人而言,我会按照您的建议将"转换器代码"放入属性中......我认为将代码放在那里没有任何问题。事实上,它可能比把它放在Converter里要好,因为这样你也可以轻松测试它。

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

我喜欢

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

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

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

ViewModel中拥有UiValue属性并在那里处理转换:

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

换写一下,在 WPF 中,没有干净的方法来替换绑定到的属性。 例如,如果您想拥有

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

在某些时候更改为:

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

你被困住了。事情不是这样做的。最好有一个像这样的恒定绑定

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

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

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

考虑到您对ConvertBack方法的疑问,如果您不需要在任何方向上进行转换,通常会返回valueBinding.DoNothing

相关内容

  • 没有找到相关文章

最新更新