如何使用C#在代码中进行数据绑定?

如何使用C#在代码中进行数据绑定?,c#,data-binding,C#,Data Binding,我打算在我的几个类之间使用数据绑定。换句话说,我不是在模型类和UI之间绑定值,而是在不同类之间绑定变量 我在很多地方读过关于C#中的数据绑定的文章,但大多数都是指Windows窗体和源对象之间的绑定 我还是个新手。这就是我如何理解我应该做的: 首先,对于我的源对象,假设它的类名为DataObject。源对象必须实现一个INotifyPropertyChange接口,然后在属性health设置为更改时触发事件。我对此没有问题 现在,假设我有一个名为CharacterClass的目标对象life是C

我打算在我的几个类之间使用数据绑定。换句话说,我不是在模型类和UI之间绑定值,而是在不同类之间绑定变量

我在很多地方读过关于C#中的数据绑定的文章,但大多数都是指Windows窗体和源对象之间的绑定

我还是个新手。这就是我如何理解我应该做的:

首先,对于我的源对象,假设它的类名为
DataObject
。源对象必须实现一个
INotifyPropertyChange
接口,然后在属性
health
设置为更改时触发事件。我对此没有问题

现在,假设我有一个名为
CharacterClass
的目标对象
life
CharacterClass
中的一个属性,也是我要绑定到源对象的
health
属性的目标属性

如何仅使用普通的.NET framework在代码中将这两个属性绑定在一起(单向和双向)

关于我为什么问这个问题的一些背景信息:

万一你认为这是一个重复的问题,那就不是了。我已经查过了。关于代码中的数据绑定的其他问题是在WPF或XAML的上下文中,这不适合我。我也读过几篇关于MSDN的文章,似乎我可以创建一个
绑定
对象,然后通过
BindingOperations.SetBinding()绑定源和目标。但是,
Binding
类似乎是
System.Windows.Data.Binding
命名空间下WPF库的一部分。虽然我使用的是C#,但我怀疑我是否有机会访问WPF库,因为我主要使用C#作为Unity3D中的一种脚本语言。我相信我只能访问香草.Net框架。但是,我对此不是很确定,因为我对C#还是新手

我相信我只能访问香草.Net框架

事实上,UI中使用了数据绑定。因此,如果有人谈论数据绑定,那么他会自动暗示“在[UI框架名称]中进行数据绑定”


您可以考虑对象到对象映射,而不是数据绑定。

< P>尽管有很多对绑定的支持,但绑定仍然与所使用的UI框架紧密结合,您仍然可以轻松地编写自己的绑定框架。p> 下面是一个POC,它在两个对象的属性之间实现单向绑定

注意:这只是一种可能的方法,POC最多(可能需要对高性能/生产场景进行微调)并使用.Net 2.0类和接口,而不依赖于任何UI框架(用你的话来说是“香草”.Net框架:))。一旦理解了这一点,您就可以轻松地扩展它以支持双向绑定

class Program
{
    public static void Main()
    {
        Source src = new Source();
        Destination dst = new Destination(src);
        dst.Name = "Destination";
        dst.MyValue = -100;
        src.Value = 50; //changes MyValue as well
        src.Value = 45; //changes MyValue as well
        Console.ReadLine();
    }
}

//A way to provide source property to destination property 
//mapping to the binding engine. Any other way can be used as well
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
internal class BindToAttribute : Attribute
{
    public string PropertyName
    {
        get;
        private set;
    }

    //Allows binding of different properties to different sources
    public int SourceId
    {
        get;
        private set;
    }

    public BindToAttribute(string propertyName, int sourceId)
    {
        PropertyName = propertyName;
        SourceId = sourceId;
    }
}

//INotifyPropertyChanged, so that binding engine knows when source gets updated
internal class Source : INotifyPropertyChanged
{
    private int _value;
    public int Value
    {
        get
        {
            return _value;
        }
        set
        {
            if (_value != value)
            {
                _value = value;
                Console.WriteLine("Value is now: " + _value);
                OnPropertyChanged("Value");
            }
        }
    }

    void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

internal class Destination
{
    private BindingEngine<Destination> _binder;

    private int _myValue;

    [BindTo("Value", 1)]
    public int MyValue
    {
        get
        {
            return _myValue;
        }
        set
        {
            _myValue = value;
            Console.WriteLine("My Value is now: " + _myValue);
        }
    }

