Customise the display of a auto-generated class - c#

I have a DB and I created classes to access/manage it using SQLMetal. Now, I use LINQ to SQL, and I want to display the result of queries in a data grid view. when I do that, the columns are named after my columns in the DB table, and all the properties are displayed. I know I could change this by using DisplayName and Browseable attributes, but because the classes are auto-generated I can't just add this attributes where there are needed. I came up with three workarounds:
creating an Adopter to adopt my classes. I'm still not sure how exactly you make an adopter for this case.
creating another program that will run after the generating of the code that will add the attributes. this seems like an hack, and I prefer to separate between the functionality and the GUI, so this method is on hold.
using the MetaDataType attribute. I couldn't get this to work, and it's requires, as far as I know, that the classes and the metadata class will be in the same DLL.
How do I do the customization? is there another way? what way should I take and how?
EDIT: forgotten to mention: I'm using winforms, but if it will simplify things, I will move to WPF.

You can set a Types Metadata Type at run time by registering it with the TypeDescriptor manually.
That goes something like this.
var type = typeof(Foo);
var metadataType = typeof(FooMetadata);
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType), type);
To show it all in context this will show a single column in the data grid with the header "Custom Bar".
public class Foo
{
public string Bar { get; set; }
public string DontShowMe { get; set; }
}
public class FooMetadata
{
[DisplayName("Custom Bar")]
public string Bar { get; set; }
[Browsable(false)]
public string DontShowMe { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var type = typeof(Foo);
var metadataType = typeof(FooMetadata);
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType), type);
this.dataGridView1.DataSource = new List<Foo> { new Foo { Bar = "Foobar" } };
}
}
The is also a TypeDescriptor.RemoveProviderTransparent if you want to switch the Metadata Type on the go but keep in mind that setting/unsetting it applies to the whole application domain and so threading needs to be taken into account.

By using the WPF DataGrid you can easily customize the auto generated columns by using the AutoGeneratingColumn event.

Why can't you use the data grid view's Columns collection to change DisplayNames and Visibles at runtime?

Related

Where to create the class

