Databinding of complex business layer classes

D

Dirk

Hello,

I have a problem to use databinding with my business layer classes. My
data class does not have simple properties (string, int or datetime),
instead, all my properties are objects of the generic type Field<T>
(see sample code).

public class Employee
{
public Field<string> Forename
{
get
{
if(m_Forename == null)
m_Forename = new Field<string>(....);

return m_Forename;
}
}

public Field<string> Lastname
{
get
{
if(m_Lastname == null)
m_Lastname= new Field<string>(....);

return m_Lastname;
}
}

// The Employee class has many more properties with the Field<T>
datatype.
}


public class Field<T>
{
public T CurrentValue
{
get
{
// Get Field from database...
return ...;
}
set
{
// Save Value in Database

// Save last change date.

// Save NT-Username of the User who made the last changes
}
}

public DateTime LastChangeDate
{
get
{
return ...;
}
}

public string LastChangeUserName
{
get
{
return ...;
}
}

// I have some more informations for each Field...
}



When I now try to use databinding of a collection of those objects to
da grid (I use the Janus GridEx, but the problem is a general
databinding problem), I have the problem that the databinding
expression only accepts one-level-property names "Forename". What I
need is something like "Forename.CurrentValue", but that is not
possible.

I know that it would be possible to add every Property in the Employee
class twice like

public string FornameValue
{
get
{
return this.Forname.CurrentValue;
}
}


But this is a ugly solution.

Does anyone have an idea how I can change the Field<T> class so that I
can use it with Databinding?

Regards
Dirk
 
J

Joanna Carter [TeamB]

"Dirk" <[email protected]> a écrit dans le message de (e-mail address removed)...

| I have a problem to use databinding with my business layer classes. My
| data class does not have simple properties (string, int or datetime),
| instead, all my properties are objects of the generic type Field<T>

Do not expose the Field<T> objects as public properties. You really need to
create a base business class that is capable of holding a list of these
field objects and then access them the derived classes.

internal abstract class Field
{
private string name;

internal Field(string name)
{
this.name = name;
}

public string Name
{
get { return name; }
}
}

internal class Field<T> : Field
{
private T value;

...

internal T Value
{
get { return ...; }
set { ... = value; }
}
}

public abstract class Base
{
private Dictionary<string, Field> fields = new Dictionary<string,
Field>();

protected bool ContainsField(string fieldName)
{
return field.ContainsKey(fieldName);
}

protected void Add<T>(string fieldname)
{
fields[fieldname] = new Field<T>(fieldName);
}

protected T GetFieldValue<T>(string fieldName)
{
return fields[fieldName].Value;
}

protected void SetFieldValue<T>(string fieldName, T value)
{
fields[fieldName].Value = value;
}
}

