When Generics was first released in .Net 2.0, I was kind of surprised how few built in generic types there were. A List, a dictionary, a event handler; but not much beyond that. Nothing like what you see with C++ STL, ATL, or WTL. (turns out there are some reasons for that, C++ Templates can do things that C# Generics just can't). But no matter, we can invent our own.
In my current desktop app, I have a reoccurring need for an OK/Cancel form. Plus I had a number of things I wanted preset for me.
The first problem is working with the Generic portion of the C# language. I want to be able to create the OK/Cancel form like this:
1: var dlg = new OkCancelForm<MyDisplayControl>();
2: if (dlg.ShowModal()== DialogResult.OK)
3: {
4: // do something here...
5: }
Also, I need to be able to retrieve, or load the user control that is being displayed. I created a property called DisplayControl. So now I can load and retrieve values from the display control. I'll expand my sample:
1: var dlg = new OkCancelForm<MyDisplayControl>();
2: dlg.DisplayControl.Data = MyData; // dlg.DisplayControl will be of type MyDisplayControl
3: // data is a property of MyDisplayControl
4: var result = dlg.ShowDialog();
5: if (result == DialogResult.OK)
6: {
7: var data dlg.DisplayControl.Data;
8: // now do something with the data
9: }
OK, so that was how I wanted it to work, now onto some real code.
The Implementation
First thing to do is to just create a new form and call it OkCancelForm. Put on an OK and cancel button just like you normally would. For a bit of advice on this: I start with two panels, one on the bottom (containing the OK and Cancel buttons) docked to the bottom. And the other docked to Fill in the middle. This will host your custom control.
Now C# creates forms with 2 files, OkCancelForm.cs and OkCancelForm.designer.cs. We first want to look at the OkCancelForm.cs, and change the class declaration to what you see below:
1: public partial class OkCancelForm<T>: Form where T: ContainerControl, new()
If you compile after doing that, you will see errors (if you are using ReSharper, you wont even have to compile, you'll just see errors). Turns out we have to change the other half of the partial file implementation that makes up a WinForms form. So open up the file named OkCancelForm.designer.cs, and change the declaration to match this:
1: partial class OkCancelForm<T>
Even though the class names were the same, because one class was a generic class and the other was not, they did not match.
But, diving into the syntax a bit from the first sample. After the form declaration you see this code: "Form where T: ContainerControl, new()". This code tells the generic (T) that T must be of type ContainerControl, and must have a default constructor that takes now parameters -- because this class is going to create a new instance of T when OkCancelForm is created. You can't be passing in stuff through the controls constructor. It just wont work.
Now for some more changes to OkCancelForm.cs. Here you can see we are adding InitializeDisplayControl() to create a new copy of the display control, and we have added a property called display control of type T (which must be a ContainerControl, remember). The other little trick we are doing there is making the control full screen.
You will also see a reference to displayPanel on like 14. That is one of the two panels I mentioned previously that is docked to the form in fill mode (the other is docked to the bottom of the form and holds the OK and cancel buttons).
1: private T _displayControl;
2: public OkCancelForm()
3: {
4: InitializeComponent();
5: InitializeDisplayControl();
6: }
7:
8: public T DisplayControl { get { return _displayControl; } }
9:
10: private void InitializeDisplayControl()
11: {
12: _displayControl = new T();
13: _displayControl.Dock = DockStyle.Fill;
14: displayPanel.Controls.Add(_displayControl);
15: }
That is the basics of what you need. Still undone in this sample is validation. But the tricky part (knowing how to make a partial class generic) is pretty well spelled out for you here.
Enjoy. And if you have any questions, please leave them in the comments.
3 comments:
The little code boxes are hard to read. And putting the whole control class in one box at the end would be good.
What if the form is localizable ?
This technique does not affect localization.
Post a Comment