I'm trying to model a production system with "facility" as Class and some subclasses down to "Activity". The facility has a name as only parameter (at the moment), and I'd like to create an instance of the class reading the name as an input from a textbox. Since "activity" is inherit the properties from it's "parent classes" I'll create an instance of the class "activity" and not it's parent.
The problem is that I don't know where to create the class and how to pass it so that when I add the first subclass "Workstation" I can edit the properties of the same "activity" I created earlier.
I don't really have any code to add at this point unfortunately, but please tell me if there's anything special you'd like to see and I'll try to add it to the post.
And by the way, it's in the shape of a WinForm application with a GUI I'm trying to do this.
There are a couple things to note here. First, you'll want to use the Composite pattern to encapsulate the relationships between your classes. (For those who don't understand the OP's type hierarchy, it does make perfect sense in a factory context. There are many activities going on, which can be grouped into workstations and at a higher level into facilities.)
So, you should probably have a base Activity class (that supports the Composite pattern by exposing a collection of child activities), and then your "levels" (like Facility and Workstation) will inherit from Activity. Each of these classes will have unique properties.
The following classes should be created in their respective files, e.g. Activity.cs, Factory.cs, Workstation.cs:
class Activity
{
// An attribute that every Activity may need: a displayable name.
// This might be useful if you have a TreeView, e.g., showing all the activities.
public string Name { get; private set; }
// Every Activity could have child activities - this is the Composite pattern.
// You can loop through these to navigate through the hierarchy of your data.
// (This is often done using recursion; see example below with GetAllWorkstations().)
public List<Activity> ChildActivities { get; private set; }
public Activity()
{
ChildActivities = new List<Activity>();
}
public override string ToString() { return Name; }
}
class Factory : Activity
{
public string City { get; private set; }
public string Address { get; private set; }
}
class Workstation : Activity
{
public string WorkstationNumber { get; private set; }
}
The responsibility of loading your model then has to be handled somewhere. A good place to do it is in your main form. For example, you might write code like this:
class MainForm : Form
{
private readonly List<Factory> topLevelFactoryActivities;
public MainForm()
{
// ... other code
topLevelFactoryActivities = LoadTopLevelFactoryActivities();
}
private IEnumerable<Factory> LoadTopLevelFactoryActivities()
{
var factories = new List<Factory>();
// TODO: Load the factories, e.g. from a database or a file.
// You can load all the child objects for each factory here as well,
// or wait until later ("lazy-loading") if you want to.
// NOTE: If this becomes complex, you can move the LoadTopLevelFactoryActivities()
// method to its own class, which then becomes your "data access layer" (DAL).
return factories;
}
}
Now, if you want to find all the workstations that are part of a particular factory, you would write a method like the following on the Factory class:
class Factory : Activity
{
// ... other code
public IEnumerable<Workstation> GetAllWorkstations()
{
return GetWorkstationsRecursive(this);
}
private IEnumerable<Workstation> WorkstationsIn(Activity parentActivity)
{
foreach (var workstation in parentActivity.ChildActivities.OfType<Workstation>)
{
// Uses a C# feature called 'iterators' - really powerful!
yield return workstation;
}
foreach (var childActivity in parentActivity.ChildActivities)
{
// Using recursion to go down the hierarchy
foreach (var workstation in WorkstationsIn(childActivity))
{
yield return workstation;
}
}
}
}
You would call it like so, e.g. in your main form:
class MainForm : Form
{
// ... other code
public MainForm()
{
// ... other code
// Assume this is assigned to the factory that you want to get all the workstations for
Factory myFactory;
var workstations = myFactory.GetAllWorkstations();
// Now you can use 'workstations' as the items source for a list, for example.
}
}
As an example use case, you might want to show a second form (that belongs to the main form) which shows a list of all the workstations. (In practice you probably shouldn't create too many windows; prefer building a nonoverlapping layout. But just to show how you might pass the model instances around...)
class WorkstationListForm : Form
{
private IEnumerable<Workstation> workstations;
public WorkstationListForm(IEnumerable<Workstation> workstations)
{
this.workstations = workstations;
//TODO: You can now use 'workstations' as the ItemsSource of a list view in this form.
}
}
You could, of course, make topLevelFactoryActivities public on your MainForm and pass the variable this of the MainForm to the WorkstationListForm constructor instead. Then you could access the member on MainForm like this:
public WorkstationListForm(MainForm mainForm)
{
var topLevelFactoryActivities = mainForm.topLevelFactoryActivities;
// Now WorkstationListForm has full access to all the data on MainForm. This may or
// may not be helpful (it's usually best to minimize sharing and public fields).
}
Second, you'll want to use a proper separation between your view (user interface code/classes) and your model (the Activity hierarchy).
Third, if there's going to be any kind of live data being pushed to the user interface then you'll need a databinding mechanism to automatically update the view whenever the model changes.
In general, #2 & #3 are popularly addressed via the Model-View-ViewModel pattern. There is an excellent tutorial here for building an MVVM app using WinForms/C#.
That should get you started, at least. Also see an answer to a similar question. (Sorry about promoting my own answer, but I don't want to type out the whole example twice. Please forgive me. :))

Expanding functionality to .NET Controls

Currently I'm working in a tool that needs to create some sort of customized .NET with some additional fields. I've been wondering what approach is better to solve this, and been thinking in the following options:
Option A:
- Create a derived class for each control (let's say, CLabel, CGroupBox...) where I define the new fields on each class. This would mean worse mantainability but would be easy to operate with.
Example:
class CLabel: Label
{
public List<Control> RelatedControls {get; set;}
public String textHint;
// more stuff...
public CLabel()
{}
}
Option B:
- This option would mean not creating a derived class from each control, but using the actual Label, GroupBox, etc controls and creating a class encapsulating all the "extra" properties. This extra properties object would be referenced in the Control.Tag property. I have doubts about this one because referencing a sort of complex object inside the Tag property feels a bit crappy to me, but this would mean better mantainability and also of course no need of subclassing controls.
Example:
Label lbl = new Label();
lbl.Tag = new ControlDescription();
Option C:
- This would mean having some sort of combination of Option A and B. Just creating the Custom Control, for example CLabel, that adds a type ControlDescription field to the Label control. This way we ensure encapsulation and mantainability, but we avoid the Tag thing.
I'm pretty sure there are lots of options out there better than those. Probably just using polymorphism - a concept I still have problems with - the custom control classes could be taken away. What of those options do you think is the best one? Do you think that all of this could be better done?
If you need easy control of the properties at design-time and you must go with windows forms you should have a lock at this http://msdn.microsoft.com/en-us/library/system.componentmodel.iextenderprovider.aspx
This way you can provide properties to different controls without wrapping them first controls.
If I correctly understand you, I'd prefer to use extension methods here. If you need to store some custom values for each control - you can store them in the static dictionary (Control, Value) but use extension methods to access it, so it would look like you have controls with some values added.
Added an example:
public static class Extension
{
private static Dictionary<Control, string> _controlStrings;
public static void GetString(this Control ctrl)
{
return _controlStrings[ctrl];
}
}
After that you can use this method like:
Button btn = new Button();
string s = btn.GetString();
Probably you could try this:
public class ExtensionData
{
//put your data here
}
public class Extended<T>
where T : Control
{
public Extended( T baseControl )
{
BaseControl = baseControl;
}
public T BaseControl { get; set; }
public ExtensionData Extension { get; set; }
}
It's like your "option B", but without using "Tag" property

Winforms Data Binding to Custom Class

I am trying to bind some Winform objects to a custom class, more specifically an instance of my custom class which I have added to the Form in the code. C#, .NET 2010 Express.
For example, here is a fragment of the class, and the UserInfoForm
public class UserInfo
{
[XmlAttribute]
public string name = "DefaultName";
[XmlAttribute]
public bool showTutorial = true;
[XmlAttribute]
public enum onCloseEvent = LastWindowClosedEvent.Exit;
}
public enum LastWindowClosedEvent
{
MainMenu,
Exit,
RunInBackground
}
public partial class Form1 : Form
{
UserInfo userToBind = new UserInfo();
TextBox TB_userName = new TextBox();
CheckBox CB_showTutorial = new CheckBox();
ComboBox DDB_onCloseEvent = new ComboBox();
public Form1()
{
InitializeComponent();
}
}
Now, I would like to bind the values of these form controls to their respective value in userToBind, but have had no luck. All the tutorials I can find are either way out of date (2002), or about binding controls to a dataset, or other type of database.
I am obviously overlooking something, but I haven't figured out what.
Thank you very much for any info you can share.
More info: UserInfo is designed to be XML-friendly so it can be saved as a user profile. UserInfo will contain other custom XML classes, all nested under the UserInfo, and many controls will only need to access these child classes.
You can use the DataBindings property of your controls (textbox, checkbox...) to add a binding to a specific control. For instance:
public Form1()
{
InitializeComponent();
TB_userName.DataBindings.Add("Text", userToBind, "name");
}
Also, IIRC, data binding only works on properties, so you'll first need to modify your UserInfo class accordingly. Moreover, if you want the UI to update automatically when modifying your objects in code, you must implement INotifyPropertyChanged in your custom classes.

C# Databinding: Create object if null

I have a report object (i.e. a business object) which has several dozen fields to populate. Each field by itself has INotifyPropertyChanged implemented. There is an accessor property for the active report called ActiveReport.
What I want to do is be able to Close the current report, without necessarily opening a new one, and be able to automatically create a report object when the user starts to enter data again.
Here is a rough idea of the structure. ActiveReport is the current report. The GUI is able to directly set the fields of the subclass (name/email) through binding. I want a new BusinessObject to be created when name is being set, but ActiveReport is null. One additional caveat, the report object is auto-generated from XSD files, so I'd rather not have to modify those.
class ControlClass {
public BusinessObject ActiveReport { get; set; }
}
class BusinessObject {
UserInfo field1 { get; set; }
}
class UserInfo : INotifyPropertyChanged {
DependencyProperty name;
DependencyProperty email;
}
I thought of the following scenarios:
Accessor property.
The binding does not seem to use the accessor.
Inserting a check into all event handlers.
I'd rather not have to resort to this -- this breaks the rationale behind using MVVM.
Multibinding
This would require the use of a converter class and instance, and that seems like overkill.
Converter
I thought to ask if there were any other good programming models for this in WPF.
You could create a behavior.
in it, you check if (AssociatedObject.DataContext as ReportObject) is null
and if it is, clear all your fields / set your datacontext / whatever
This should do the trick:
public class ControlClass
{
public BusinessObject ActiveReport { get; set; }
private UserInfo _editableUserData
public UserInfo EditableUserData
{
get { return _editableUserData; }
set
{
if (_editableUserData != null)
_editableUserData.PropertyChanged -= UserDataChanged;
_editableUserData = value;
if (_editableUserData != null)
_editableUserData.PropertyChanged += UserDataChanged;
RaisePropertyChanged("EditableUserData");
}
}
private void UserDataChanged(object sender, PropertyChangedEventArgs e)
{
if (ActiveReport == null)
ActiveReport = new BusinessObject(EditableUserData);
}
}

Aggregate detail values in Master-Detail view

I have a master-detail relationship in some custom entities. Say I have the following structure:
class Master : INotifyPropertyChanged
{
public int Id { get; set; } // + property changed implementation
public string Name { get; set; } // + property changed implementation
public ObservableCollection<Detail> Details { get; }
}
class Detail : INotifyPropertyChanged
{
public int Id { get; private set; }
public string Description { get; set; }
public double Value { get; set; } // + property changed implementation
}
My goal is to have a ListView, using a GridView, showing a list of Master objects. When I select a specific Master, I'll have a separate ListView for the details, allowing editing. Basically, a fairly standard Master-Detail view.
However, I also want the GridView for the Master to show the Sum of all of that master's Detail elements, ie: Details.Select(d => d.Value).Sum();
This is fairly easy to display using a custom IValueConverter. I can convert from the details collection directly to a double displaying sum, and bind a TextBlock's Text to the Details OneWay, via the IValueConverter. This will work, and show the correct values when I open the window.
However, if I change one of the detail members, this will not update (even though detail implements INotifyPropertyChanged), since the collection itself is still the same (the ObservableCollection reference hasn't changed).
I want to have an aggregated value in a master list, showing the sum (or average/count/etc) within the detail list, and have this stay up to date when the user changes properties in details. How can I go about implementing this?
Edit:
Ideally, I would prefer if there is a means of accomplishing this that doesn't involve changing the Master class directly. The application in question is using the MVVM pattern, and I'd really prefer to not change my Model classes in order to implement a specific View. Is there a way to do this without introducing custom logic into the model?
I was considering possibilities with the UI where you'd make the binding explicit and perform binding/updates from a command... but it seems that the easiest way to do it would be to extend the ObservableCollection to add/remove listeners to each Detail instance as its added/removed, then just fire CollectionChanged when any of them change. Call it DeeplyObservableCollection<T>.
class Master : INotifyPropertyChanged
{
public int Id { get; set; } // + property changed implementation
public string Name { get; set; } // + property changed implementation
public double Sum {get {return Details.Sum(x=>x.Value);}}
public DeeplyObservableCollection<Detail> Details { get; }
// hooked up in the constructor
void OnDOCChanged(object sender, CollectionChangedEventArgs e)
{ OnPropertyChanged("Sum"); }
}
Worst case you'd have to wrap an ObservableCollection in another type if you can't properly override all the methods you need...

Categories