带有null值的WPF ComboBox SelectedValue绑定显示为空白

时间:2022-04-29 11:46:59

I have a problem while trying to bind 2 or more Comboboxes SelectedValue to a property, that is null. Only 1 of the comboboxes bound to this property will show the real value.

尝试将2个或更多Comboboxes SelectedValue绑定到属性时出现问题,即null。只有1个绑定到此属性的组合框将显示实际值。

Below is my Xaml where i use DataTemplate to select a Combobox for presentation of the viewModel.

下面是我的Xaml,我使用DataTemplate选择一个Combobox来呈现viewModel。

Xaml:

XAML:

<Window x:Class="Test.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Test"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <DataTemplate DataType="{x:Type local:PropertyValueViewModel}">
        <ComboBox SelectedValue="{Binding Value}" ItemsSource="{Binding SelectableValues}" DisplayMemberPath="Description" SelectedValuePath="Value"/>
    </DataTemplate>
</Window.Resources>
<StackPanel>
    <Label Content="These uses template:"></Label>
    <ContentPresenter Content="{Binding ValueSelector}"></ContentPresenter>
    <ContentPresenter Content="{Binding ValueSelector}"></ContentPresenter>
    <ContentPresenter Content="{Binding ValueSelector}"></ContentPresenter>
</StackPanel>

And the code behind:

而背后的代码:

 public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        ValueSelector = new PropertyValueViewModel()
        {
            SelectableValues = new List<SelectableValue>()
            {
                new SelectableValue("NULL", null),
                new SelectableValue("1", 1)
            },
            Value = null
        };

        DataContext = this;
    }

    public static readonly DependencyProperty ValueSelectorProperty = DependencyProperty.Register(
        "ValueSelector", typeof(PropertyValueViewModel), typeof(MainWindow), new PropertyMetadata(default(PropertyValueViewModel)));

    public PropertyValueViewModel ValueSelector
    {
        get { return (PropertyValueViewModel)GetValue(ValueSelectorProperty); }
        set { SetValue(ValueSelectorProperty, value); }
    }
}

/// <summary>
/// My viewModel
/// </summary>
public class PropertyValueViewModel
{
    public object Value { get; set; }
    public object SelectableValues { get; set; }
}

/// <summary>
/// The items in the combobox
/// </summary>
public class SelectableValue
{
    public SelectableValue(string header, object value)
    {
        Value = value;
        Description = header;
    }

    public object Value { get; set; }

    public string Description { get; set; }
}

Now i am wondering why only 1 of them can show the NULL value at startup? I can change the value in any of them, and they will all sync with the value in the property - if i select 1 and then back to NULL, they will all show NULL. It seems like its only the initial value is not shown correctly.

现在我想知道为什么只有其中一个可以在启动时显示NULL值?我可以更改其中任何一个的值,它们都会与属性中的值同步 - 如果我选择1然后再返回NULL,它们都将显示为NULL。看起来它只是初始值没有正确显示。

If i avoid using DataTemplate the binding works too. Does anyone know why the DAtaTemplate behaves this way?

如果我避免使用DataTemplate,绑定也会起作用。有谁知道DAtaTemplate为什么会这样?

1 个解决方案

#1


3  

Interesting problem.

有趣的问题。

Fundamentally, this appears to be caused by your choice to use null as one of the selectable values. null, of course, has special meaning, for C#, .NET, and WPF. The problem also involves the order in which the initialization of the ComboBox element is done. The SelectedValuePath property is initialized after the SelectedValue property.

从根本上说,这似乎是由于您选择使用null作为可选值之一。当然,对于C#,.NET和WPF,null具有特殊含义。该问题还涉及完成ComboBox元素初始化的顺序。 SelectedValuePath属性在SelectedValue属性之后初始化。

This means that as your program is starting up and the ComboBox elements are created, when null is assigned to the SelectedValue property through its binding, the ComboBox does not yet have enough information to handle that value as a legitimate item selection. Instead, it interprets it as no selection at all.

这意味着当您的程序启动并且创建了ComboBox元素时,当通过其绑定将null赋值给SelectedValue属性时,ComboBox还没有足够的信息来处理该值作为合法项目选择。相反,它将其解释为根本没有选择。

Why does the last ComboBox still get initialized the way you want? I'm not really sure…I didn't investigate very far regarding that. I could speculate, but the odds of my guessing correctly seem low so I won't bother. Since it's the anomaly and not necessarily in keeping with expected behavior (based on above, even if the behavior is the desired behavior) I'll chalk it up to one of WPF's many "quirks". :)

