I am trying to data bind a Xaml property to a property of a dynamic object. In this case the dynamic object is a JObject
from the Json.Net library. JObject
is a well behaved dynamic object and I've had no problems with it in the past. It makes for a lightweight way to go to/from json serialized objects and C# code.
In this case however the binding throws an exception at runtime.
The binding:
<TextBlock Text="{Binding UserProfile.Value.name}"/>
where Value
is a JObject
and name
a dynamic property (string)
The exception:
Error: BindingExpression path error: 'name' property not found on 'Newtonsoft.Json.Linq.JObject,
Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'.
BindingExpression: Path='UserProfile.Value.name' DataItem='Google.Apps.ViewModel.MainViewModel';
target element is 'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is 'Text'
(type 'String')
Curiously if I explicitly convert the JObject to an ExpandoObject the binding works.
private dynamic _value; // set to a deserialized JObject elsewhere
public dynamic Value
{
get
{
if (_value != null) // debug code
{
dynamic o = new ExpandoObject();
o.name = _value.name;
return o;
}
return _value;
}
}
For binding to dynamic objects to work does the data object need to be an ExpandoObject
. Obviously the binding is interrogating the dataobject's type and using reflection to get the property and value. Is there special case logic for ExpandoObject
and is ExpandoObject
the only option for doing this?
UPDATE
As @dkozl points out this type of binding works with straight up WPF. I can get the desired behavior with this WPF window and dynamic property:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock FontSize="36" Width="920">Label</TextBlock>
<TextBlock Text="{Binding Path=TheName.name, ElementName=root}" FontSize="36"/>
</StackPanel>
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public dynamic TheName
{
get
{
string json = @"{ 'name': 'James' }";
return JsonConvert.DeserializeObject<dynamic>(json);
}
}
}
However with a WinRT 8.1 Store app this very similar binding produces an error:
<Page
x:Name="root"
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Vertical">
<TextBlock FontSize="36" >Label</TextBlock>
<TextBlock Text="{Binding Path=TheName.name, ElementName=root}" FontSize="36"/>
</StackPanel>
</Grid>
</Page>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public dynamic TheName
{
get
{
string json = @"{ 'name': 'James' }";
return JsonConvert.DeserializeObject<dynamic>(json);
}
}
}
Simple project that demonstrates this is here. Unfortunately it looks like a difference of behavior in WinRT binding.