C# 在WinRT中绑定到XAML中的DynamicObjects
我有一个C# 在WinRT中绑定到XAML中的DynamicObjects,c#,windows-runtime,winrt-xaml,C#,Windows Runtime,Winrt Xaml,我有一个observateCollection类,XAML拒绝绑定到包含的对象上的属性 我知道我在某个地方读到XAML支持动态和动态对象,所以我很困惑为什么这不起作用。其他问题,比如这一个,都毫无帮助: 我在运行时(以及在设计时将鼠标悬停在我的{Bindings上时,在设计器中)出现此错误: 错误:BindingExpression路径错误:“在”PremiseMetro.Light,PremiseMetro,“…BindingExpression:path='DisplayName'数据项=
observateCollection
类,XAML拒绝绑定到包含的对象上的属性
我知道我在某个地方读到XAML支持动态
和动态对象
,所以我很困惑为什么这不起作用。其他问题,比如这一个,都毫无帮助:
我在运行时(以及在设计时将鼠标悬停在我的{Binding
s上时,在设计器中)出现此错误:
错误:BindingExpression路径错误:“在”PremiseMetro.Light,PremiseMetro,“…BindingExpression:path='DisplayName'数据项=”PremiseMetro.Light,PremiseMetro,“…目标元素是”Windows.UI.Xaml.Controls.TextBlock'(Name='null');目标属性是'Text'(键入'String')
请帮忙
谢谢
测试可观察对象
类:
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));
}
}
类灯光:DynamicObject,INotifyPropertyChanged{
专用只读词典_properties=new Dictionary();
公共事件属性更改事件处理程序属性更改;
公共重写bool TryGetMember(GetMemberBinder绑定器,输出对象结果){
字符串名称=binder.name;
结果=空;
//如果在字典中找到属性名称,
//将结果参数设置为属性值并返回true。
//否则,返回false。
实物道具;
if(_properties.TryGetValue(名称,out prop)){
结果=道具;
返回true;
}
返回false;
}
//如果尝试设置属性的值,则
//未在类中定义,将调用此方法。
public override bool TrySetMember(SetMemberBinder绑定器,对象值){
字符串名称=binder.name;
_属性[名称]=值;
if(CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess)
OnPropertyChanged(名称);
其他的
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal,()=>OnPropertyChanged(name));
//您可以随时为字典添加值,
//所以这个方法总是返回true。
返回true;
}
公共对象GetMember(字符串propName){
var binder=binder.GetMember(CSharpBinderFlags.None,
propName,GetType(),
新名单{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null)
});
var callsite=callsite.Create(活页夹);
返回callsite.Target(callsite,this);
}
///
///设置属性的值。
///
///物业名称
///新价值
///如果为true,则不会尝试更新服务器。
public void SetMember(字符串propertyName,对象val){
var binder=binder.SetMember(CSharpBinderFlags.None,
propertyName,GetType(),
新名单{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None,null)
});
var callsite=callsite.Create(活页夹);
目标(callsite,this,val);
}
受保护的虚拟void OnPropertyChanged(字符串propertyName){
PropertyChangedEventHandler处理程序=PropertyChanged;
if(handler!=null)handler(这是新的PropertyChangedEventArgs(propertyName));
}
}
my MainViewMOdel构造函数中的测试:
Light light = new Light();
((dynamic) light).DisplayName = "Test Light";
((dynamic) light).Brightness= "27%";
((dynamic) light).PowerState= false;
Lights = new ObservableCollection<dynamic> {
light
};
灯光=新灯光();
((动态)灯)。DisplayName=“测试灯”;
((动态)光)。亮度=“27%”;
((动态)灯)。PowerState=false;
灯光=新的可见光采集{
光
};
我的测试XAML:
能否尝试将ViewModel更改为:
dynamic light = new ExpandoObject();
light.DisplayName = "Test Light";
light.Brightness = "27%";
light.PowerState = false;
var objs = new ObservableCollection<dynamic> { light };
dynamic light=new expandooobject();
light.DisplayName=“测试灯”;
亮度。亮度=“27%”;
light.PowerState=false;
var objs=新的可观测集合{light};
看看你的库中是否正确工作?简短回答:否,不支持绑定到UWP中
DynamicObject
实例上的动态属性
但是,确实存在一些类似的方法,使用ICustomPropertyProvider
假设您的类
如下所示:
public class SomeClass : DynamicObject, INotifyPropertyChanged {
private string _StaticStringProperty;
public string StaticStringProperty { get => _StaticStringProperty; set => SetField(ref _StaticStringProperty, value); }
private Dictionary<string, object> _DynamicProperties = new Dictionary<string, object>();
public override IEnumerable<string> GetDynamicMemberNames()
{
yield return "DynamicStringProperty";
yield return "DynamicIntegerProperty";
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _DynamicProperties.GetValueOrDefault(binder.Name, null);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_DynamicProperties[binder.Name] = value;
RaisePropertyChanged(binder.Name);
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
protected bool SetField<T>(ref T target, T value, [CallerMemberName]string caller = null)
{
if (EqualityComparer<T>.Default.Equals(target, value))
return false;
target = value;
RaisePropertyChanged(caller);
return true;
}
}
并能够提供动态属性
:
public class DynamicCustomProperty<TOwner, TValue> : ICustomProperty
{
public Func<dynamic, TValue> Getter { get; set; }
public Action<dynamic, TValue> Setter { get; set; }
public Func<dynamic, object, TValue> IndexGetter { get; set; }
public Action<dynamic, object, TValue> IndexSetter { get; set; }
public object GetValue(object target) => Getter.Invoke(target);
public void SetValue(object target, object value) => Setter.Invoke(target, (TValue)value);
public object GetIndexedValue(object target, object index) => IndexGetter.Invoke(target, index);
public void SetIndexedValue(object target, object value, object index) => IndexSetter.Invoke(target, index, (TValue)value);
public bool CanRead => Getter != null || IndexGetter != null;
public bool CanWrite => Setter != null || IndexSetter != null;
public string Name { get; set; }
public Type Type => typeof(TValue);
}
public类dynamicccustomproperty:ICustomProperty
{
公共函数Getter{get;set;}
公共操作设置器{get;set;}
public Func IndexGetter{get;set;}
公共操作索引设置器{get;set;}
公共对象GetValue(对象目标)=>Getter.Invoke(目标);
public void SetValue(object target,object value)=>Setter.Invoke(target,(TValue)value);
公共对象GetIndexedValue(对象目标,对象索引)=>IndexGetter.Invoke(目标,索引);
public void SetIndexedValue(对象目标、对象值、对象索引)=>IndexSetter.Invoke(目标、索引、(TValue)值);
公共bool CanRead=>Getter!=null | | IndexGetter!=null;
公共bool CanWrite=>Setter!=null | | IndexSetter!=null;
公共字符串名称{get;set;}
公共类型类型=>typeof(TValue);
}
最后,我们可以将它们绑定到XAML中:
<TextBox Header="Static String" Text="{Binding StaticStringProperty, Mode=TwoWay}"/>
<TextBox Header="Dynamic String" Text="{Binding DynamicStringProperty, Mode=TwoWay}"/>
<TextBox Header="Dynamic Integer" Text="{Binding DynamicIntegerProperty, Mode=TwoWay}"/>
DynamicObject
本身不提供成员发现机制,作为一个抽象类,可能不知道自己是否
public class DynamicCustomProperty<TOwner, TValue> : ICustomProperty
{
public Func<dynamic, TValue> Getter { get; set; }
public Action<dynamic, TValue> Setter { get; set; }
public Func<dynamic, object, TValue> IndexGetter { get; set; }
public Action<dynamic, object, TValue> IndexSetter { get; set; }
public object GetValue(object target) => Getter.Invoke(target);
public void SetValue(object target, object value) => Setter.Invoke(target, (TValue)value);
public object GetIndexedValue(object target, object index) => IndexGetter.Invoke(target, index);
public void SetIndexedValue(object target, object value, object index) => IndexSetter.Invoke(target, index, (TValue)value);
public bool CanRead => Getter != null || IndexGetter != null;
public bool CanWrite => Setter != null || IndexSetter != null;
public string Name { get; set; }
public Type Type => typeof(TValue);
}
<TextBox Header="Static String" Text="{Binding StaticStringProperty, Mode=TwoWay}"/>
<TextBox Header="Dynamic String" Text="{Binding DynamicStringProperty, Mode=TwoWay}"/>
<TextBox Header="Dynamic Integer" Text="{Binding DynamicIntegerProperty, Mode=TwoWay}"/>