【发布时间】:2013-09-04 02:51:10
【问题描述】:
我有一个ObservableCollection<dynamic> 类,XAML 拒绝绑定到包含对象的属性。
我知道我在某处读到 XAML 支持 dynamic 和 DyanmicObject,所以我非常困惑为什么这不起作用。其他问题,例如这个问题,非常没有帮助:
Can i bind against a DynamicObject in WinRT / Windows 8 Store Apps
我在运行时(以及在设计时将鼠标悬停在我的{Bindings 上时在设计器中)收到此错误:
Error: BindingExpression path error: 'DisplayName' property not found on 'PremiseMetro.Light, PremiseMetro, ... BindingExpression: Path='DisplayName' DataItem='PremiseMetro.Light, PremiseMetro, ... target element is 'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is 'Text' (type 'String')
请帮忙!
谢谢。
一个测试ObservableObject类:
class Light : DynamicObject, INotifyPropertyChanged {
private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
public event PropertyChangedEventHandler PropertyChanged;
public override bool TryGetMember(GetMemberBinder binder, out object result) {
string name = binder.Name;
result = null;
// If the property name is found in a dictionary,
// set the result parameter to the property value and return true.
// Otherwise, return false.
object prop;
if (_properties.TryGetValue(name, out prop)) {
result = prop;
return true;
}
return false;
}
// If you try to set a value of a property that is
// not defined in the class, this method is called.
public override bool TrySetMember(SetMemberBinder binder, object value) {
string name = binder.Name;
_properties[name] = value;
if (CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess)
OnPropertyChanged(name);
else
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal, () => OnPropertyChanged(name));
// You can always add a value to a dictionary,
// so this method always returns true.
return true;
}
public object GetMember(string propName) {
var binder = Binder.GetMember(CSharpBinderFlags.None,
propName, GetType(),
new List<CSharpArgumentInfo> {
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});
var callsite = CallSite<Func<CallSite, object, object>>.Create(binder);
return callsite.Target(callsite, this);
}
/// <summary>
/// Sets the value of a property.
/// </summary>
/// <param name="propertyName">Name of property</param>
/// <param name="val">New value</param>
/// <param name="fromServer">If true, will not try to update server.</param>
public void SetMember(String propertyName, object val) {
var binder = Binder.SetMember(CSharpBinderFlags.None,
propertyName, GetType(),
new List<CSharpArgumentInfo> {
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});
var callsite = CallSite<Func<CallSite, object, object, object>>.Create(binder);
callsite.Target(callsite, this, val);
}
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
我的 MainViewMOdel 构造函数中的测试:
Light light = new Light();
((dynamic) light).DisplayName = "Test Light";
((dynamic) light).Brightness= "27%";
((dynamic) light).PowerState= false;
Lights = new ObservableCollection<dynamic> {
light
};
我的测试 XAML:
<Grid Margin="10" Width="1000" VerticalAlignment="Stretch">
<ListBox x:Name="GDOList" ItemsSource="{Binding Path=Lights}" >
<ListBox.ItemTemplate>
<DataTemplate >
<Grid Margin="6">
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding Path=DisplayName}" Margin="5" />
<TextBlock Text="{Binding Path=PowerState}" Margin="5" />
<TextBlock Text="{Binding Path=Brightness}" Margin="5" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Page>
【问题讨论】:
-
DynamicObject本身不提供成员发现机制,并且作为抽象类,在访问特定成员之前可能不知道自己是否存在。这将使 WinRT 很难确定应该表示哪些属性。你有没有可能读到它支持ExpandoObject? -
我的 XAML 知道成员名称。如果基于反射的绑定没有找到,我不明白为什么运行时无法尝试动态绑定。这就是我认为它会做的事情。
-
找到这个:sdlsdk.codeplex.com/WorkItem/…。似乎与动态对象的数据绑定已损坏(DynamicObject 或 ExpandoObject),MS 已将其解决为“无法修复”。布拉格。
-
@M.Babcock 好吧,
DynamicObject确实 提供了一种成员发现机制,即GetDynamicMemberNames()方法。
标签: c# windows-runtime winrt-xaml