views:

56

answers:

3

I have a datagridView, that is bound to a List. This List is made up of my class which contains 2 public properties, a String Name, and another List CustomList. See below:

public class MyClass2
{        
    public string Name
    { get; set;}


    public string Description
    {
        get;
        set;
    }
}

public class MyClass
{
    List<MyClass2> myList;

    public string Name
    {
        get;
        set;
    }

    public List<MyClass2> CustomList
    {
        get { return myList ?? (myList= new List<MyClass2>()); }
    }

}

And then in my designer page:

List<MyClass> myClassList = new List<MyClass>();
dataGridView.DataSource = myClassList;

As it is right now, the only column that appears in the grid, is the MyClass:Name column, and the CustomList column does not show up. What I'd like is the CustomList column to show and to display something like "Collection" with the "..." button showing, and when it is clicked to have the "Collection Editor" to popup.

Does anyone know if this is possible and how to enable it? If there's a tutorial or anything that would help me out I'd appreciate that too. Thanks.

A: 

What you want to do is add a column template with a button in it:

http://geekswithblogs.net/carmelhl/archive/2008/11/11/126942.aspx

In the handler for the button, get the selected MyClass item from the collection and bind its list property to a grid in your popup.

Dave Swersky
This is not ASP.NET.
leppie
it could be, but either way the solution should work.
Matt Ellen
+1  A: 

Short answer: Yes, you can do it with some code.

Long answer: To write the code is gonna be a pain in the ass, as you would have to know not only how the DataGridView behaves with custom columns, but you would need to know how to expose design time elements at runtime, which requires quite a bit of plumbing. Extensive knowledge about the PropertyGrid must also be known.

Note: This might a fun component to write. (I might actually tackle it if I get some time)

leppie
+1  A: 

So using the 'button' approach posted by Dave, and some code that I found that implements the CollectionEditor, I can edit the CustomList in MyClass2

Here's my solution, although not quite as clean as I'd like:

Put this class somewhere:

class MyHelper : IWindowsFormsEditorService, IServiceProvider, ITypeDescriptorContext
{
    public static void EditValue(IWin32Window owner, object component, string propertyName)
    {
        PropertyDescriptor prop = TypeDescriptor.GetProperties(component)[propertyName];
        if (prop == null) throw new ArgumentException("propertyName");
        UITypeEditor editor = (UITypeEditor)prop.GetEditor(typeof(UITypeEditor));
        MyHelper ctx = new MyHelper(owner, component, prop);
        if (editor != null && editor.GetEditStyle(ctx) == UITypeEditorEditStyle.Modal)
        {
            object value = prop.GetValue(component);
            value = editor.EditValue(ctx, ctx, value);
            if (!prop.IsReadOnly)
            {
                prop.SetValue(component, value);
            }
        }
    }
    private readonly IWin32Window owner;
    private readonly object component;
    private readonly PropertyDescriptor property;
    private MyHelper(IWin32Window owner, object component, PropertyDescriptor property)
    {
        this.owner = owner;
        this.component = component;
        this.property = property;
    }
    #region IWindowsFormsEditorService Members

    public void CloseDropDown()
    {
        throw new NotImplementedException();
    }

    public void DropDownControl(System.Windows.Forms.Control control)
    {
        throw new NotImplementedException();
    }

    public System.Windows.Forms.DialogResult ShowDialog(System.Windows.Forms.Form dialog)
    {
        return dialog.ShowDialog(owner);
    }

    #endregion

    #region IServiceProvider Members

    public object GetService(Type serviceType)
    {
        return serviceType == typeof(IWindowsFormsEditorService) ? this : null;
    }

    #endregion

    #region ITypeDescriptorContext Members

    IContainer ITypeDescriptorContext.Container
    {
        get { return null; }
    }

    object ITypeDescriptorContext.Instance
    {
        get { return component; }
    }

    void ITypeDescriptorContext.OnComponentChanged()
    { }

    bool ITypeDescriptorContext.OnComponentChanging()
    {
        return true;
    }

    PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor
    {
        get { return property; }
    }

    #endregion

Add a button column to the data grid:

DataGridViewButtonColumn butt = new DataGridViewButtonColumn();
butt.HeaderText = "CustomList";
butt.Name = "CustomList";
butt.Text = "Edit CustomList...";
butt.UseColumnTextForButtonValue = true;

dataGridView.Columns.Add(butt);
dataGridView.CellClick += new DataGridViewCellEventHandler(dataGridView_CellClick);

Then call it in the button handler of the cell click.

if (e.RowIndex < 0 || e.ColumnIndex != dataGridView.Columns["CustomList"].Index)
            return;

//get the name of this column
string name = (string)dataGridView[dataGridView.Columns["Name"].Index, e.RowIndex].Value;

var myClassObject= myClassList.Find(o => o.Name == name);

MyHelper.EditValue(this, myClassObject, "CustomList");

I'd still be interested in hearing other approaches, and not having to implement my own CollectionEditor. And I'm still interested in having it look more like what the TabControl uses to add TabPages in the PropertyGrid...by displaying the "..." button...but this might work for now.

AE
This is pretty much the approach I suggested. You will have to make sure the DataGridView behaves :) Else it looks good +1
leppie