How to emulate missing property in object

A

Andrus

I'm implementing entity object which should populate its properties from
database when property is first referenced.

In RDL reports I use object properties like

MyObject.MyProperty

MyObject is instance of MyEntity class. There is no MyProperty property in
MyObject at design time.

So I need to catch missing property reference and provide its value at run
time.
Something line

public class MyEntity {
override exception MissingPropertyGetException {
return dbo.ExecuteScalar("SELECT "+Exception.UnreferencedPropertyName+
" FROM myEntity");
}
}

Any idea how to implement this ?

Andrus.
 
M

Marc Gravell

If RDL uses standard data-binding via the component model (which I
would expect for compatibility with DataTable etc), then perhaps look
into ICustomTypeDescriptor (1.1 onwards) on TypeDescriptionProvider
(2.0 onwards); you can provide your own runtime properties that can
bear as little or as much resemblance as you choose to the class
properties (although reflection is the default).

Marc
 
M

Marc Gravell

As an example of TypeDescriptionProvider, perhaps look here:

http://www.codeproject.com/csharp/HyperPropertyDescriptor.asp

Forget about all the Reflection.Emit stuff (that is way off topic);
the important bit is the override of
CustomTypeDescriptor.GetProperties(); get hold of the original set
from "base", copy into a list, add your new dummy properties and push
the list into a new PropertyDescriptionCollection.

Marc
 
A

Andrus

the important bit is the override of CustomTypeDescriptor.GetProperties();
get hold of the original set from "base", copy into a list, add your new
dummy properties and push the list into a new
PropertyDescriptionCollection.

Marc,

thank you for excellent information.
I found that I need to emulate static method call since FYIReporting engine
does not allow to use properties in reports.

I'm new to C# so I created testcase below based on your article.

How to catch MyStaticMethod call in HyperTypeDescriptionProvider class and
return string "test" ?


Andrus.

using System.ComponentModel;

using System;

using System.Reflection;

static class Program {

static void Main() {

Type t = typeof(MyEntity);

MethodInfo mInfo = t.GetMethod("MyStaticMethod");

Object returnVal = mInfo.Invoke(t, null);

System.Windows.Forms.MessageBox.Show(returnVal.ToString());

}

}

[TypeDescriptionProvider(typeof(HyperTypeDescriptionProvider))]

class MyEntity {

// I need to emulate the following method:

// public static string MyStaticMethod() { return "test"; }

}

sealed class HyperTypeDescriptionProvider : TypeDescriptionProvider {

// How to catch MyStaticMethod call and return string "test" ?

}
 
M

Marc Gravell

I'm pretty sure that the component model cover neither statics not
methods, being oriented towards instance methods.

However, I'm also very confused generally; you started off talking
about RDL and properties... now, I've used RDL to generate reports
from OO data, and it worked just fine, with values hanging off
instance properties.

Sorry to be a pain, but can you clarify (again) exactly what you are
trying to do? I may be ablt to knock up a test case... Because I'm
really not sure that static methods are the way to go here!

Marc
 
M

Marc Gravell

Long post warning!

OK; I threw together a "from first principles" example of a
strongly-typed bindable (both via properties and
INotifyPropertyChanged) property-bag implementation using .Net 2.0
principles, and a trivial winform harness. I'd be interested to know
if this (with suitable property definitions) works with your RDL...

Note that this is a *simplified* version of such, as it only supports
a single type. In production code I would expect Bag to act as a
base-class, and as such a few changes would have to be made to
identify the correct (current) type (rather than using typeof(Bag)).

Marc

using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

Bag.AddProperty<int>("TestProp", new
DefaultValueAttribute(5));
Bag.AddProperty<string>("Name");

Bag bag = new Bag();
using (Form form = new Form())
using (Button b1 = new Button())
using (Label l1 = new Label())
using (Label l2 = new Label()) {
form.DataBindings.Add("Text", bag, "Name");
l1.DataBindings.Add("Text", bag, "Name");
l2.DataBindings.Add("Text", bag, "TestProp");
b1.Dock = l1.Dock = l2.Dock = DockStyle.Top;
b1.Text = "Update name";
b1.Click += delegate {
TypeDescriptor.GetProperties(bag)["Name"].SetValue(bag,
DateTime.Now.ToLongTimeString());
};
form.Controls.AddRange(new Control[] { b1, l1, l2 });
form.ShowDialog();
}
}
}

