views:

220

answers:

5

Hello,

i wish to create a form at runtime that will read the columns for any datasource and create fields based on the columns and datatype just like a datagridviews insert line

Best regards, Mark

A: 

Use control data binding. It will do all the work for you.

alemjerus
No it won't. Data binding only works where you have a control to bind.
ProfK
+2  A: 

What you are doing sounds a lot like how PropertyGrid already works, which is essentially:

foreach(PropertyDescriptor prop in TypeDescriptor.GetProperties(obj)) {
    object val = prop.GetValue(obj);
    string s = prop.Converter.ConvertToString(val);
    Control cont = // TODO: create some control and set x/y
    cont.Text = s;
    this.Controls.Add(cont);
}

To avoid lots of work with alignment, using Dock to set the positions might help:

        using(Form form = new Form())
        using (PropertyGrid grid = new PropertyGrid())
        {
            form.Text = obj.ToString(); // why not...
            grid.Dock = DockStyle.Fill;
            form.Controls.Add(grid);
            grid.SelectedObject = obj;
            form.ShowDialog(this);
        }

I wonder if it is easier to use PropertyGrid in simple circumstances, though. Or there are some 3rd-party versions that work similarly.

Marc Gravell
That looks like the trick!
Mark
how does propertygrid get the names and datatypes. its just what i need just id like to make a custom format. i suppose it could extend it..?
Mark
prop.Name, prop.DisplayName, prop.PropertyType, etc.
Marc Gravell
A: 

I don't fully understand your question. Is it correct that you want to create a Windows form which provides input fields (textboxes, checkboxes, etc.) for all fields/properties of an object that you feed to the form as its DataSource?

You might have to use reflection for this (see the System.Reflection namespace). For example, to get a list of all properties:

using System.Reflection;

....

public object DataSource;

...

Debug.Assert( DataSource != null );
var properties = DataSource.GetType().GetProperties();

You would then instantiate one input control per property:

foreach ( var property in properties )
{
    // extract some information about each property:
    string propertyName = property.Name;
    Type propertyType = property.PropertyType;
    bool propertyReadOnly = !property.CanWrite;

    // create input controls based on this information:
    // ...
}

However, it might be fairly tricky to reliably map property types to the correct input control; for example, what are you going to do when you encounter a property with some unknown class as its type, or when a property is a collection of values? You might have to create a sub-form inside your form in some cases; in other cases, a listbox might be enough.

stakx
For data-binding, it should *ideally* use `TypeDescriptor` / `PropertyDescriptor`, to support `ICustomTypeDescriptor`, `TypeDescriptionProvider`, `ITypedList`, etc.
Marc Gravell
is this how the above propertygrid works internally?yes that is correct. what idealy i want to do is show a form with a datagrid in it that enables a user to select one row and return the databound object(the datasource is specified by an external source)the datasource should then be filterable by any one of its properties(using linq)so the external source would call something likeCustomer selectedCustomer =(Customer) new SearchResult().Execute(DALConnector.Dc.Customers);
Mark
public partial class SearchResult : Form{ public Object Execute(Object dataSource) { SearchResults.DataSource = dataSource; if (ShowDialog() == DialogResult.OK) { if (SearchResults.CurrentRow != null) return SearchResults.CurrentRow.DataBoundItem; } return null; }
Mark
A: 

I've recently built a sample project that uses the Dynamic Data assemblies of ASP.NET to do just this for a WPF grid, but I'm sure you could adapt the concept to WinForms. Dynamic Data provides much richer metadata than just reflection or the database, but it does require an Entity Data Model, or a LINQ to SQL data model.

basically, all you need is a reference to System.Web.DymamicData, and maybe you can find something useful in my class:

public class DynamicDataGridBuilder<TContext, TEntity> where TEntity : EntityObject
{
    readonly MetaModel model = new MetaModel();
    public DynamicDataGridBuilder()
    {            
        model.RegisterContext(typeof(TContext), new ContextConfiguration { ScaffoldAllTables = true });
    }

    public void BuildColumns(DataGrid targetGrid)
    {
        MetaTable metaTable = model.GetTable(typeof(TEntity));

        // Decision whether to auto-generated columns still rests with the caller.
        targetGrid.Columns.Clear();
        foreach (var metaColumn in metaTable.Columns.Where(x => x.GetType().Name == "MetaColumn" && x.Scaffold))      
        {
            switch (metaColumn.ColumnType.Name)
            {
                case "Boolean":
                    targetGrid.Columns.Add(new DataGridCheckBoxColumn { Binding = new Binding(metaColumn.Name), Header = metaColumn.DisplayName });
                    break;
                default:
                    targetGrid.Columns.Add(new DynamicDataGridTextColumn { MetaColumn = metaColumn, Binding = new Binding(metaColumn.Name), Header = metaColumn.DisplayName });
                    break;
            }
        }
    }
}

TContext is the type of your object model, and TEntity the type of the entity / class in that model your want to generate controls for.

ProfK
A: 

Ok so heres what i came up with!

public partial class Form2 : Form
{
    private Boolean isBrowsable(PropertyInfo info)
    {
        return info.GetCustomAttributes(typeof(BrowsableAttribute), false).Length>-1;
    }
    public Form2()
    {
        InitializeComponent();
    }
    public Form2(Boolean showCheckBoxes)
    {
        InitializeComponent();
        _showCheckBoxes = true;
    }

    private Boolean _showCheckBoxes;
    private Object _reflection;
    private TableLayoutPanel _table =  new TableLayoutPanel{Dock=DockStyle.Fill, CellBorderStyle = TableLayoutPanelCellBorderStyle.Single};

    public Object SelectedObject
    {
        get
        {
            return _reflection;
        }
        set
        {
            //clear all controls from the table
            _table.Controls.Clear();

            foreach (var property in _reflection.GetType().GetProperties())
            {
                if (isBrowsable(property))
                {
                    if ((property.PropertyType == typeof(int)) || (property.PropertyType == typeof(string)))
                    {
                        var textField = new TextBox { Dock = DockStyle.Fill, AutoSize = true };
                        textField.DataBindings.Add("Text", _reflection, property.Name);


                        _table.Controls.Add(textField, 2, _table.RowCount += 1);

                        var propertyLabel = new Label
                        {
                            Text = property.Name,
                            Dock = DockStyle.Fill,
                            TextAlign = ContentAlignment.MiddleLeft
                        };

                        _table.Controls.Add(propertyLabel, 1, _table.RowCount);

                        if (_showCheckBoxes)
                        {
                            var checkBox = new CheckBox
                                               {
                                                   AutoSize = true,
                                                   Name = property.Name,
                                                   Dock = DockStyle.Left,
                                                   CheckAlign = ContentAlignment.TopLeft
                                               };
                            _table.Controls.Add(checkBox, 0, _table.RowCount);
                        }
                    }
                }
            }
            //add one extra row to finish alignment

            var panel = new Panel { AutoSize = true };
            _table.Controls.Add(panel, 2, _table.RowCount += 1);
            _table.Controls.Add(panel, 1, _table.RowCount);
            if (_showCheckBoxes)
            {
                _table.Controls.Add(panel, 0, _table.RowCount);
            }
            Controls.Add(_table);


            if (!Controls.Contains(_table))
                Controls.Add(_table);
        }
    }

    public Boolean Execute(Object reflection)
    {
        SelectedObject = reflection;
        return ShowDialog() == DialogResult.OK;
    }
}

thanks all!

Mark