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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31015703/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 13:13:28  来源:igfitidea点击:

WPF ComboBox SelectedValue binding with null value shows up blank

c#wpfbindingcomboboxdatatemplate

提问by Patrick

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 绑定到一个属性时遇到问题,该属性为空。只有 1 个绑定到此属性的组合框会显示实际值。

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

下面是我的 Xaml,我使用 DataTemplate 选择一个组合框来展示 viewModel。

Xaml:

Xml:

<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.

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

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

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

采纳答案by Peter Duniho

Interesting problem.

有趣的问题。

Fundamentally, this appears to be caused by your choice to use nullas 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 ComboBoxelement is done. The SelectedValuePathproperty is initialized afterthe SelectedValueproperty.

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

This means that as your program is starting up and the ComboBoxelements are created, when nullis assigned to the SelectedValueproperty through its binding, the ComboBoxdoes 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 lastComboBoxstill 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 desiredbehavior) 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 nullas a selectable value. If every selectable value is non-null, then the non-null value used to initialize each element's SelectedValueproperty is retained and when the SelectedValuePathis initialized, the current selection for the ComboBoxis set correctly.
  • Don't use SelectedValuePath. Instead, just bind to SelectedItemand initialize the Valueproperty with the desired SelectableValueclass instance (e.g. the first one in the list).
  • In the ComboBox's Loadedevent, refresh the target of the binding.
  • 不要null用作可选值。如果每个可选值都是非空值,则用于初始化每个元素SelectedValue属性的非空值将被保留,并且在SelectedValuePath初始化时,ComboBox将正确设置的当前选择。
  • 不要使用SelectedValuePath. 相反,只需使用所需的类实例(例如列表中的第一个实例)绑定SelectedItem并初始化Value属性SelectableValue
  • ComboBoxLoaded事件中,刷新绑定的目标。

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 nullas 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.

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

That said, the third option does work, and if you're lucky, the only real hazard in using nullis 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 SelectedValueproperty of the control). Since at this point, the SelectedValuePathhas been set, assigning nullto 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 Valueproperties in your models. Having two different Valueproperties used for bindings in a single XAML element is very confusing. I would use, for example, SelectedValueand ItemValue, for the PropertyValueViewModelclass and the SelectableValueclass, respectively.


顺便说一句,我强烈建议您消除Value模型中属性名称的歧义。将两个不同的Value属性用于单个 XAML 元素中的绑定是非常令人困惑的。例如,我会分别将SelectedValueItemValue用于PropertyValueViewModel类和SelectableValue类。