interface IBag {
void OnAfterValueChanged(string propertyName);
void AddHandler(object key, EventHandler value);
void RemoveHandler(object key, EventHandler value);
void OnEvent(object key);
IBagValue GetValue(string propertyName);
}
interface IBagValue {
void ResetValue();
bool IsDefaultValue { get;}
object Value { get; set;}
event EventHandler ValueChanged;
}
interface IBagDefinition {
IBagValue Create(IBag bag);
PropertyDescriptor Property { get;}
}
sealed class BagDefinition<T> : IBagDefinition {
private readonly PropertyDescriptor property;
public PropertyDescriptor Property { get { return property; } }
private readonly T defaultValue;
public T DefaultValue { get { return defaultValue; } }
public string Name { get { return Property.Name; } }
IBagValue IBagDefinition.Create(IBag bag) {
return new BagValue<T>(bag, this);
}
public BagDefinition(string propertyName, Attribute[] attributes)
{
defaultValue = default(T);
if (attributes != null) { // check for a default value
foreach (Attribute attrib in attributes) {
DefaultValueAttribute defAttrib = attrib as
DefaultValueAttribute;
if (defAttrib != null) {
defaultValue = (T)defAttrib.Value;
break;
}
}
}
property = new BagPropertyDescriptor(propertyName,
attributes);
}
internal class BagPropertyDescriptor : PropertyDescriptor {
public BagPropertyDescriptor(string name, Attribute[]
attributes) : base(name, attributes) { }
private IBagValue GetBagValue(object component) {
return ((IBag)component).GetValue(Name);
}
public override object GetValue(object component) {
return GetBagValue(component).Value;
}
public override void SetValue(object component, object value)
{
GetBagValue(component).Value = value;
}
public override Type ComponentType {
get { return typeof(Bag); }
}
public override Type PropertyType {
get { return typeof(T); }
}
public override bool IsReadOnly {
get { return false; }
}
public override bool CanResetValue(object component) {
return true;
}
public override void ResetValue(object component) {
GetBagValue(component).ResetValue();
}
public override bool ShouldSerializeValue(object component) {
return !GetBagValue(component).IsDefaultValue;
}
public override bool SupportsChangeEvents {
get { return true; }
}
public override void AddValueChanged(object component,
EventHandler handler) {
GetBagValue(component).ValueChanged += handler;
}
public override void RemoveValueChanged(object component,
EventHandler handler) {
GetBagValue(component).ValueChanged -= handler;
}

}
}
sealed class BagValue<T> : IBagValue, ITypeDescriptorContext {
private T value;
private readonly IBag bag;
void IBagValue.ResetValue() {
Value = Definition.DefaultValue;
}
bool IBagValue.IsDefaultValue {
get { return EqualityComparer<T>.Default.Equals(Value,
Definition.DefaultValue); }
}
private readonly BagDefinition<T> definition;
public IBag Bag { get { return bag; } }
public BagDefinition<T> Definition { get { return definition; } }
public BagValue(IBag bag, BagDefinition<T> definition) {
if (bag == null) throw new ArgumentNullException("bag");
if (definition == null) throw new
ArgumentNullException("definition");
this.bag = bag;
this.definition = definition;
Value = Definition.DefaultValue;
}
public T Value {
get { return value; }
set {
if (EqualityComparer<T>.Default.Equals(Value, value))
return;
this.value = value;
Bag.OnAfterValueChanged(Definition.Name);
Bag.OnEvent(Definition);
}
}
public event EventHandler ValueChanged {
add { Bag.AddHandler(Definition, value); }
remove { Bag.RemoveHandler(Definition, value); }
}
object IBagValue.Value {
get { return Value; }
set { Value = (T) value; }
}

IContainer ITypeDescriptorContext.Container { get { return
null; } }
object ITypeDescriptorContext.Instance { get { return Bag; } }
void ITypeDescriptorContext.OnComponentChanged() {
Bag.OnAfterValueChanged(Definition.Name); }
bool ITypeDescriptorContext.OnComponentChanging() { return true; }
PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor { get
{return Definition.Property;} }
object IServiceProvider.GetService(Type serviceType) { return
null; }
}

