Reputation: 15638
I intend to use data binding between a few of my classes. In other words, I am not binding values between a model class and the UI, but to bind variables between different classes.
I have read about data binding in C# on several places, but most of them are referring to binding between Windows Form and a source object.
I am still new to C#. This is how I understand what I should do:
First, for my source object, say has a class name of DataObject
. The source object has to implement a INotifyPropertyChange
interface, and then trigger the event whenever the property, health
, is set to change. I have no problem with this.
Now, suppose I have a target object called CharacterClass
. life
is a property in CharacterClass
, and is also the target property that I want to bind to the source object's health
property.
How can I bind the two properties together (both one-way and two-way) in code with only just the ordinary .NET framework?
A bit of background information on why I ask this question:
Just in case you think this is a duplicated question, it's not. I have searched through SE. The other questions on databinding in code are in the context of WPF or XAML, which is not for me. I have also read several articles on MSDN and it seems that I could create a Binding
object, and then bind the source and target via BindingOperations.SetBinding()
. However, the Binding
class seems to be part of the WPF library under the namespace of System.Windows.Data.Binding
. Although I am using C#, I doubt I would have the luxury to access to WPF libraries because I'm mainly using C# as only a scripting language within Unity3D. I believe I only have access to the the vanilla .Net framework. But, I am not very sure about this because I am still new to C#.
Upvotes: 7
Views: 7417
Reputation: 2726
Though there is a lot of support for bindings that is tightly coupled with the UI framework being used, you can still easily write your own binding framework.
Here is a POC which implements one-way binding between properties of two objects.
Note: This is just one of the possible ways, a POC at best (may need fine-tuning for high performance/production scenario) and uses .Net 2.0 classes and interfaces with no dependency on any UI framework (the 'vanilla' .net framework in your words :)). Once you have understood this, you can easily extend this to support 2-way binding as well
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));
}
}
}
Upvotes: 9
Reputation: 37760
I believe I only have access to the the vanilla .Net framework
The reality is that data binding is used in UI. So, if anybody talks about data binding, then he automatically implies "data binding in [UI Framework name]".
You may consider object-to-object mapping instead of data binding.
Upvotes: 2