public class Employee : Base
{
public string Forename
{
get
{
string fieldName = "Forename";

if (ContainsField(fieldName))
Add<string>(fieldName);

return GetFieldValue<string>(fieldName);
}
}

I have taken this one step further and used the idea of a Data Packet which
contains the list of fields and manages all sorts of other stuff under the
hood, but this should give you the general idea.

Joanna
 
J

Joanna Carter [TeamB]

"Joanna Carter [TeamB]" <[email protected]> a écrit dans le message de
news: (e-mail address removed)...

Sorry, I forgot the cast on the fields in the accessors.

| protected T GetFieldValue<T>(string fieldName)
| {
*** return ((Field<T>) fields[fieldName]).Value;
| }
|
| protected void SetFieldValue<T>(string fieldName, T value)
| {
*** ((Field<T>) fields[fieldName]).Value = value;
| }

Joanna
 
I

intrader

Dirk said:
Hello,

I have a problem to use databinding with my business layer classes. My
data class does not have simple properties (string, int or datetime),
instead, all my properties are objects of the generic type Field<T>
(see sample code).

public class Employee
{
public Field<string> Forename
{
get
{
if(m_Forename == null)
m_Forename = new Field<string>(....);

return m_Forename;
}
}

public Field<string> Lastname
{
get
{
if(m_Lastname == null)
m_Lastname= new Field<string>(....);

return m_Lastname;
}
}

// The Employee class has many more properties with the Field<T>
datatype.
}


public class Field<T>
{
public T CurrentValue
{
get
{
// Get Field from database...
return ...;
}
set
{
// Save Value in Database

// Save last change date.

// Save NT-Username of the User who made the last changes
}
}

public DateTime LastChangeDate
{
get
{
return ...;
}
}

public string LastChangeUserName
{
get
{
return ...;
}
}

// I have some more informations for each Field...
}



When I now try to use databinding of a collection of those objects to
da grid (I use the Janus GridEx, but the problem is a general
databinding problem), I have the problem that the databinding
expression only accepts one-level-property names "Forename". What I
need is something like "Forename.CurrentValue", but that is not
possible.

I know that it would be possible to add every Property in the Employee
class twice like

public string FornameValue
{
get
{
return this.Forname.CurrentValue;
}
}


But this is a ugly solution.

Does anyone have an idea how I can change the Field<T> class so that I
can use it with Databinding?

Regards
Dirk
I like your general idea and I am struggling with similar issue. I ran
into a very good article by Alexander Gornik "Making ASP.NET databinding
via # work, without reflection"
(http://www.codeproject.com/aspnet/TypeDescriptors.asp)
Hope this helps.
The solution by Joanna is very feasible but undesirable due to the
strange and complex databinding expression you may need to use.
I would like to see her post a working small example.
Regards,
intrader
 
I

intrader

Dirk said:
Hello,

I have a problem to use databinding with my business layer classes. My
data class does not have simple properties (string, int or datetime),
instead, all my properties are objects of the generic type Field<T>
(see sample code).

public class Employee
{
public Field<string> Forename
{
get
{
if(m_Forename == null)
m_Forename = new Field<string>(....);

return m_Forename;
}
}

public Field<string> Lastname
{
get
{
if(m_Lastname == null)
m_Lastname= new Field<string>(....);

return m_Lastname;
}
}

// The Employee class has many more properties with the Field<T>
datatype.
}


public class Field<T>
{
public T CurrentValue
{
get
{
// Get Field from database...
return ...;
}
set
{
// Save Value in Database

// Save last change date.

// Save NT-Username of the User who made the last changes
}
}

public DateTime LastChangeDate
{
get
{
return ...;
}
}

public string LastChangeUserName
{
get
{
return ...;
}
}

// I have some more informations for each Field...
}



When I now try to use databinding of a collection of those objects to
da grid (I use the Janus GridEx, but the problem is a general
databinding problem), I have the problem that the databinding
expression only accepts one-level-property names "Forename". What I
need is something like "Forename.CurrentValue", but that is not
possible.

I know that it would be possible to add every Property in the Employee
class twice like

public string FornameValue
{
get
{
return this.Forname.CurrentValue;
}
}


But this is a ugly solution.

Does anyone have an idea how I can change the Field<T> class so that I
can use it with Databinding?

Regards
Dirk
I may be mistaken here, but I have seen the syntax Forename_CurrentValue
in the # syntax.
 
J

Joanna Carter [TeamB]

"intrader" <[email protected]> a écrit dans le message de [email protected]...

| The solution by Joanna is very feasible but undesirable due to the
| strange and complex databinding expression you may need to use.
| I would like to see her post a working small example.

Far from being a strange and complex databinding, it couldn't be simpler.

{
List<Employee> employees = GetEmployeeList(...);

myGrid.DataSource = employees;

...
}

If you need to bind individual controls to an Employee, then all you do is

{
Employee e = new Employee();

textBox1.DataBindings.Add("Text", e, "Forename");

...
}

Joanna
 
D

Dirk

Hello Joanna, Intrader

thank you for your help.

First I tried "Forname_CurrentValue", but it did not work. Then I
retried "Forname.CurrentValue" and I recognized that it work readonly,
editing a column with such an Binding expression doesnt seem to work?

I will now analyze which of the two solutions I will realize.

regards
Dirk
 
J

Joanna Carter [TeamB]

"Josh" <[email protected]> a écrit dans le message de (e-mail address removed)...

| I am new to generics and would like to undersatnd the code you posted. I
| cannot get it to compile howver (even with the code change in your
subsequent
| post). Can you post a complete code that will compile?

There really isn't that much that doesn't work; after all I wrote the code
in the newsreader :)

This is checked to compile and run :

// base non-generic class because we can't have use Field<T>
// for different types in a Dictionary that will only hold one type
internal abstract class Field
{
private string name;

internal Field(string name)
{
this.name = name;
}

public string Name
{
get { return name; }
}
}

// this is the real generic Field<T> class that will be instantiated and
held
// in the Dictionary<string, Field> in our base business class
internal class Field<T> : Field
{
private T value;

internal Field(string name) : base(name) { }

internal T Value
{
get { return value; }
set { this.value = value; }
}
}

public abstract class Base
{
private Dictionary<string, Field> fields = new Dictionary<string,
Field>();

protected Base()
{
// get list of publicly declared properties in class derived from this
class
PropertyInfo[] properties = GetType().GetProperties();

foreach (PropertyInfo property in properties)
{
// bind Field<> to the type of the property
Type fieldType =
typeof(Field<>).MakeGenericType(property.PropertyType);

// create an array to hold the constructor args
object[] args = new object[] { property.Name };

// use Activator to create the Field<T> instance
Field newField = (Field) Activator.CreateInstance(fieldType,
BindingFlags.Instance | BindingFlags.NonPublic, null, args, null, null);

// add the field to the private list
fields.Add(property.Name, newField);
}
}

protected T GetFieldValue<T>(string fieldName)
{
return ((Field<T>) fields[fieldName]).Value;
}

protected void SetFieldValue<T>(string fieldName, T value)
{
((Field<T>) fields[fieldName]).Value = value;
}
}

public class Employee : Base
{
public Employee() : base() { }

public string Forename
{
get { return GetFieldValue<string>("Forename"); }
set { SetFieldValue<string>("Forename", value); }
}
}

Does that help ?

Joanna
 
I

intrader

Joanna said:
"intrader" <[email protected]> a écrit dans le message de [email protected]...

| The solution by Joanna is very feasible but undesirable due to the
| strange and complex databinding expression you may need to use.
| I would like to see her post a working small example.

Far from being a strange and complex databinding, it couldn't be simpler.

{
List<Employee> employees = GetEmployeeList(...);

myGrid.DataSource = employees;

...
}

If you need to bind individual controls to an Employee, then all you do is

{
Employee e = new Employee();

textBox1.DataBindings.Add("Text", e, "Forename");

...
}

Joanna
Sorry for the characterization.
What you have described is quite simple and elegant.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top