sealed class Bag : IBag, INotifyPropertyChanged {
private EventHandlerList events;
void IBag.AddHandler(object key, EventHandler handler) {
AddHandler(key, handler);
}
void IBag.RemoveHandler(object key, EventHandler handler) {
RemoveHandler(key, handler);
}
void IBag.OnEvent(object key) {
OnEvent(key);
}
private void AddHandler(object key, Delegate handler) {
if (handler == null) return;
if (events == null) events = new EventHandlerList();
events.AddHandler(key, handler);
}
private void RemoveHandler(object key, Delegate handler) {
if (events == null || handler == null) return;
events.RemoveHandler(key, handler);
}
private void OnEvent(object key) {
if (events == null) return;
EventHandler handler = events[key] as EventHandler;
if (handler != null) handler(this, EventArgs.Empty);
}
public event PropertyChangedEventHandler PropertyChanged {
add { AddHandler(EVENT_PropertyChanged, value); }
remove { RemoveHandler(EVENT_PropertyChanged, value); }
}
private static readonly object EVENT_PropertyChanged = new
object();
private void OnPropertyChanged(string propertyName) {
if (events == null) return;
PropertyChangedEventHandler handler =
events[EVENT_PropertyChanged] as PropertyChangedEventHandler;
if (handler != null) handler(this, new
PropertyChangedEventArgs(propertyName));
}
void IBag.OnAfterValueChanged(string propertyName) {
OnPropertyChanged(propertyName);
}
IBagValue IBag.GetValue(string propertyName) {
return GetValue(propertyName);
}
private readonly Dictionary<string, IBagValue> values =
new Dictionary<string,
IBagValue>(StringComparer.InvariantCulture);
private IBagValue GetValue(string propertyName) {
lock (values) {
IBagValue value;
if (!values.TryGetValue(propertyName, out value)) {
value = CreateValue(this, propertyName);
values.Add(propertyName, value);
}
return value;
}
}

static readonly Dictionary<string, IBagDefinition> defintions =
new Dictionary<string,
IBagDefinition>(StringComparer.InvariantCulture);

public static void AddProperty<T>(string propertyName, params
Attribute[] attributes) {
BagDefinition<T> def = new BagDefinition<T>(propertyName,
attributes);
lock (defintions) {
defintions.Add(propertyName, def);
BagDescriptionProvider.ResetProperties();
}
}
internal static PropertyDescriptorCollection GetProperties() {
lock (defintions) {
PropertyDescriptor[] props = new
PropertyDescriptor[defintions.Count];
int i = 0;
foreach (IBagDefinition def in defintions.Values) {
props[i++] = def.Property;
}
return new PropertyDescriptorCollection(props, true);
}
}
static IBagValue CreateValue(IBag bag, string propertyName) {
lock (defintions) {
return defintions[propertyName].Create(bag);
}
}

public Bag() { }
static Bag() {
BagDescriptionProvider.Initialize();
}

}

sealed class BagDescriptionProvider : TypeDescriptionProvider {
static readonly BagTypeDescriptor descriptor;

[MethodImpl(MethodImplOptions.NoInlining)]
internal static void Initialize() { } // to force static ctor
static BagDescriptionProvider() {
ICustomTypeDescriptor parent =
TypeDescriptor.GetProvider(typeof(Bag)).GetTypeDescriptor(typeof(Bag));
descriptor = new BagTypeDescriptor(parent);
TypeDescriptor.AddProvider(new BagDescriptionProvider(),
typeof(Bag));
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type
objectType, object instance) {
return descriptor;
}
internal static void ResetProperties() {
descriptor.ResetProperties();
}
}
sealed class BagTypeDescriptor : CustomTypeDescriptor {
public BagTypeDescriptor(ICustomTypeDescriptor parent)
: base(parent) {
if (parent == null) throw new ArgumentNullException("parent");
}
private PropertyDescriptorCollection properties;
internal void ResetProperties() {
properties = null;
}
public override PropertyDescriptorCollection
GetProperties(Attribute[] attributes) {
return GetProperties();
}
public override PropertyDescriptorCollection GetProperties() {
if (properties == null) {
properties = Bag.GetProperties();
}
return properties;
}
}
 
A

Andrus

I'm pretty sure that the component model cover neither statics not
methods, being oriented towards instance methods.

I can use instance methods also, if static method emulation is not possible.
I can also use properties but this requires changing RDLEngine, i'm not sure
I have enough knowledge for it chaning.
However, I'm also very confused generally; you started off talking
about RDL and properties... now, I've used RDL to generate reports
from OO data, and it worked just fine, with values hanging off
instance properties.

Sorry to be a pain, but can you clarify (again) exactly what you are
trying to do? I may be ablt to knock up a test case... Because I'm
really not sure that static methods are the way to go here!

Marc,

I'm creating Winforms application which uses MS ReportViewer control in
local mode and also www.fyireporting.com RDLViewer and Designer .
I'm try make RDLDesigner and MS Report Designer more usable for end users
for creating invoice layouts and other reports.

