0

enter image description here

I have a subclassed DataGridView with a DataSource of SortableBindingList<T>. I use this thing everywhere, with the same underlying configuration. The current subclassed version mostly changes the designer defaults for properties, so they don't need to be re-set for each component instance.

I want to pack a lot more functionality into the subclassed control, such as...

  • Intelligent column sorting, with the ability to sort descending on the first click of a column.
  • The ability for mouse clicks and hotkeys to raise an events to the parent, with the item T that was clicked on.
  • Clean and integrated icon display for certain columns
  • Easy row selection (from item T)
  • Easy row updating (for item T)
  • Smart context menus
  • Smart tooltips

All of the permutations I can think of require a generic class approach SortableDataGridView<T>, which breaks the designer. According to this post, there isn't a way to have your cake and eat it too, so T in the class signature is out. I need the designer support.

Is there another design pattern or approach I can that will keep the designer happy, but still give me the ability to store my SortableBindingList<T> within the component and use T in raised events?

Essentially I think I'm looking for a way to use anonymous types within the class, without using them in the class signature-

public class SortableDataGridView : System.Windows.Forms.DataGridView
{
    protected SortableBindingList<T> _data;
    ...
}

These approaches don't seem likely to work-

  • A non-generic subclass of a generic base class (probably not possible)
  • A special factory approach, where the factory method accepts but the base constructor doesn't.

The most likely solution approach is possibly to use a manager class, which interfaces closely with the SortableDataGridView. All of the type-specific operations are recast as objects for the benefit of the non-generic DataGridView, and the manager class recasts them to T before it raises events to the container.

Pseudocode;

public class SortableDataGridView : System.Windows.Forms.DataGridView
{
    protected IDataGridManager _manager;
    ...
}

public interface IDataGridManager
{
    object DataSource { get; };
    void ItemDoubleClicked(object item); 
    ...
}

public class MyDataGridManager<T> : IDataGridManager
{
    protected SortableBindingList<T> _data; 

    public object DataSource
    {
        get
        {
            return _data;
        }
    }

    public void ItemDoubleClicked(object item)
    {
        // raise event with item as T 
    }
    ...
}

Is the above a reasonable approach, or is there a better and more concise way?

Memetican
  • 381
  • 5
  • 18
  • The trick is to not use the designer. They're just classes so they work fine as long as you don't open the designer. – Enigmativity Jun 20 '17 at 09:00
  • Thanks @Enigmativity- in this case the designer is very helpful to us in keeping all of the column configurations straight. That's about the only part of the configuration I can't default into the subclassed control. Having said that, for some of the DGV's the columns are configurable and are stored in the database, so in the long run this could be a good solution for us – Memetican Jun 20 '17 at 11:43
  • In the past I have created two forms - one with the controls I update using the designer and the other the form with the generic controls. I either use inheritance or plain old copy and paste to get the designer code into the generic form code. – Enigmativity Jun 20 '17 at 12:05

1 Answers1

1

If there are only a few T and if you know them, there is a workaround.

Implement your SortableDataGridView<T> and then derive classes with your concrete types from it. So the designer knows how to instantiate the custom control.

public class SortableDataGridView<T> : System.Windows.Forms.DataGridView where T : class
{
    ...
}

public class SortableDataGridView_MyClass1 : SortableDataGridView<MyClass1>
{
  ...
}

I have tested that with a class LabelEx<T> : Label and a class LabelExString : Label<string>. The LabelExString control is shown in the WinForms Toolbox and can be used normally.

KBO
  • 653
  • 7
  • 17
  • In the primary project I'm currently working on there are about 50 DataGridViews with about 45 different T's. But as a backup plan, I like this approach for it's straightforwardness. The non-generic subclass would only take a few lines of code, and I can keep it neatly with the T class definition- each collection of DataGridView is part of a feature so that code is fairly well isolated. Thanks for that – Memetican Jun 20 '17 at 11:39