为什么最后一个ComboBox仍然按照你想要的方式进行初始化?我不太确定......我没有对此进行过很长时间的调查。我可以推测,但我的猜测正确的几率似乎很低,所以我不会打扰。因为它是异常的并且不一定与预期的行为一致(基于上面,即使行为是期望的行为),我将把它归结为WPF的许多“怪癖”之一。 :)

I found several work-arounds for the issue:

我找到了几个解决这个问题的方法:

  • Don't use null as a selectable value. If every selectable value is non-null, then the non-null value used to initialize each element's SelectedValue property is retained and when the SelectedValuePath is initialized, the current selection for the ComboBox is set correctly.
  • 不要将null用作可选值。如果每个可选值都为非null,则保留用于初始化每个元素的SelectedValue属性的非null值,并且在初始化SelectedValuePath时,正确设置ComboBox的当前选择。
  • Don't use SelectedValuePath. Instead, just bind to SelectedItem and initialize the Value property with the desired SelectableValue class instance (e.g. the first one in the list).
  • 不要使用SelectedValuePath。相反,只需绑定到SelectedItem并使用所需的SelectableValue类实例(例如列表中的第一个)初始化Value属性。
  • In the ComboBox's Loaded event, refresh the target of the binding.
  • 在ComboBox的Loaded事件中,刷新绑定的目标。

The first two are significant departures from your current design. Personally, if at all possible I would go with one or the other. It seems to me that there's a clear danger in using null as a selectable value in a ComboBox, and this may not be the only oddity you run into. In the long run, maintenance of this part of the code may cost a lot more if you continue to use null.

前两个与您当前的设计有很大不同。就个人而言,如果可能的话,我会选择其中一个。在我看来,使用null作为ComboBox中的可选值存在明显的危险,这可能不是您遇到的唯一奇怪之处。从长远来看,如果继续使用null,维护这部分代码可能会花费更多。

That said, the third option does work, and if you're lucky, the only real hazard in using null is on initialization. My proposed work-around for that option would look something like this:

也就是说,第三个选项确实有效,如果你很幸运,使用null的唯一真正危险就是初始化。我建议的解决方案的解决方案看起来像这样:

XAML:

XAML:

<DataTemplate DataType="{x:Type local:PropertyValueViewModel}">
    <ComboBox SelectedValue="{Binding Value}"
              ItemsSource="{Binding SelectableValues}"
              DisplayMemberPath="Description"
              SelectedValuePath="Value"
              Loaded="comboBox_Loaded"/>
</DataTemplate>

C#:

C#:

private void comboBox_Loaded(object sender, RoutedEventArgs e)
{
    ComboBox comboBox = (ComboBox)e.OriginalSource;

    BindingOperations.GetBindingExpression(comboBox, ComboBox.SelectedValueProperty)
                     .UpdateTarget();
}

This forces WPF to update the target (i.e. the SelectedValue property of the control). Since at this point, the SelectedValuePath has been set, assigning null to the property this time correctly updates the selected item for the ComboBox.

这会强制WPF更新目标(即控件的SelectedValue属性)。此时,已设置SelectedValuePath,此时为属性分配null,正确更新ComboBox的选定项。


By the way, I would strongly recommend that you disambiguate the names of the Value properties in your models. Having two different Value properties used for bindings in a single XAML element is very confusing. I would use, for example, SelectedValue and ItemValue, for the PropertyValueViewModel class and the SelectableValue class, respectively.

顺便说一下,我强烈建议您消除模型中Value属性的名称。在单个XAML元素中使用两个不同的Value属性进行绑定非常令人困惑。我将分别为PropertyValueViewModel类和SelectableValue类使用SelectedValue和ItemValue。

#1


3  

Interesting problem.

有趣的问题。

Fundamentally, this appears to be caused by your choice to use null as one of the selectable values. null, of course, has special meaning, for C#, .NET, and WPF. The problem also involves the order in which the initialization of the ComboBox element is done. The SelectedValuePath property is initialized after the SelectedValue property.

从根本上说,这似乎是由于您选择使用null作为可选值之一。当然,对于C#,.NET和WPF,null具有特殊含义。该问题还涉及完成ComboBox元素初始化的顺序。 SelectedValuePath属性在SelectedValue属性之后初始化。