I want to allow users to use any field from some one row tables in report
expressions.
Those one row tables contain current company information, current user
information, current workplace information, current POS information
(separate table for each entity).

Table field name may be not know at application design time since users can
add fields to tables at run time.

My idea is:

1. Create classes Company, User, Computer, POS in some dll.
2. Add dll containing those classes to to report
3. Use classes in report expressionis like

Company.Name()

Company.CompanySpecialField()

4. Method CompanySpecialField() is not defined in Company class.
I need to catch call to CompanySpecialField() and al other unknown methods
and return field value from database by executing

ExecuteScalar("SELECT CompanySpecialField FROM Company")

I can use Company.Get("Name") but this syntax is to much typing

So I'm looking a way to use

Company.CompanySpecialField()

or if this is not possible use instance method

Code.Company.CompanySpecialField()

or use static property (this requires rdl engine changing but would be best
since contains fewes number of characters)

Company.CompanySpecialField

or even use instance property

Code.Company.CompanySpecialField


Andrus.
 
A

Andrus

Marc,

thank you very much for this code.

I'm confused.

Is this only way to to too much code for dynamicproperty adding ?

I looked into into it at found that this requires that property must be
added to object before calling it.

How to trap nonexisting property calling action and add property in this
case by class itself ?

Andrus.

Marc Gravell said:
Long post warning!

OK; I threw together a "from first principles" example of a strongly-typed
bindable (both via properties and INotifyPropertyChanged) property-bag
implementation using .Net 2.0 principles, and a trivial winform harness.
I'd be interested to know if this (with suitable property definitions)
works with your RDL...

Note that this is a *simplified* version of such, as it only supports a
single type. In production code I would expect Bag to act as a base-class,
and as such a few changes would have to be made to identify the correct
(current) type (rather than using typeof(Bag)).

Marc

using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

Bag.AddProperty<int>("TestProp", new DefaultValueAttribute(5));
Bag.AddProperty<string>("Name");

Bag bag = new Bag();
using (Form form = new Form())
using (Button b1 = new Button())
using (Label l1 = new Label())
using (Label l2 = new Label()) {
form.DataBindings.Add("Text", bag, "Name");
l1.DataBindings.Add("Text", bag, "Name");
l2.DataBindings.Add("Text", bag, "TestProp");
b1.Dock = l1.Dock = l2.Dock = DockStyle.Top;
b1.Text = "Update name";
b1.Click += delegate {
TypeDescriptor.GetProperties(bag)["Name"].SetValue(bag,
DateTime.Now.ToLongTimeString());
};
form.Controls.AddRange(new Control[] { b1, l1, l2 });
form.ShowDialog();
}
}
}

