ValueTuple does not support DisplayMemberPath. Combobox, WPF [duplicate]
Asked Answered
M

1

6

I have a combobox and want to bind its ItemsSource to an IEnumerable<(string,string)>. If I do not set DisplayMemberPath, then it works and it shows in the dropdown area the result of calling ToString() in the items. Nevertheless when I set DisplayMemberPath="Item1" it does not show anything anymore. I have made the following sample in which you may see that if I use classic Tuple type it works as expected.

When debugging I have checked that the valuetuple has Item1 and Item2 as properties also.

My XAML:

<Window x:Class="TupleBindingTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Loaded="MainWindow_OnLoaded"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <ComboBox x:Name="TupleCombo" Grid.Row="0" VerticalAlignment="Center" 
                  DisplayMemberPath="Item1" />
        <ComboBox x:Name="ValueTupleCombo" Grid.Row="1" VerticalAlignment="Center" 
                  DisplayMemberPath="Item1" />
    </Grid>
</Window>

And my codebehind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;

namespace TupleBindingTest
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private IEnumerable<Tuple<string, string>> GetTupleData()
        {
            yield return Tuple.Create("displayItem1", "valueItem1");
            yield return Tuple.Create("displayItem2", "valueItem2");
            yield return Tuple.Create("displayItem3", "valueItem3");
        }

        private IEnumerable<(string, string)> GetValueTupleData()
        {
            yield return ( "displayItem1", "valueItem1");
            yield return ("displayItem2", "valueItem2");
            yield return ("displayItem3", "valueItem3");
        }

        private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
        {
            TupleCombo.ItemsSource = GetTupleData();
            ValueTupleCombo.ItemsSource = GetValueTupleData();
        }
    }
}

At runtime this sample will show the data properly in the first combobox but will show nothing in the second.

Why does this happen?

Moskowitz answered 2/8, 2017 at 18:35 Comment(0)
D
17

That's because DisplayMemberPath internally sets binding with the specified path for each item's template. So setting DisplayMemberPath="Item1" is basically shorthand for setting following ComboBox.ItemTemplate:

<DataTemplate>
    <ContentPresenter Content="{Binding Item1}" />
</DataTemplate>

Now WPF only supports binding to properties. That's why it works fine when you use Tuples - because its members are properties, and also why it does not work with ValueTuples - because its members are fields.

In your particular case though, since you use these collections solely as binding sources, you could use anonymous types to achieve your goal (its members are also properties), e.g.:

private IEnumerable<object> GetTupleData()
{
    yield return new { Label = "displayItem1", Value = "valueItem1" };
    yield return new { Label = "displayItem2", Value = "valueItem2" };
    yield return new { Label = "displayItem3", Value = "valueItem3" };
}

Then you could set up your ComboBox with:

<ComboBox DisplayMemberPath="Label" SelectedValuePath="Value" (...) />
Dogvane answered 2/8, 2017 at 19:2 Comment(1)
Moral of the story, the items of a ValueTuple are exposed as fields and not properties, and WPF doesn't support binding to fields.Trinatrinal

© 2022 - 2024 — McMap. All rights reserved.