    //No mapping defined for this property, hence it is not bound
    private string _name;
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
            Console.WriteLine("Name is now: " + _name);
        }
    }

    public Destination(Source src)
    {
        //Binder for Source no 1
        _binder = new BindingEngine<Destination>(this, src, 1);
    }
}

internal class BindingEngine<T>
{
    private readonly T _destination;
    private readonly PropertyDescriptorCollection _sourceProperties;
    private readonly Dictionary<string, PropertyDescriptor> _srcToDestMapping;

    public BindingEngine(T destination, INotifyPropertyChanged source, int srcId)
    {
        _destination = destination;

        //Get a list of destination properties
        PropertyDescriptorCollection destinationProperties = TypeDescriptor.GetProperties(destination);

        //Get a list of source properties
        _sourceProperties = TypeDescriptor.GetProperties(source);

        //This is the source property to destination property mapping
        _srcToDestMapping = new Dictionary<string, PropertyDescriptor>();

        //listen for INotifyPropertyChanged event on the source
        source.PropertyChanged += SourcePropertyChanged;

        foreach (PropertyDescriptor property in destinationProperties)
        {
            //Prepare the mapping.
            //Add those properties for which binding has been defined
            var attribute = (BindToAttribute)property.Attributes[typeof(BindToAttribute)];
            if (attribute != null && attribute.SourceId == srcId)
            {
                _srcToDestMapping[attribute.PropertyName] = property;
            }
        }
    }

    void SourcePropertyChanged(object sender, PropertyChangedEventArgs args)
    {
        if (_srcToDestMapping.ContainsKey(args.PropertyName))
        {
            //Get the destination property from mapping and update it
            _srcToDestMapping[args.PropertyName].SetValue(_destination, _sourceProperties[args.PropertyName].GetValue(sender));
        }
    }
}
类程序
{
公共静态void Main()
{
Source src=新的Source();
目的地dst=新目的地(src);
dst.Name=“目的地”;
dst.MyValue=-100;
src.Value=50;//同时更改MyValue
src.Value=45;//同时更改MyValue
Console.ReadLine();
}
}
//将源属性提供给目标属性的方法
//映射到绑定引擎。也可以使用任何其他方法
[AttributeUsage(AttributeTargets.Property,AllowMultiple=false)]
内部类BindToAttribute:属性
{
公共字符串PropertyName
{
得到;
私人设置;
}
//允许将不同的属性绑定到不同的源
公共int源ID
{
得到;
私人设置;
}
public BindToAttribute(字符串propertyName,int sourceId)
{
PropertyName=PropertyName;
SourceId=SourceId;
}
}
//INotifyPropertyChanged,以便绑定引擎知道何时更新源
内部类源:INotifyPropertyChanged
{
私有整数值;
公共整数值
{
得到
{
返回_值;
}
设置
{
如果(_值!=值)
{
_价值=价值;
WriteLine(“值现在为:”+_值);
不动产变更(“价值”);
}
}
}
void OnPropertyChanged(字符串propertyName)
{
var handler=PropertyChanged;
if(处理程序!=null)
{
处理程序(这是新的PropertyChangedEventArgs(propertyName));
}
}
公共事件属性更改事件处理程序属性更改;
}
内部类目的地
{
私人绑定引擎(binding engine);;
私有int_myValue;
[BindTo(“Value”,1)]
公共整数MyValue
{
得到
{
返回_myValue;
}
设置
{
_我的价值=价值;
WriteLine(“我的值现在是:”+_myValue);
}
}
//没有为此属性定义映射,因此它没有绑定
私有字符串\u名称;
公共字符串名
{
得到
{
返回_name;
}
设置
{
_名称=值;
Console.WriteLine(“名称现在是:”+_Name);
}
}
公共目的地(源src)
{
//源1的活页夹
_binder=新的BindingEngine(此,src,1);
}
}
内部类绑定引擎
{
专用只读T_目的地;
私有只读属性DescriptorCollection\u sourceProperties;
专用只读字典\u srcToDestMapping;
公共绑定引擎(T目标,INotifyPropertyChanged源,int srcId)
{
_目的地=目的地;
//获取目标属性的列表
PropertyDescriptorCollection destinationProperties=TypeDescriptor.GetProperties(目标);
//获取源属性的列表
_sourceProperties=TypeDescriptor.GetProperties(源);
//这是目标的源属性