interface IBag {
void OnAfterValueChanged(string propertyName);
void AddHandler(object key, EventHandler value);
void RemoveHandler(object key, EventHandler value);
void OnEvent(object key);
IBagValue GetValue(string propertyName);
}
interface IBagValue {
void ResetValue();
bool IsDefaultValue { get;}
object Value { get; set;}
event EventHandler ValueChanged;
}
interface IBagDefinition {
IBagValue Create(IBag bag);
PropertyDescriptor Property { get;}
}
sealed class BagDefinition<T> : IBagDefinition {
private readonly PropertyDescriptor property;
public PropertyDescriptor Property { get { return property; } }
private readonly T defaultValue;
public T DefaultValue { get { return defaultValue; } }
public string Name { get { return Property.Name; } }
IBagValue IBagDefinition.Create(IBag bag) {
return new BagValue<T>(bag, this);
}
public BagDefinition(string propertyName, Attribute[] attributes) {
defaultValue = default(T);
if (attributes != null) { // check for a default value
foreach (Attribute attrib in attributes) {
DefaultValueAttribute defAttrib = attrib as
DefaultValueAttribute;
if (defAttrib != null) {
defaultValue = (T)defAttrib.Value;
break;
}
}
}
property = new BagPropertyDescriptor(propertyName, attributes);
}
internal class BagPropertyDescriptor : PropertyDescriptor {
public BagPropertyDescriptor(string name, Attribute[] attributes) :
base(name, attributes) { }
private IBagValue GetBagValue(object component) {
return ((IBag)component).GetValue(Name);
}
public override object GetValue(object component) {
return GetBagValue(component).Value;
}
public override void SetValue(object component, object value) {
GetBagValue(component).Value = value;
}
public override Type ComponentType {
get { return typeof(Bag); }
}
public override Type PropertyType {
get { return typeof(T); }
}
public override bool IsReadOnly {
get { return false; }
}
public override bool CanResetValue(object component) {
return true;
}
public override void ResetValue(object component) {
GetBagValue(component).ResetValue();
}
public override bool ShouldSerializeValue(object component) {
return !GetBagValue(component).IsDefaultValue;
}
public override bool SupportsChangeEvents {
get { return true; }
}
public override void AddValueChanged(object component, EventHandler
handler) {
GetBagValue(component).ValueChanged += handler;
}
public override void RemoveValueChanged(object component,
EventHandler handler) {
GetBagValue(component).ValueChanged -= handler;
}

}
}
sealed class BagValue<T> : IBagValue, ITypeDescriptorContext {
private T value;
private readonly IBag bag;
void IBagValue.ResetValue() {
Value = Definition.DefaultValue;
}
bool IBagValue.IsDefaultValue {
get { return EqualityComparer<T>.Default.Equals(Value,
Definition.DefaultValue); }
}
private readonly BagDefinition<T> definition;
public IBag Bag { get { return bag; } }
public BagDefinition<T> Definition { get { return definition; } }
public BagValue(IBag bag, BagDefinition<T> definition) {
if (bag == null) throw new ArgumentNullException("bag");
if (definition == null) throw new
ArgumentNullException("definition");
this.bag = bag;
this.definition = definition;
Value = Definition.DefaultValue;
}
public T Value {
get { return value; }
set {
if (EqualityComparer<T>.Default.Equals(Value, value)) return;
this.value = value;
Bag.OnAfterValueChanged(Definition.Name);
Bag.OnEvent(Definition);
}
}
public event EventHandler ValueChanged {
add { Bag.AddHandler(Definition, value); }
remove { Bag.RemoveHandler(Definition, value); }
}
object IBagValue.Value {
get { return Value; }
set { Value = (T) value; }
}

IContainer ITypeDescriptorContext.Container { get { return null; } }
object ITypeDescriptorContext.Instance { get { return Bag; } }
void ITypeDescriptorContext.OnComponentChanged() {
Bag.OnAfterValueChanged(Definition.Name); }
bool ITypeDescriptorContext.OnComponentChanging() { return true; }
PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor { get
{return Definition.Property;} }
object IServiceProvider.GetService(Type serviceType) { return null; }
}

sealed class Bag : IBag, INotifyPropertyChanged {
private EventHandlerList events;
void IBag.AddHandler(object key, EventHandler handler) {
AddHandler(key, handler);
}
void IBag.RemoveHandler(object key, EventHandler handler) {
RemoveHandler(key, handler);
}
void IBag.OnEvent(object key) {
OnEvent(key);
}
private void AddHandler(object key, Delegate handler) {
if (handler == null) return;
if (events == null) events = new EventHandlerList();
events.AddHandler(key, handler);
}
private void RemoveHandler(object key, Delegate handler) {
if (events == null || handler == null) return;
events.RemoveHandler(key, handler);
}
private void OnEvent(object key) {
if (events == null) return;
EventHandler handler = events[key] as EventHandler;
if (handler != null) handler(this, EventArgs.Empty);
}
public event PropertyChangedEventHandler PropertyChanged {
add { AddHandler(EVENT_PropertyChanged, value); }
remove { RemoveHandler(EVENT_PropertyChanged, value); }
}
private static readonly object EVENT_PropertyChanged = new object();
private void OnPropertyChanged(string propertyName) {
if (events == null) return;
PropertyChangedEventHandler handler = events[EVENT_PropertyChanged]
as PropertyChangedEventHandler;
if (handler != null) handler(this, new
PropertyChangedEventArgs(propertyName));
}
void IBag.OnAfterValueChanged(string propertyName) {
OnPropertyChanged(propertyName);
}
IBagValue IBag.GetValue(string propertyName) {
return GetValue(propertyName);
}
private readonly Dictionary<string, IBagValue> values =
new Dictionary<string, IBagValue>(StringComparer.InvariantCulture);
private IBagValue GetValue(string propertyName) {
lock (values) {
IBagValue value;
if (!values.TryGetValue(propertyName, out value)) {
value = CreateValue(this, propertyName);
values.Add(propertyName, value);
}
return value;
}
}

static readonly Dictionary<string, IBagDefinition> defintions =
new Dictionary<string,
IBagDefinition>(StringComparer.InvariantCulture);

public static void AddProperty<T>(string propertyName, params
Attribute[] attributes) {
BagDefinition<T> def = new BagDefinition<T>(propertyName,
attributes);
lock (defintions) {
defintions.Add(propertyName, def);
BagDescriptionProvider.ResetProperties();
}
}
internal static PropertyDescriptorCollection GetProperties() {
lock (defintions) {
PropertyDescriptor[] props = new
PropertyDescriptor[defintions.Count];
int i = 0;
foreach (IBagDefinition def in defintions.Values) {
props[i++] = def.Property;
}
return new PropertyDescriptorCollection(props, true);
}
}
static IBagValue CreateValue(IBag bag, string propertyName) {
lock (defintions) {
return defintions[propertyName].Create(bag);
}
}

public Bag() { }
static Bag() {
BagDescriptionProvider.Initialize();
}

}

