9

I remember learning VB4 and dragging a button onto a form, double-clicking on that button, and typing code into that event handler I had just been magically blessed with. Coming from QBASIC I was thrilled with the "V" in "VB", the visual designer was literally the best thing since sliced bread.

Of course you could do all that programmatically but the magic of the "V" was so appealing you just couldn't help but drag that button. We were encouraged to take that route.

But then a few years ago I started learning about C# and the .net framework and was fascinated by the way everything I thought I knew had just gone out the window. There's a lot of magic going on in VB6 that's completely unveiled in .net: take constructors and the InitializeComponents method for example. In the latter you'd find all the control instances you've dragged from the toolbox, all the events you've registered and the properties you've set in the designer.

And that's fine... I guess. It's just, I feel like I don't "own" what's going on, this code that I can only modify via the designer annoys the heck out of me. Every time you copy a button that says "Ok" from one form to another (sometimes along with its brother "Cancel"), you are actually duplicating code, and that's a sin isn't it? DRY, don't repeat yourself, says the Pope.

Religions and schools of thought aside, in all objectivity, shouldn't we derive forms from base forms instead, and have the "Ok" button (and all of its friends) live on the base form? Something like a FormBase from which derives a DialogFormBase; all classes created in no time... by typing code. The buttons are created depending on how the class is instanciated (i.e. a constructor enum argument determines which buttons are to be created), controls are laid out inside an arrangement of split panels and flow layout panels, injected into the form as Content that fits into the main content panel. Isn't this what ASP.net does with master pages and content placeholders? I'd derive a form when I need a new "master page", but this new "master page" still derives from a base form class so the visuals are consistent across the entire application.

To me that's much more code reuse than anything else I've ever done with the designer in WinForms, and it wasn't even hard, and code isn't cluttered with a 200-lines method that I have no control over, I can put comments where I like, they won't get overwritten by a designer. I guess it's just a matter of patterns and architecture, which is what brought me to this link: Best design for Windows forms that will share common functionality, where I realized I was spot on, except the answer there, suggests exactly what I'm doing, but it's advising against form inheritance because of designer considerations. That's the part I don't get. I would rather advise against using the designer because of code structure considerations, especially in regards with form and control inheritance, which I see no reason to avoid other than well it breaks the designer.

We can't all be just lazy, so what part am I missing?

Mathieu Guindon
  • 1,720
  • 16
  • 33
  • I don't understand. Are you implying that we should not use visual designers and write all GUI code by hand? – Euphoric Mar 23 '13 at 16:41
  • Pre .NET where was the code that controlled the all the objects dragged on the form? – JeffO Mar 24 '13 at 19:21
  • 1
    @JeffO judging from the legacy code I have dealt with, I'd say right on the form, somewhere between ad-hoc inline SQL and business logic. Point is, WinForms *is* .net; so if the designer is working well with what *nowadays* smells like "bad code" and "bad coding habits" and actually breaks when you start structuring things up, I say drop the designer and treat forms as what they are (classes!)... and how is coding a UI layer in C# so different from coding a UI layer in ExtJS, anyway? It's "tedious" to do that in WinForms because "hey look there's a designer"? Is coding a ExtJS UI tedious? – Mathieu Guindon Mar 24 '13 at 22:13
  • I am going to quote another user: CodeCaster ; An event handler is meant to connect the GUI to your business logic. If you have a textbox to enter a user's name and an Add button, clicking the Add button should merely call _userRepository.AddUser(UsernameTextbox.Text). You don't want business logic in event handlers. http://stackoverflow.com/questions/8048196/functionality-code-in-event-handler-bad-practice – Pieter B Mar 25 '13 at 10:16
  • @Pieter doesn't that put a DAL dependency into your presentation layer? – Mathieu Guindon Mar 25 '13 at 18:26
  • @retailcoder the topicstarter complains that a lot of code needlessly gets duplicated. Which makes no sense. But you shouldn't have logic in your eventhandlers. If you have a button that rings a bell and you copy/paste it, it's sensible to have another button that rings a bell. But ofcourse not all the code to ring the bell should be copied, just the call to ringing that bell. You do this by not having the ring-bell code in the event handler. And then, it makes sense that the code gets copied. Because it will only be a call not logic. – Pieter B Mar 25 '13 at 19:08
  • @PieterB what I meant, was that calling a repository method inside the UI layer means your UI layer somehow has a reference to your Data Access Layer (DAL) - UI code shouldn't even know about data; I'd make the bell-rigning (or user-saving) button's Click event handler handled in the business layer, and leave it up to the business layer to access the data and give the UI a new/refreshed view model if/when needed. As for the event handlers, I encapsulate them in a Behavior class instance (merely a dictionary of handlers) and call into that: the UI layer doesn't even own the click-handling code. – Mathieu Guindon Mar 25 '13 at 21:35

4 Answers4

10

I'm gonna oppose you with different paradigm : Favor composition over inheritance.

