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}"/>