sealed class BagDescriptionProvider : TypeDescriptionProvider {
static readonly BagTypeDescriptor descriptor;

[MethodImpl(MethodImplOptions.NoInlining)]
internal static void Initialize() { } // to force static ctor
static BagDescriptionProvider() {
ICustomTypeDescriptor parent =
TypeDescriptor.GetProvider(typeof(Bag)).GetTypeDescriptor(typeof(Bag));
descriptor = new BagTypeDescriptor(parent);
TypeDescriptor.AddProvider(new BagDescriptionProvider(),
typeof(Bag));
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type
objectType, object instance) {
return descriptor;
}
internal static void ResetProperties() {
descriptor.ResetProperties();
}
}
sealed class BagTypeDescriptor : CustomTypeDescriptor {
public BagTypeDescriptor(ICustomTypeDescriptor parent)
: base(parent) {
if (parent == null) throw new ArgumentNullException("parent");
}
private PropertyDescriptorCollection properties;
internal void ResetProperties() {
properties = null;
}
public override PropertyDescriptorCollection GetProperties(Attribute[]
attributes) {
return GetProperties();
}
public override PropertyDescriptorCollection GetProperties() {
if (properties == null) {
properties = Bag.GetProperties();
}
return properties;
}
}
 
M

Marc Gravell

Nobody ever said that working with dynamic properties would be easy
;-p
If you want easy, then see if you can declare a DataTable and throw
that at your RDL; the resultant DataView works very similarly, but
much less code (but it instance oriented rather than type oriented).

As for detecting non-existant properties... you don't want a lot, eh?

The following changes appear to work, but it is stepping a little bit
away from the centre-line IMO (obviously you'd need to figure out how
to define the property; I went for a basic "object")

public static PropertyDescriptor AddProperty<T>(string
propertyName, params Attribute[] attributes) {
BagDefinition<T> def = new BagDefinition<T>(propertyName,
attributes);
lock (defintions) {
defintions.Add(propertyName, def);
BagDescriptionProvider.ResetProperties();
}
return def.Property;
}

internal static PropertyDescriptorCollection GetProperties() {
lock (defintions) {
PropertyDescriptor[] props = new
PropertyDescriptor[defintions.Count];
int i = 0;
foreach (IBagDefinition def in defintions.Values) {
props[i++] = def.Property;
}
return new PhantomPropertyDescriptorCollection(props,
false);
}
}

internal sealed class PhantomPropertyDescriptorCollection :
PropertyDescriptorCollection {
public
PhantomPropertyDescriptorCollection(PropertyDescriptor[] properties,
bool readOnly)
: base(properties, readOnly) {
}
public override PropertyDescriptor Find(string name, bool
ignoreCase) {
PropertyDescriptor prop = base.Find(name, ignoreCase);
if (prop == null) {
prop = AddProperty<object>(name);
Add(prop);
}
return prop;
}
}
 
A

Andrus

The following changes appear to work

Mark,

Thank you.
I created a test sample.
However,

System.Reflection.PropertyInfo p = bag.GetType().GetProperty("test");

assigns null to p.
I think I'm missing something simple.
What I'm doing wrong ?

How to simplify this code ?
How to assign some string to property test so that Messagebox outputs string
?

Dynamic property implementation like this in Python requires only few lines
of code.
Any idea how to implement static property or method emulation ?

Andrus.


using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

static class Program {
[STAThread]
static void Main() {
Bag bag = new Bag();
// the following line assigns null to p. Why ?
System.Reflection.PropertyInfo p = bag.GetType().GetProperty("test");

System.Windows.Forms.MessageBox.Show(p.GetValue(bag,null ).ToString());
}
}

interface IBag {
void OnAfterValueChanged(string propertyName);
void AddHandler(object key, EventHandler value);
void RemoveHandler(object key, EventHandler value);
void OnEvent(object key);
IBagValue GetValue(string propertyName);
}

interface IBagValue {
void ResetValue();
bool IsDefaultValue { get;}
object Value { get; set;}
event EventHandler ValueChanged;
}