This means that as your program is starting up and the ComboBox elements are created, when null is assigned to the SelectedValue property through its binding, the ComboBox does not yet have enough information to handle that value as a legitimate item selection. Instead, it interprets it as no selection at all.

这意味着当您的程序启动并且创建了ComboBox元素时,当通过其绑定将null赋值给SelectedValue属性时,ComboBox还没有足够的信息来处理该值作为合法项目选择。相反,它将其解释为根本没有选择。

Why does the last ComboBox still get initialized the way you want? I'm not really sure…I didn't investigate very far regarding that. I could speculate, but the odds of my guessing correctly seem low so I won't bother. Since it's the anomaly and not necessarily in keeping with expected behavior (based on above, even if the behavior is the desired behavior) I'll chalk it up to one of WPF's many "quirks". :)

为什么最后一个ComboBox仍然按照你想要的方式进行初始化?我不太确定......我没有对此进行过很长时间的调查。我可以推测,但我的猜测正确的几率似乎很低,所以我不会打扰。因为它是异常的并且不一定与预期的行为一致(基于上面,即使行为是期望的行为),我将把它归结为WPF的许多“怪癖”之一。 :)

I found several work-arounds for the issue:

我找到了几个解决这个问题的方法:

  • Don't use null as a selectable value. If every selectable value is non-null, then the non-null value used to initialize each element's SelectedValue property is retained and when the SelectedValuePath is initialized, the current selection for the ComboBox is set correctly.
  • 不要将null用作可选值。如果每个可选值都为非null,则保留用于初始化每个元素的SelectedValue属性的非null值,并且在初始化SelectedValuePath时,正确设置ComboBox的当前选择。
  • Don't use SelectedValuePath. Instead, just bind to SelectedItem and initialize the Value property with the desired SelectableValue class instance (e.g. the first one in the list).
  • 不要使用SelectedValuePath。相反,只需绑定到SelectedItem并使用所需的SelectableValue类实例(例如列表中的第一个)初始化Value属性。
  • In the ComboBox's Loaded event, refresh the target of the binding.
  • 在ComboBox的Loaded事件中,刷新绑定的目标。

The first two are significant departures from your current design. Personally, if at all possible I would go with one or the other. It seems to me that there's a clear danger in using null as a selectable value in a ComboBox, and this may not be the only oddity you run into. In the long run, maintenance of this part of the code may cost a lot more if you continue to use null.

前两个与您当前的设计有很大不同。就个人而言,如果可能的话,我会选择其中一个。在我看来,使用null作为ComboBox中的可选值存在明显的危险,这可能不是您遇到的唯一奇怪之处。从长远来看,如果继续使用null,维护这部分代码可能会花费更多。

That said, the third option does work, and if you're lucky, the only real hazard in using null is on initialization. My proposed work-around for that option would look something like this:

也就是说,第三个选项确实有效,如果你很幸运,使用null的唯一真正危险就是初始化。我建议的解决方案的解决方案看起来像这样:

XAML:

XAML:

<DataTemplate DataType="{x:Type local:PropertyValueViewModel}">
    <ComboBox SelectedValue="{Binding Value}"
              ItemsSource="{Binding SelectableValues}"
              DisplayMemberPath="Description"
              SelectedValuePath="Value"
              Loaded="comboBox_Loaded"/>
</DataTemplate>

C#:

C#:

private void comboBox_Loaded(object sender, RoutedEventArgs e)
{
    ComboBox comboBox = (ComboBox)e.OriginalSource;

    BindingOperations.GetBindingExpression(comboBox, ComboBox.SelectedValueProperty)
                     .UpdateTarget();
}

This forces WPF to update the target (i.e. the SelectedValue property of the control). Since at this point, the SelectedValuePath has been set, assigning null to the property this time correctly updates the selected item for the ComboBox.

这会强制WPF更新目标(即控件的SelectedValue属性)。此时,已设置SelectedValuePath,此时为属性分配null,正确更新ComboBox的选定项。


By the way, I would strongly recommend that you disambiguate the names of the Value properties in your models. Having two different Value properties used for bindings in a single XAML element is very confusing. I would use, for example, SelectedValue and ItemValue, for the PropertyValueViewModel class and the SelectableValue class, respectively.

顺便说一下,我强烈建议您消除模型中Value属性的名称。在单个XAML元素中使用两个不同的Value属性进行绑定非常令人困惑。我将分别为PropertyValueViewModel类和SelectableValue类使用SelectedValue和ItemValue。