views:

580

answers:

5

Hi,

I'm displaying Business Object in generic DataGrids, and I want to set the column header through a custom attribute, like:

class TestBo
 {
    [Header("NoDisp")]
    public int ID {get; set;}

    [Header("Object's name")]
    public String Name { get; set; }
}

So far, so good, but I'd also want to separate my display from my data, by inheritance:

class TestBO
{
   public int ID {get; set;}
   public String Name { get; set; }
}

class TestPresentationBO : TestBO
{
  //Question: how to simply set the Header attribute on the different properties?
}

I see a solution via reflection with a SetCustomAttribute in the Child constructor, but it will be cumbersome, so is there a simple and elegant trick for this problem?

Please prevent me from breaking the data/presentation separation ;o)

+2  A: 

Make the properties in TestBo virtual and override them in TestPresentationBO. That way you can add the attributes.

Steven
+3  A: 

Question: how to simply set the Header attribute on the different properties?

There is no way to set an attribute on an inherited member the way you have suggested, since attributes are specific to a type. SetCustomAttribute won't help you - it's only any good when you construct new types at runtime. Once an attribute has been compiled in you cannot change it at runtime, since it's part of the metadata.

If you want to maintain the separation you will have to find another way.

(You could make the properties virtual, override them in the Presentation class and add attributes on the overrides, but this looks dodgy and doesn't really separate anything - you end up with a complete TestBO class in your TestPresentationBO anyway...)

romkyns
+2  A: 

You can do it like WCF RIA Services. Add an attribute to TestBO, like [Presentation] taking a type as parameter. This new type will redefine the properties, but with the presentation attributes. At run-time, you have to get the identity of the new type and get the custom attributes of its properties.

Or forget about the attribute and have a dictionary mapping the BO with the presentation BO class. This presentation BO class does the same thing as above, i.e. redefine properties with custom attributes.

the presentation BO class is never instantiated, it is simply reflected upon to get presentation info.

Timores
A: 

Just thinking, can't you solve this with partial classes and the MetadatatypeAttribute? MVC2 uses this pattern for Model validation.

Jesper Palm
+2  A: 

Are you using the MVVM (model view view-model) pattern? It seems to me, and partly from the other answers, that you can't really do this with the custom attributes like you want. But, it also seems to me that your TestPresentationBO is really just like a "View Model" for TestBO. A view model is basically a sort of wrapper or surrogate for a business or logic class--which is basically what you want. (This summary of a view model may not be 100% accurate; I'm just starting out with MVVM myself.)

You can create a TestBOViewModel to wrap TestBO, then pass the collection of TestBOViewModel to the datagrid. Of course, you can decorate the properties exposing the wrapped class with [Header("Object's name")] etc. This doesn't use inheritance, but I don't see why you'd need to use inheritance in this situation. Using a view model, does, however, cleanly separate your presentation (view) from your data (model) by using the wrapper (view model).

For more info on the MVVM pattern, I found this to be an interesting read: WPF Apps With The Model-View-ViewModel Design Pattern.

Something like this. Of course, you can add validation and other goodies in here too.

public class TestBOViewModel // extend from DependencyObject 
{                            // if you want to use dependency properties

    private TestBO _myBO;

    public TestBOViewModel(TestBO bo)
    {
        _myBO = bo;
    }

    [Header("NoDisp")]
    public int ID 
    {
        get { return _myBO.ID; }
        set { _myBO.ID = value; }
    }
}
Benny Jobigan