interface IBagDefinition {
IBagValue Create(IBag bag);
PropertyDescriptor Property { get;}
}

sealed class BagDefinition<T> : IBagDefinition {
private readonly PropertyDescriptor property;
public PropertyDescriptor Property { get { return property; } }
private readonly T defaultValue;
public T DefaultValue { get { return defaultValue; } }
public string Name { get { return Property.Name; } }

IBagValue IBagDefinition.Create(IBag bag) {
return new BagValue<T>(bag, this);
}

public BagDefinition(string propertyName, Attribute[] attributes) {
defaultValue = default(T);
if (attributes != null) { // check for a default value
foreach (Attribute attrib in attributes) {
DefaultValueAttribute defAttrib = attrib as
DefaultValueAttribute;
if (defAttrib != null) {
defaultValue = (T)defAttrib.Value;
break;
}
}
}
property = new BagPropertyDescriptor(propertyName, attributes);
}


internal class BagPropertyDescriptor : PropertyDescriptor {
public BagPropertyDescriptor(string name, Attribute[] attributes)
: base(name, attributes) { }

private IBagValue GetBagValue(object component) {
return ((IBag)component).GetValue(Name);
}

public override object GetValue(object component) {
return GetBagValue(component).Value;
}

public override void SetValue(object component, object value) {
GetBagValue(component).Value = value;
}

public override Type ComponentType {
get { return typeof(Bag); }
}

public override Type PropertyType {
get { return typeof(T); }
}

public override bool IsReadOnly {
get { return false; }
}

public override bool CanResetValue(object component) {
return true;
}

public override void ResetValue(object component) {
GetBagValue(component).ResetValue();
}

public override bool ShouldSerializeValue(object component) {
return !GetBagValue(component).IsDefaultValue;
}
public override bool SupportsChangeEvents {
get { return true; }
}

public override void AddValueChanged(object component,
EventHandler handler) {
GetBagValue(component).ValueChanged += handler;
}

public override void RemoveValueChanged(object component,
EventHandler handler) {
GetBagValue(component).ValueChanged -= handler;
}

}
}

sealed class BagValue<T> : IBagValue, ITypeDescriptorContext {
private T value;
private readonly IBag bag;
void IBagValue.ResetValue() {
Value = Definition.DefaultValue;
}
bool IBagValue.IsDefaultValue {
get {
return EqualityComparer<T>.Default.Equals(Value,
Definition.DefaultValue);
}
}
private readonly BagDefinition<T> definition;
public IBag Bag { get { return bag; } }
public BagDefinition<T> Definition { get { return definition; } }
public BagValue(IBag bag, BagDefinition<T> definition) {
if (bag == null) throw new ArgumentNullException("bag");
if (definition == null) throw new ArgumentNullException("definition");
this.bag = bag;
this.definition = definition;
Value = Definition.DefaultValue;
}

public T Value {
get { return value; }
set {
if (EqualityComparer<T>.Default.Equals(Value, value))
return;
this.value = value;
Bag.OnAfterValueChanged(Definition.Name);
Bag.OnEvent(Definition);
}
}

public event EventHandler ValueChanged {
add { Bag.AddHandler(Definition, value); }
remove { Bag.RemoveHandler(Definition, value); }
}

object IBagValue.Value {
get { return Value; }
set { Value = (T)value; }
}

IContainer ITypeDescriptorContext.Container {
get {
return null;
}
}
object ITypeDescriptorContext.Instance { get { return Bag; } }
void ITypeDescriptorContext.OnComponentChanged() {
Bag.OnAfterValueChanged(Definition.Name);
}
bool ITypeDescriptorContext.OnComponentChanging() { return true; }
PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor {
get { return Definition.Property; }
}
object IServiceProvider.GetService(Type serviceType) {
return null;
}
}

