Is it possible to bind to a ValueTuple field in WPF with C#7
Asked Answered
R

3

28

If I have a viewmodel property

public (string Mdf, string MdfPath) MachineDefinition { get; set; }

and I try to bind to it in XAML / WPF

<Label Content="{Binding Path=MachineDefinition.Item2}" />

or

<Label  Content="{Binding Path=MachineDefinition.MdfPath}" />

I get the same error

enter image description here

I see that ValueTuple fields are really fields not properties. Is this the problem?

Rambow answered 4/4, 2017 at 13:36 Comment(4)
Yes, that's the problem.Monopolist
Not sure why but Tuple has property fields and ValueTuple had field fields. It's a bit annoying :(Rambow
You can still make wrapper property like public string Mdf { get { return MachineDefinition.Mdf; } }. It's not the best ( not even a good ) way but still should work :)Monopolist
But that won't support INPC out of the box. That will require more boilerplate. My original aim was that these two properties should not generate separate change events. They are a coherent pair. I will just have to create a c# immutable class of the pair. ValueTuples fail for this use case.Rambow
R
48

The confusion is that for old style Tuple ( pre C#7 ) all the Items were properties

https://msdn.microsoft.com/en-us/library/dd386940(v=vs.110).aspx

and thus bindable. For ValueTuple they are fields

https://github.com/dotnet/runtime/blob/5ee73c3452cae931d6be99e8f6b1cd47d22d69e8/src/libraries/System.Private.CoreLib/src/System/ValueTuple.cs#L269

and not bindable.

If you google "WPF Tuple Binding" you get loads of false positives because old style tuples are bindable but the new ones are not.

Rambow answered 4/4, 2017 at 13:44 Comment(1)
It seems value tuples violate several good practicies: they expose public fields AND that means their are mutable structs, which I always thought is a major no-no. Probably perfomance considerations overweight this.Predictor
S
11

Something you could try is to implement a value converter. Here is an example...

public class TupleDisplayNameConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var tuple = value as (Int32 Id, String Name)?;

        if (tuple == null)
            return null;

        return tuple.Value.Name;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}


<TextBlock Text="{Binding Converter={StaticResource TupleDisplayNameConverter}, Mode=OneWay}" />

Hope this helps.

Sympathize answered 12/7, 2017 at 17:31 Comment(1)
you could also use reflection if you wanted something more generic, ie return value?.GetType()?.GetField((string)parameter )?.GetValue(value); that that would limit you to the real names (Item1, Item2, ...)Uncontrollable
S
4

The MdfPath approach will never work, since the name part is very restrictive in terms of where it actually exists. Essentially, it is pure compiler voodoo, and doesn't exist in the type model, which means that anything that talks to the type model (which includes reflection, UI tools, serializers, etc) will only see the Item1, Item2 names; not the fake names.

Supposal answered 4/4, 2017 at 13:41 Comment(1)
You will see in my question that I tried both. The real problem is the ValueTuple uses fields while Tuple uses properties. The former is unbindable and the latter bindable.Rambow

© 2022 - 2024 — McMap. All rights reserved.