Inconsistencies and unexpected results when binding to objects with explicit interface implementations
- Dominant language
- C#
- Stars
- 7.7k
- Forks
- 1.3k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 61
Description
* .NET Core Version: **3.1**
* Windows version: **1909**
* Does the bug reproduce also in WPF for .NET Framework 4.8?: **Yes**
* Is this bug related specifically to tooling in Visual Studio (e.g. XAML Designer, Code editing, etc...)? No, although IntelliSense for the XAML code actually does the right thing in this case, treating the property as being the static type as opposed to the runtime type, which makes sense, as it can't know the runtime type yet. 🙂
**Problem description:**
When a class has an explicit interface implementation of a property, XAML bindings don't match the behaviour of C# code, failing to return the interface implementation when bound to properties of the interface type.
**Actual behavior:**
Viewmodel properties of type `ISomeInterface`, are treated as their runtime type in XAML bindings, rather than as the interface type. As a result, explicit interface implementations of properties are not accessible unless the bound property is cast in a particular way.
**Expected behavior:**
A bound property in XAML should be treated as being of the property type, not its underlying runtime type. This would make it consistent with C# code.
**Minimal repro:**
The meaningful parts look like this:
```cs
public interface IData
{
public string Name { get; }
public string OnlyOnInterface { get; }
}
public class Data : IData
{
string IData.Name { get; } = "Interface Property";
string IData.OnlyOnInterface { get; } = "Interface-Only Property";
public string Name { get; } = "Class Property";
// Including an additional property so that it's clear in the XAML editor which type it
// thinks the bound object is: it won't show this property if it think the object is of
// type IData.
public int OtherProperty { get; } = 123;
}
public class ViewModel
{
public IData DataAsInterface { get; } = new Data();
public Data DataAsClass { get; } = new Data();
public string CSharpInterfaceName { get; }
public string CSharpClassName { get; }
public string CSharpCastClassName { get; }
public ViewModel()
{
CSharpInterfaceName = DataAsInterface.Name;
CSharpClassName = DataAsClass.Name;
CSharpCastClassName = ((IData)DataAsClass).Name;
}
}
```
Demo project here: https://github.com/ben-reilly/wpf-explicit-interface-binding-bug
---
Below is an image from the demo project linked above. To the left is the content of the `{Binding ...}` which is used for the text block to the right; the right side shows which of the implementations of the `Name` property is being loaded: the one for the class or the explicit interface implementation.
For reference and comparison, nos. 7 through 9 show how C# code determines the value of the `Name` property under three different circumstances (see the ViewModel constructor in the demo project).

Notice in particular that...
- No. 0 doesn't load anything, giving a runtime error because `Data` doesn't have a property with the name `OnlyOnInterface`, despite the fact that the viewmodel property is of type `IData`, which has such a property.
- Even though no. 1 is bound to a property of type `IData`, it returns the class implementation of the `Name` property, rather than the explicit interface implementation. In C# code, a variable of type `IData` would return the interface implementation of the property (see nos. 7 and 9).
- Explicitly casting to the interface works for the class-typed property (no. 5) but not for the interface-typed property (no. 2)...
- ...except when the binding path is prepended with `Path=` (no. 3), though that is not required for the class-typed property (nos. 5 and 6).
In my humble opinion, the results for nos. 0, 1, and 2 are incorrect.
It seems surprising to me that when binding to a viewmodel property of type `IData` it is treated as its runtime type, `Data`; that is not how types work in C# (no. 7), so I wouldn't expect it to work that way in XAML bindings either.
Additionally, it is surprising that no. 2 and no. 5 are different, especially given that nos. 5 and 6 match.
Contributor guide
Assessment
This issue has not been assessed yet.