sealed class Bag : IBag, INotifyPropertyChanged {

private EventHandlerList events;
void IBag.AddHandler(object key, EventHandler handler) {
AddHandler(key, handler);
}
void IBag.RemoveHandler(object key, EventHandler handler) {
RemoveHandler(key, handler);
}
void IBag.OnEvent(object key) {
OnEvent(key);
}
private void AddHandler(object key, Delegate handler) {
if (handler == null) return;
if (events == null) events = new EventHandlerList();
events.AddHandler(key, handler);
}
private void RemoveHandler(object key, Delegate handler) {
if (events == null || handler == null) return;
events.RemoveHandler(key, handler);
}
private void OnEvent(object key) {
if (events == null) return;
EventHandler handler = events[key] as EventHandler;
if (handler != null) handler(this, EventArgs.Empty);
}
public event PropertyChangedEventHandler PropertyChanged {
add { AddHandler(EVENT_PropertyChanged, value); }
remove { RemoveHandler(EVENT_PropertyChanged, value); }
}
private static readonly object EVENT_PropertyChanged = new object();
private void OnPropertyChanged(string propertyName) {
if (events == null) return;
PropertyChangedEventHandler handler = events[EVENT_PropertyChanged] as
PropertyChangedEventHandler;
if (handler != null) handler(this, new
PropertyChangedEventArgs(propertyName));
}

void IBag.OnAfterValueChanged(string propertyName) {
OnPropertyChanged(propertyName);
}

IBagValue IBag.GetValue(string propertyName) {
return GetValue(propertyName);
}

private readonly Dictionary<string, IBagValue> values =
new Dictionary<string, IBagValue>(StringComparer.InvariantCulture);

private IBagValue GetValue(string propertyName) {
lock (values) {
IBagValue value;
if (!values.TryGetValue(propertyName, out value)) {
value = CreateValue(this, propertyName);
values.Add(propertyName, value);
}
return value;
}
}

static readonly Dictionary<string, IBagDefinition> defintions =
new Dictionary<string,
IBagDefinition>(StringComparer.InvariantCulture);

public static PropertyDescriptor AddProperty<T>(string propertyName,
params Attribute[] attributes) {
BagDefinition<T> def = new BagDefinition<T>(propertyName, attributes);
lock (defintions) {
defintions.Add(propertyName, def);
BagDescriptionProvider.ResetProperties();
}
return def.Property;
}

internal static PropertyDescriptorCollection GetProperties() {
lock (defintions) {
PropertyDescriptor[] props = new PropertyDescriptor[defintions.Count];
int i = 0;
foreach (IBagDefinition def in defintions.Values) {
props[i++] = def.Property;
}
return new PhantomPropertyDescriptorCollection(props, false);
}
}

internal sealed class PhantomPropertyDescriptorCollection :
PropertyDescriptorCollection {

public PhantomPropertyDescriptorCollection(PropertyDescriptor[]
properties, bool readOnly)
: base(properties, readOnly) {
}

public override PropertyDescriptor Find(string name, bool ignoreCase) {
PropertyDescriptor prop = base.Find(name, ignoreCase);
if (prop == null) {
prop = AddProperty<object>(name);
Add(prop);
}
return prop;
}
}

static IBagValue CreateValue(IBag bag, string propertyName) {
lock (defintions) {
return defintions[propertyName].Create(bag);
}
}

public Bag() { }
static Bag() {
BagDescriptionProvider.Initialize();
}

}

sealed class BagDescriptionProvider : TypeDescriptionProvider {
static readonly BagTypeDescriptor descriptor;

[MethodImpl(MethodImplOptions.NoInlining)]
internal static void Initialize() { } // to force static ctor
static BagDescriptionProvider() {
ICustomTypeDescriptor parent =
TypeDescriptor.GetProvider(typeof(Bag)).GetTypeDescriptor(typeof(Bag));
descriptor = new BagTypeDescriptor(parent);
TypeDescriptor.AddProvider(new BagDescriptionProvider(), typeof(Bag));
}
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
object instance) {
return descriptor;
}

internal static void ResetProperties() {
descriptor.ResetProperties();
}
}

sealed class BagTypeDescriptor : CustomTypeDescriptor {
public BagTypeDescriptor(ICustomTypeDescriptor parent)
: base(parent) {
if (parent == null) throw new ArgumentNullException("parent");
}
private PropertyDescriptorCollection properties;
internal void ResetProperties() {
properties = null;
}
public override PropertyDescriptorCollection GetProperties(Attribute[]
attributes) {
return GetProperties();
}
public override PropertyDescriptorCollection GetProperties() {
if (properties == null) {
properties = Bag.GetProperties();
}
return properties;
}
}
 
M

Marc Gravell

PropertyInfo is reflection; it tells you what is actually defined on a
type. PropertyDescriptor is component-model, and supports virtual
properties.
 
A

Andrus

PropertyInfo is reflection; it tells you what is actually defined on a
type. PropertyDescriptor is component-model, and supports virtual
properties.

Marc,

thank you.

As I understand from this message, ReportViewer cannot read virtual
property value.
Also there is no way to get value of virtual property in code. So I cannot
use this id reports.

I also added real property "test" to bag class.

bag.GetType().GetProperty("test");

still returns null.

Andrus.
 

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