Even when you start writing GUI code by hand and use inheritance to share behavior and visuals between forms, you will hit same problem as normal OOP design. Lets say you have DialogForm, that has OK/Cancel buttons and GridForm, that has grid. You derive all dialogs from DialogForm and all forms with Grid from GridForm. And then you find out you want form with both. How would you solve it? If you are using form inheritance, then there is no way solve it. On the other side, if you are using UserControls, then you can simply put GridControl on your form and be done with it. And you can still use designer with UserControls, so you don't have to waste time writing tedious GUI code.

Aaron McIver
  • 3,262
  • 16
  • 19
Euphoric
  • 36,735
  • 6
  • 78
  • 110
  • Well the base form merely has a SplitContainer with Panel2 fixed, containing a FlowLayoutPanel called `BottomPanel` and hosting the common Ok/Cancel/Apply/Close behaviors; Panel1 contains a SplitContainer with Panel1 fixed (to host common "instructions" label, or menus, toolbars, whatever goes on top) and Panel2 holding a protected Panel called `MainContent`; each actual implementation decides how to fill it up. All content can be injected into the implementation and yes, this can be a user control made with the designer to save time, good point.. but what about the form itself? – Mathieu Guindon Mar 23 '13 at 17:31
  • The problem here is scale. When you take application with 10 forms, then having single common form is no big deal. The problems start when you have application with dozens or hundreds of forms. Then, you will have forms that have pieces common, but never enough to create base form. In you case, it might happen you want to use different layout or different buttons, but keep everything else same. And thats when the problems start. – Euphoric Mar 23 '13 at 17:41
  • I find writing UI code just boils down to defining what controls you need and how you're configuring them. Writing the UI code yourself gives you a taste of exactly what UI code is supposed to be doing, so that when you start putting business logic and data access in there you're more likely to ask yourself whether you're coding things at the right place; as for scale, like I said the content is injected into the form so this implies I have *content* that could derive from other *content*... but I don't *need* to derive from another *form*. – Mathieu Guindon Mar 23 '13 at 17:44
  • 2
    The 'problem with scale" does not exist if you have a halfway decent design. We have a project where I work with a few hundred forms. We use form inheritance heavily to provide consistency and common functionality, and we've never had any problems with it. – Mason Wheeler Mar 23 '13 at 19:01
  • 2
    @MasonWheeler I had exactly opposite experience. We tried to use form inheritance in one of our applications, and every time we did even little change in the base forms, the whole thing broke down and we had to manually fix all other forms. Also, designer behaves weird when you work with inherited form. – Euphoric Mar 23 '13 at 21:40
  • 1
    @Euphoric: Which designer? We use Delphi, and its designer works just fine with inherited forms. And again, you don't have these problems *if you have a good design*. If you don't plan anything out, of course it's all going to be brittle and break anytime you touch it, but that's no different from any other code. – Mason Wheeler Mar 23 '13 at 23:07
2

Much of this is tied to the technology stack of choice.

WinForms and WPF may both be .NET UI frameworks but vary greatly in how you achieve the end goal. Certain paradigms move between technologies just fine but others don't and you shouldn't try and force a paradigm simply because you feel it's meant to exist in every framework. There is a reason that MVVM is geared at WPF/SL and MVC is a seperate concept in ASP.NET from WebForms and so on. Certain technologies work better with certain paradigms.

Keep in mind that generated code should generally be removed from your DRY concerns. While applicable in some instances, more often than not it should be ignored. DRY concepts should certainly remain a focus outside your Presentation layer but at the Presentation layer generated code is more often than not a byproduct of the framework you are working in. This is not to say you can't apply DRY principles in your approach but remain cautious. Could you create a base Form and inherit from it indefinitely? Yes. Could you drag and drop components on a new Form each and every time as needed? Yes. Keep focused on the end goal, avoiding potential defects, making it easy to refactor downstream, and scalability while working within the confines of the technology stack of choice.

Aaron McIver
  • 3,262
  • 16
  • 19
0

You can use inheritance providing you correctly encapsulate your fields and populate accordingly. If you were keen to use inheritance my suggestion would be to have the base form and a corresponding Model for that form, and for each derivation derive both the form and the Model.

A far better alternative would be to group related controls into one or more UserControl controls. You just need a little bit of logic to put the relevant data into it.

Sam
  • 6,152
  • 2
  • 20
  • 34
0

I totally agree with Mason Wheeler, although in three years maybe even himself had changed his mind.

I´m trying to look for alternatives to migrate from Delphi executables to the WEB.

Up to now I decided myself for UNIGUI, because I can still use form inheritance. I would like Delphi to have Mixins like Dart, as it would let me have less form ancestors. But I feel people write much more code in MVC than with Visual Form Inheritance, since most of the navigation, calls to the validation rules in the datamodule(applyupdates), sql filtering criteria, searching the client dataset, everything is written in 5 maybe 6 Form ancestors.

Even the "With MVC it will be easier for you to leave Delphi in the Feature" for me don´t work as an argument, since people invited OOP, everything are classes, properties, methods. So all I need is a translator. Really could not find any advantage, maybe for websites which are much less complicated but change in a Daily base.

Mason Wheeler
  • 82,151
  • 24
  • 234
  • 309