Well I am sure this is sounding weird when the whole world is all over WPF. However, I am going to mention my experience about some cool stuff that exists in the framework which I have been exploring for the past few days.
I had a funny problem to solve (I am sure there will be lots of folks there who might find this a not so interesting post). We all know that WPF has a great deal of support for data reference binding (DRB) and sure it is. However, in my specific problem, I wanted to utilize the cool feature of WPF DRB without really meaning to write a WPF application. So, let's say I am wanting to replace the data binding capability under System.Data with the one under System.Windows.Data. Why??? because of the cool stuff that it has in it!! And when I say cool stuff I am specifically referring to literal expressions that can be provided in the Path property, ValueConverters that can be used to translate/transform value of one object to another, etc.
WPF DRB largely operates over DependencyObject - type of objects and DependencyProperties - add to it a spicy of INotifyPropertyChanged interface. [Do a search on those three key words and there's plenty of material out there that describes the whats and hows of each of those].
Now I am going to describe two scenarios and how to solve them using DRB. Note, there could be parallel frameworks and technologies that may be able to achieve the same thing. The beauty is how much code you would end up writing. And with WPF DRB, you will be amazed.
Scenario 1:
I want to bind a collection of an entity (e.g. Persons) to a generic object like a Table, which is my middle tier object. This middle tier, generic table will be consumed by the presentation layer (could be winforms, webforms, office apps, anthing) to be rendered as a UI table. Moreover, the declarative side of my app would read a value from the DB like:
"<Table Source=PersonsCollection>"
So essentially what we are doing is
1. Taking an object in one format and converting it into another.
2. Whilst it's converted to another format, we are establishing two-way binding between those two objects. If I change a value in either one of these objects, it will automatically change the corresponding value on the other object. So e.g. if, Table.Rows[0].Cells[0].CellValue represents the PersonName. If I change the CellValue in this scenario, it will automatically change the value on Persons[0].PersonName to which this cell is bound to.
My Table class may look like this:
[Serializable]
public class Table : DependencyObject, INotifyPropertyChanged
{
private Rows _rows = new Rows();
public Rows Rows
{
get { return _rows; }
set { _rows = value; }
}
}
[Serializable]
public class Rows : ObservableCollection<Row>
{}
[Serializable]
public class Row : DependencyObject, INotifyPropertyChanged
{
private Cells _cells = new Cells();
public Cells Cells
{
get { return _cells; }
set { _cells = value; }
}
}
[Serializable]
public class Cells : ObservableCollection<Cell>
{}
[Serializable]
public class Cell : DependencyObject, INotifyPropertyChanged
{
public string CellValue
{
get { return (string)this.GetValue(CellValueProperty); }
set
{
this.SetValue(CellValueProperty, value);
OnPropertyChanged("CellValue");
}
}
public static readonly DependencyProperty CellValueProperty = DependencyProperty.Register(
"CellValue", typeof(string), typeof(Cell), new PropertyMetadata(""));
}
And let's say my Person class and Persons collection class looks like this:
[Serializable]
public class Persons: ObservableCollection<IData>
{
}
[Serializable]
public class Person : IData, INotifyPropertyChanged
{
private string _pName;
public string PersonName
{
get { return (string)this.GetValue(PersonNameProperty); }
set
{
this.SetValue(PersonNameProperty, value);
OnPropertyChanged("PersonName");
}
}
public static readonly DependencyProperty PersonNameProperty = DependencyProperty.Register(
"PersonName", typeof(string), typeof(Person), new PropertyMetadata(new Person()));
}
[Serializable]
public abstract class IData : DependencyObject
{}
Before we move ahead here are a quick few things:
1. I am declaring a DependencyObject class - IData (I know it is not an interface). The person class derives from it. And Persons collection is not really a collection of Person object but a collection of IData. Why?? Because I like it that way.
2. Persons collection is of type ObservableCollection - this is a collection class available in the framework and it implements INotifyPropertyChanged.
3. I am declaring DependencyProperties at various places to store values that I am going to use for Binding.
Now, assume that I am given an expression as above with some more information as follows:
"<Table Source=Persons>
<Row Source= ".[i]"> //This maps to Persons[i] --> i.e. Person object at position represented by "i"
<Cell = ".Persons[i].PersonName"/> //This is the Property inside the Persons[i]
</Row>
</Table>"
This is more like a template which defines what data my final table should contain.
So in short we are going to take this template and apply it to the Persons collection and get the Table object out of it.
I am not going to paste the whole code here, however, this is what we will do in short:
Table t = new Table();
for (int dataElementCount = 0; dataElementCount < PersonCollection.Count; dataElementCount++)
{
Row r = new Row();
for (int cellCount = 0; cellCount < NoOfCellsRequiredInTheRow; cellCount++)
{
Cell c1 = new Cell();
System.Windows.Data.Binding cellbinding = new System.Windows.Data.Binding();
cellbinding.Source = dc;
dataRefExpression = template.Rows[rowTemplateCount].Cells[cellCount].DataReferenceExpression.Replace("[i]", "[" + dataElementCount.ToString() + "]"); --> This will be .Persons[0].PersonName for the element "0".
cellbinding.Path = new System.Windows.PropertyPath(dataRefExpression);
cellbinding.Mode = BindingMode.TwoWay;
//// IMPORTANT NOTE 1: We at this instance know the target DependencyObject (Cell) and the DependencyProperty
//// (CellValueProperty) on that object.
BindingOperations.SetBinding(c1, Cell.CellValueProperty, cellbinding);
r.Cells.Add(c1);
}
t.Rows.Add(r);
}
That's it and by the end of it you will have your Generic table ready with TwoWay DRB. So change the CellValue representing the PersonName in the generic table and it will automatically change the underlying Persons collection and vice-versa.
Scenario 2:
The second scenario is something like this. A database value has a expression stored like this
"Global.Customers[i].CustomerName" = "Global.Persons[i].PersonName"
This may look simple however, it's really not that simple. Refer to my IMPORTANT NOTE 1 above. The binding to be set successfully requires the target DependencyObject and the DependencyProperty on that object. In this scenario, all I know is the source collection is Persons and Target collection is Customers. However, the expression says "Global.". The reason is to show that the DependencyObject actually lies one level below in the hierarchy this time, so I cannot assume by default that the object corresponding to the "substring" before the first "." (dot) --> Global, is a DependencyObject. So how do we get to the DependencyObject --> Customers[i] and Persons[i]??
Two options that I can immediately think of:
1. Use some smart reflection and take each substring check if it's a DependencyObject then enlist it's dependency properties and see if it is equivalent to the one that we are interested in setting... phew... thinking of it, itself is scary.
2. Put WPF DRB to use.
Wow...did I just reduce the above sentence (1) to 1/10 of it's size?? Well the solution that we will see will have an equivalent effect.
Here's how I tricked WPF DRB to do what I wanted.
I created a dummy class such as:
public class ValueSetter : DependencyProperty , INotifyPropertyChanged
{
public object Dummy
{
get { return (string)this.GetValue(DummyProperty); }
set
{
this.SetValue(DummyProperty, value);
OnPropertyChanged("Dummy");
}
}
public static readonly DependencyProperty DummyProperty = DependencyProperty.Register(
"Dummy", typeof(object), typeof(ValueSetter), new PropertyMetadata(new object()));
public void SetValue(ref ValueSetter vsw2)
{
this.Dummy = vsw2.Dummy;
}
}
And then smarties out there would have guessed what's coming next.
I write the same exact DRB code that I used for Table object above for both my Left side and Right side of the above expression --> "Global.Customers[i].CustomerName" = "Global.Persons[i].PersonName"
ValueSetter vsw1 = new ValueSetter();
ValueSetter vsw2 = new ValueSetter();
Binding bx1 = new System.Windows.Data.Binding();
bx1.Path = new System.Windows.PropertyPath(literalExpression1);
bx1.Source = Source1; /////THIS IS YOUR GLOBALS OBJECT
bx1.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(vsw1, ValueSetter.DummyProperty, bx1);
Binding bx2 = new System.Windows.Data.Binding();
bx2.Path = new System.Windows.PropertyPath(literalExpression2);
bx2.Source = Source2; ////THIS IS ALSO YOUR GLOBALS OBJECT
bx2.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(vsw2, ValueSetter.DummyProperty, bx2);
vsw1.SetValue(ref vsw2);
bx1 = null;
bx2 = null;
vsw1 = null;
vsw2 = null;
And Bingo!!!! We have achieved what we wanted to.. the Global.Customers[i].CustomerName will now contain the value represented by Global.Persons[i].PersonName!!!
And the reason why this worked is because of the magic of two way binding. Setting a value from VSW2 on VSW1, caused the binding on VSW1 to automatically change (update) the value on Customers[i].CustomerName which would in this case be bound to VSW1.DummyProperty.
That does it. I hope this code would be found useful. And uh oh... where is XAML and all that WPF thingi.... surprise.. we are not at all touching it here... we are only using the DRB framework...that goes to prove how cool the WPF DRB framework is. Moreover, at this instance we are assigning a primitive value (string) to another string. You can extend this example by actually writing ValueConverter classes and assigning an instance of those to Binding.Converter property.
Lastly... Not even a single line of Reflection!!!
That's all for now... Cheers!!!