与自定义转换器绑定时,枚举正在转换为字符串



问题概述

我有一个名为EnumDisplayConverter的自定义IValueConverter。它应该取一个Enum值并返回名称,以便可以显示它。不知怎的,即使这个转换器被用于Enum类型的属性之间的绑定,转换器也被传递了一个值String.Empty。这当然会导致一个错误,因为String不是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

复制步骤

  1. 运行MainWindow
  2. 从左侧的ListBox中选择任意项目
  3. ListBox中选择其他项目
  4. 观察未处理的异常

代码说明

如果不清楚代码应该做什么,我会解释一下。

CCD_ 11表示用户正在通过CCD_ 12编辑的数据对象。每个CCD_ 13都有一个CCD_具有Thing也是一种选择,这就是为什么ChosenThingThing?(即Nullable(Of Thing)(。

Null数据项在ComboBox中不起作用;没有选择";。为了绕过这一点,我将值-1添加到我的可选Thing值列表中。在Foo.SelectableChosenThing中,我检查-1,并将其转换为Null以获得Foo.ChosenThing的实际值。这使我能够正确地绑定到ComboBox

问题详细信息

只有当OpenFoo在被赋予新值之前被设置为Nothing时,错误才会出现。如果我去掉线路OpenFoo = Nothing,一切都正常。然而,在实际应用程序中,我希望在加载选择时将OpenFoo设置为Nothing——此外,它并没有解释为什么会发生这种情况。

当所涉及的属性为预期类型Thing时,为什么EnumDisplayConverter被传递为String类型的value

经过调查,发现问题来自ComboBox控件。当ComboBox的所选项目为null时,它将null的显示值替换为String.Empty。以下是中的一个片段。NETComboBox.UpdateSelectionBoxItem:参考源

...
// display a null item by an empty string
if (item == null)
{
item = String.Empty;
itemTemplate = ContentPresenter.StringContentTemplate;
}

SelectionBoxItem = item;
SelectionBoxItemTemplate = itemTemplate;
...

这发生在考虑任何DataTemplates之前,所以当调用我的DataTemplateget时,它被赋予了String.Empty的值来显示,而不是null

解决方案是要么

  • DataTrigger添加到ContentControlStyle,该CCD_50检查String.Empty,并且在这种情况下不使用转换器
  • 修改EnumDisplayConverter以检查非Enum值并返回DependencyProperty.UnsetValue

最新更新