7

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

我在几个地方读到了 C# 中的数据绑定,但其中大多数是指 Windows 窗体和源对象之间的绑定。

我还是 C# 的新手。这就是我理解我应该做什么的方式:

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

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

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

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

万一你认为这是一个重复的问题,它不是。我已经通过 SE 进行了搜索。关于代码中数据绑定的其他问题是在 WPF 或 XAML 的上下文中,这不适合我。我还阅读了 MSDN 上的几篇文章,似乎我可以创建一个Binding对象,然后通过BindingOperations.SetBinding(). 但是,Binding该类似乎是 WPF 库的一部分,位于 System.Windows.Data.Binding. 尽管我使用的是 C#,但我怀疑我是否有机会访问 WPF 库,因为我主要使用 C# 作为 Unity3D 中的一种脚本语言。我相信我只能访问 vanilla .Net 框架。但是,我对此不太确定,因为我还是 C# 的新手。

4

2 回答 2

9

尽管对与所使用的 UI 框架紧密耦合的绑定有很多支持,但您仍然可以轻松编写自己的绑定框架。

这是一个 POC,它实现了两个对象的属性之间的单向绑定。

注意:这只是可能的方法之一,充其量是 POC(可能需要针对高性能/生产场景进行微调)并使用 .Net 2.0 类和接口,不依赖于任何 UI 框架('vanilla'.net用你的话来说框架:))。一旦你理解了这一点,你就可以轻松地扩展它以支持 2-way binding

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));
        }
    }
}

在此处输入图像描述

于 2012-10-18T10:52:24.793 回答
0

我相信我只能访问 vanilla .Net 框架

现实情况是 UI 中使用了数据绑定。因此,如果有人谈论数据绑定,那么他会自动暗示“[UI 框架名称] 中的数据绑定”。

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

于 2012-10-18T05:55:36.100 回答