[英]What is the proper way to bind to a value while showing a friendly name from a list of presets also defined on that item?
Got a tough one. 有一个艰难的。 Consider a ViewModel that is comprised of a list of objects, where each object defines an int value, and some of those objects also define a Presets dictionary of ints keyed on a 'friendly' string representing that value in the UI.
考虑一个ViewModel,它由一个对象列表组成,其中每个对象定义一个int值,其中一些对象还定义了一个Presets字典,该字典是在一个'友好'字符串上键入的,该字符串表示UI中的该值。
Here's 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; } // Shortened for readability. Assume this property supports INPC
public Dictionary<int,string> Presets{ get; private set; }
}
The goal for the UI is if the item has no presets, the user can enter any int value they want. UI的目标是如果项目没有预设,用户可以输入他们想要的任何int值。 However, if there are presets, we want to limit them to those values and also display them in the UI as the Friendly names from the dictionary.
但是,如果有预设,我们要限制他们只能使用这些值,并且在UI从字典中的友好名称显示出来。
Our first attempt was to use a TextBox and a ComboBox, modifying their visibilities depending on if there were presets or not, like this... 我们的第一次尝试是使用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
...but when we're using this in a DataTemplate of a list that supports virtualization, the combo occasionally displays blanks. ...但是当我们在支持虚拟化的列表的DataTemplate中使用它时,组合偶尔会显示空白。 I believe is because when the item is reused and the DataContext changes, SelectedValue updates before ItemsSource meaning there's potentially no preset value to match on, thus the proposed SelectedValue value gets tossed by the control, then ItemsSource updates, but there's no selected value so it shows a blank.
我相信是因为当重新使用项目并且DataContext更改时,SelectedValue会在ItemsSource之前更新,这意味着可能没有匹配的预设值,因此建议的SelectedValue值被控件抛出, 然后 ItemsSource更新,但是没有选定的值,所以它显示一个空白。
My next thought (and what we preferred anyway) was to use only a TextBox that displayed the preset name but was actually bound to Value, then use a converter to work its magic, and let the user type either the friendly name or the actual value. 我的下一个想法(以及我们最喜欢的)是仅使用显示预设名称但实际上绑定到Value的TextBox,然后使用转换器来实现其魔力,并让用户键入友好名称或实际值。 If the user typed something that wasn't a valid value or a preset, we'd just throw an error.
如果用户键入的内容不是有效值或预设,我们只会抛出错误。 If there were no presets, it would simply act as a pass-through of the value.
如果没有预设,它只会作为价值的传递。
However, there I'm not sure how to pass in the presets to the converter. 但是,我不知道如何将预设传递给转换器。 You can't set a binding on a ConverterParameter to pass them in that way, and if you use a multi-binding, then I'm not sure how to structure the 'ConvertBack' call since there too I need them passed in, not sent back out.
你不能在ConverterParameter上设置绑定以这种方式传递它们,如果你使用多重绑定,那么我不知道如何构造'ConvertBack'调用,因为我也需要它们传入,而不是送回去了。
I'm thinking the proper way is to implement UiValue in the ViewModel which we'd simply bind to like this... 我认为正确的方法是在ViewModel中实现UiValue,我们只需将它绑定到这样......
<TextBox Text="{Binding UiValue}" />
...then move the code that would've been in the converter to that property's getter/setter implementation, or simply be a pass-through to Value if there are no presets. ......然后移动会一直在转换到该属性的getter / setter方法实现的代码,或者仅仅是一个直通如果没有预置值。 However, this seems like too much logic is going in the ViewModel where it should be in the View (ala a converter or similar.) Then again, maybe that's exactly the point of the ViewModel.
然而,这看起来似乎在ViewModel中的逻辑过多,它应该在View中(转换器或类似物)。然后,也许这正是ViewModel的重点。 I dunno.
我不知道。 Thoughts welcome.
欢迎思考。
Personally, I would go for putting the 'converter code' into the property as you suggested... I don't see any problem with having the code in there. 就个人而言,我会按照你的建议把'转换器代码'放到属性中...我没有看到在那里有代码的任何问题。 In fact, it's probably better than having it in a
Converter
because then you can easily test it too. 事实上,它可能比在
Converter
使用它更好,因为那样你也可以轻松测试它。
Sorry, this isn't much of an answer, but I felt that your question deserved at least one. 对不起,这不是一个很好的答案,但我觉得你的问题至少应该有一个。
I like your question, because it illustrates the way of thinking that stands behind the existence of a ViewModel
in WPF. 我喜欢你的问题,因为它说明了在WPF中存在
ViewModel
背后的思维方式。 Sometimes they seem inevitable. 有时他们似乎不可避免。
Converters are designed to be stateless, which is why it's difficult to pass context variables like presets
. 转换器被设计为无状态,这就是为什么很难传递像
presets
这样的上下文变量。 ViewModel
is a layer, of which responsibility is to prepare Model
for binding purposes. ViewModel
是一个层,其职责是为绑定目的准备Model
。 The role of a "model" is to handle logic. “模型”的作用是处理逻辑。 Thus, a
ViewModel
may handle in detail the behaviour (logic) of a View
. 因此,
ViewModel
可以详细处理View
的行为(逻辑)。 This is precisely what you want. 这正是你想要的。 Most of the time I find myself not needing Converters at all.
大多数时候我发现自己根本不需要转换器。
Sometimes it feels more natural that the view logic should be in the View
, but then ViewModel
seems superfluous. 有时感觉更自然,视图逻辑应该是在
View
,但随后ViewModel
似乎是多余的。 However, when that logic is located in the ViewModel
it's usually easier to auto-test. 但是,当该逻辑位于
ViewModel
,通常更容易进行自动测试。 I wouldn't be afraid of putting stuff like this in ViewModel
at all. 我根本不会害怕在
ViewModel
中放置这样的东西。 Often this is the easiest (and correct) way. 通常这是最简单(和正确)的方式。
Have UiValue
property in ViewModel
and handle conversion there: 在
ViewModel
有UiValue
属性并处理转换:
public string UiValue{ get{/*...*/} set{/*...*/} }
To rephrase, in WPF there is no clean way to replace the property you bind to. 换句话说,在WPF中没有干净的方法来替换绑定的属性。 Eg if you wanted to have
例如,如果你想拥有
<TextBox Text="{Binding IntValue}" />
change at some point to: 在某些时候改变为:
<TextBox Text="{Binding PresetValue}" />
you're trapped. 你被困了。 This is not how things are done.
这不是事情的完成方式。 Better have a constant binding like
最好有一个像常量一样的绑定
<TextBox Text="{Binding UiValue}" />
and deal with the logic behind the UiValue
property. 并处理
UiValue
属性背后的逻辑。
Another possible approach (instead of playing with visibility of ComboBox
and TextBox
) is to have a DataTemplateSelector, which would decide whether a ComboBox or TextBox should be created for SomeItem
. 另一种可能的方法(而不是使用
ComboBox
和TextBox
可见性)是有一个DataTemplateSelector,它决定是否应该为SomeItem
创建一个ComboBox或TextBox。 If presets
are null or empty select TextBox-based DataTemplate
, otherwise take ComboBox. 如果
presets
为null或为空,则选择基于TextBox的DataTemplate
,否则选择ComboBox。 If I'm not mistaken you'd have to investigate FrameworkElement.DataContext
property from within the selector to find the context ( presets
). 如果我没弄错,你必须从选择器中调查
FrameworkElement.DataContext
属性来查找上下文( presets
)。
Considering your doubt about ConvertBack
method, most commonly value
or Binding.DoNothing
is returned in case you don't need conversion in any of the directions. 考虑到您对
ConvertBack
方法的疑问,如果您不需要在任何方向进行转换,则最常返回value
或Binding.DoNothing
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.