wpf - 与自定义转换器绑定时,枚举被强制转换为字符串
问题描述
问题概述
我有一个习惯IValueConverter
叫EnumDisplayConverter
. 它应该接受一个Enum
值并返回名称以便显示。不知何故,即使这个转换器被用于Enum
类型属性之间的绑定,转换器也被传递了一个String.Empty
. 这当然会导致错误,因为String
is not an Enum
,更不用说它真的出乎意料。
重现代码
以下代码可用于重现错误。之后是重现的步骤和对代码用途的解释。
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VBTest"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<DockPanel>
<ListBox Name="LB_Foos" DockPanel.Dock="Left" ItemsSource="{Binding FooOptions}" SelectionChanged="ListBox_SelectionChanged"/>
<ComboBox ItemsSource="{x:Static local:MainWindow.SelectableThings}" SelectedItem="{Binding OpenFoo.SelectableChosenThing}" VerticalAlignment="Center" HorizontalAlignment="Center" Width="100">
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Content" Value="{Binding Converter={x:Static local:EnumDisplayConverter.Instance}}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="-1">
<Setter Property="Content" Value="None"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DockPanel>
</Window>
Imports System.Collections.ObjectModel
Imports System.Globalization
Class MainWindow
Shared Sub New()
Dim Things = (From v As Thing In [Enum].GetValues(GetType(Thing))).ToList
Things.Insert(0, -1)
SelectableThings = New ReadOnlyCollection(Of Thing)(Things)
End Sub
Public Shared ReadOnly Property SelectableThings As IReadOnlyList(Of Thing)
Public ReadOnly Property FooOptions As New ReadOnlyCollection(Of Integer)({1, 2, 3, 4})
'This is a placeholder method meant to set OpenFoo to a new instance of Foo when a selection is made.
'In the actual application, this is done with data binding and involves async database calls.
Private Sub ListBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
OpenFoo = Nothing
Select Case LB_Foos.SelectedItem
Case 1
OpenFoo = New Foo With {.ChosenThing = Nothing}
Case 2
OpenFoo = New Foo With {.ChosenThing = Thing.A}
Case 3
OpenFoo = New Foo With {.ChosenThing = Thing.B}
Case 4
OpenFoo = New Foo With {.ChosenThing = Thing.C}
End Select
End Sub
Public Property OpenFoo As Foo
Get
Return GetValue(OpenFooProperty)
End Get
Set(ByVal value As Foo)
SetValue(OpenFooProperty, value)
End Set
End Property
Public Shared ReadOnly OpenFooProperty As DependencyProperty =
DependencyProperty.Register("OpenFoo",
GetType(Foo), GetType(MainWindow))
End Class
Public Enum Thing
A
B
C
End Enum
Public Class Foo
Public Property ChosenThing As Thing?
Public Property SelectableChosenThing As Thing
Get
Return If(_ChosenThing, -1)
End Get
Set(value As Thing)
Dim v As Thing? = If(value = -1, New Thing?, value)
ChosenThing = v
End Set
End Property
End Class
Public Class EnumDisplayConverter
Implements IValueConverter
Public Shared ReadOnly Property Instance As New EnumDisplayConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
If value Is Nothing Then Return Nothing
Return [Enum].GetName(value.GetType, value)
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
Return Binding.DoNothing
End Function
End Class
重现步骤
- 跑
MainWindow
ListBox
从左侧选择任何项目- 从
ListBox
- 观察未处理的异常
代码说明
如果不清楚代码应该做什么,我会解释一下。
Foo
表示用户正在通过 编辑的数据对象MainWindow
。每个Foo
人都有一个选项Thing
。没有aThing
也是一种选择,这就是为什么ChosenThing
是 a Thing?
(ie Nullable(Of Thing)
)。
Null
数据项在 a 中不起作用ComboBox
,因为Null
意味着“没有选择”。为了解决这个问题,我在-1
我的可选值列表中添加了一个Thing
值。在Foo.SelectableChosenThing
中,我检查-1
并将其转换Null
为 的实际值Foo.ChosenThing
。这让我可以ComboBox
正确绑定。
问题详情
该错误似乎仅在被赋予新值之前OpenFoo
设置为时发生。Nothing
如果我取出线路OpenFoo = Nothing
,一切正常。但是,在实际应用程序中,我想在加载选择时设置OpenFoo
-Nothing
此外,它并没有解释为什么会发生这种情况。
当所涉及的属性是预期类型时,为什么要EnumDisplayConverter
传递一个value
类型?String
Thing
解决方案
事实证明,经过一番调查,问题出在ComboBox
控件上。当被选中的项目为 时ComboBox
,null
将显示值替换null
为String.Empty
。这是来自.NET 参考源ComboBox.UpdateSelectionBoxItem
的片段:
...
// display a null item by an empty string
if (item == null)
{
item = String.Empty;
itemTemplate = ContentPresenter.StringContentTemplate;
}
SelectionBoxItem = item;
SelectionBoxItemTemplate = itemTemplate;
...
这发生在DataTemplate
考虑任何 s 之前,所以当我DataTemplate
的 get 被调用时,它被赋予了一个值String.Empty
to display 而不是null
.
解决方案是
DataTrigger
在ContentControl
's中添加一个在这种情况下Style
检查String.Empty
并且不使用转换器的。- 修改
EnumDisplayConverter
以检查非Enum
值并返回DependencyProperty.UnsetValue
。
推荐阅读
- xpath - Automation Anywhere 中的动态表格行选择
- magento - Magento 1.9.3.1:客户组更改后,购物车中可配置产品的默认(不正确)价格显示
- javascript - 在不影响导航按钮的情况下单击元素外部时尝试关闭移动菜单(删除类)
- python - 熊猫分析不显示输出
- python - Python - BeautifulSoup 从输入中提取值
- swift - 我得到一个错误'String' cannot match values in my Switch statement using swift,为什么?
- java - 如何在 HBase 中扫描和删除数百万行
- angular - 如何将两个文本框值传递给Angular6中的函数?
- python - 如何让枯燥、稀疏的文字加粗?
- php - CakePHP 1.3 获取所有表相关数据