Tuesday, April 19, 2016

Structure C# like JavaScript

Been building a “proof of concept” Windows program that ended up including 9 different user controls, 4 Custom controls and some (gasp) printed output.

My custom controls included:
  • A Textbox with the equivalent of the HTML Textbox “placeholder”.
  • A custom Checkbox to emulate a material Design checkbox.
  • A Label control to automatically support word wrapped text – like HTML.
  • A Panel with rounded corners (which also required a couple of extensions to System.Drawing.Graphics to support drawing and filling the bounding round cornered “box”**).
There was the makings of a “rant” here; taking Microsoft to task on the lack of updates to the basic controls to meet modern requirements. I stifled it when I realised that rather than providing solutions matching what would, after all, be ever shifting changes in design “fashion” they had simply provided the tools necessary to remedy the situation. If you count yourself as a programmer then you have to accept the challenge to one’s design skills – mine do lack I admit.

What is the true difference between a Custom Control and a User Control?

This is a question I have seen asked in many places and so far all the answers I have seen have been “lies for children*” (simplifications that attempt to help someone make the right choice).

The key difference is that a User Control inherits from UserControl and a Custom control inherits from Control. That’s about it. You can add other controls to either and both will turn up in the visual designer “toolbox” in Visual Studio after a “build”. They can both place custom attributes and events into the control properties window as well as take advantage of properties inherited from their base types (these can also be suppressed if required).

Generally, you would choose to create a Custom Control if it is intended as a single control rather than a defined collection. (However there is no reason why a custom control might not act as a custom container for other Windows controls added, perhaps, at design time – like my panel with rounded corners). You would also select this type if you intend to manage drawing the control and also if you intend to subclass an existing control type.

User controls are a good choice if you are going to build a control using two or more pre-existing controls. This approach effectively provides a local name space and would normally provide code to handle individual sub-component events. User controls are better used when adding additional visual attributes during the paint event rather than managing the whole drawing requirement.

I think that my custom built control base type selection would normally be founded upon usage. “User Controls” being particular to a given application while “Custom Controls” might well be ported to other projects. Which probably does not help at all.

Why all those User Controls?

Well the program required repetitive collections of controls arranged as cards, subsections of cards and subsections of those subsections – all at the whim of the user. In addition, the program provides a preview pane to show how the output would be presented with support for interactive testing on the fly. At the design stage I could see that this had quite a lot of potential to get messy built using a conventional Windows Forms approach. I knew that if I built something similar in JavaScript then I would end up with a set of objects that managed their portion of the UI and dealt with the relevant events. Any required communication would be through callbacks to functions. The C# equivalent then became clear – a set of UserControls communication between themselves using Delegates. In fact some of the presentation side of the program had already been built using JavaScript and it was interesting to note the strong similarities between some of the C# and JavaScript code blocks.

That was how it worked out – a varying number of custom UserControls communicating with each other in the main but with the owning form being called upon to manage “global” functions like database saves. I can only estimate the savings in code lines as “substantial”.

Printing controls

Providing a print output started out feeling a bit “retro” as the most obvious way to accomplish the task was to print what was effectively an image of the program output preview pane (or at least the contents). I could just about recall that the Visual Basic 3 manual(s)*** had included some functionality to print a window content but had never actually done any such thing in all my years of code.

Establishing the basic mechanism proved simple enough as any control has the capacity to draw itself to a bitmap and the resulting bitmaps can easily be drawn to the printed page (scaling as required).

Worth noting that when doing this it is easy to add a drop shadow by extending the size of the bitmap by a few pixels. You can then draw some lines in a sequence of grey shades to create the shadow effect.

private Bitmap drawControlsImage(Control control, bool addShadow = false) {     Bitmap bm = new Bitmap(control.Width, control.Height + ((addShadow) ? 3 : 0));     control.DrawToBitmap(bm, new Rectangle(0, 0, control.Width, control.Height));     if(dropShadow)     {         Point pt = new Point(0, bm.Height - 3);         using (Graphics g = Graphics.FromImage(bm))         {             using (var pen = new Pen(shadow[0]))             {                 for (var sp = 0; sp < 3; sp++)                 {                     pen.Color = shadow[sp];                     g.DrawLine(pen, pt.X, pt.Y, pt.X + bm.Width - 2, pt.Y);                     pt.Y++;                 }             }         }     }     return bm; }

having previously coded

private Color[] shadow = new Color[3];         and shadow[0] = Color.FromArgb(181, 181, 181); shadow[1] = Color.FromArgb(195, 195, 195); shadow[2] = Color.FromArgb(211, 211, 211);

My printer output took the form of one or more major components (User Controls) emulating a Material design “card”. Finding the best way to order the cards in columns to optimise the printed layout looked set to be an interesting problem. It was analogous to the 2D rectangle bin packing challenge but modified in that there was at least an implicit order in the cards plus the target rectangle could be proportionately increased in size (at least conceptually) by applying ScaleTransform() to the output graphics surface and thus (negative) zoom. While thinking of the best approach I also thought about the issue as a “flow layout” problem – with the option to resize the logical bounding rectangle until a fit was achieved.

After a happy hour reading around the potential algorithms I realised that my requirement was very much a special case – a simple stacking exercise.

Which at first sight produced an acceptable result – but a little thought turned up the most obvious fault. Early positioning of objects in a column could undermine later reductions in the overall width.

What was needed was a two pass process with the first pass constrained to not building a column “higher” than the measured page “vertical” space. Second pass could then work as before towards the smallest packed size. So far, testing shows that the two pass method produces a better result and (on most occasions) a nicely balanced layout.

It amused me to note that I was quite happy to write the first draft of my stacking algorithm based upon a two dimensional array of SizeF (SizeF[,]) but when I came to re-write it to include two passes I changed the structure to List<List<SizeF>> (effectively a sparse array with inbuilt equivalents of Push() and Pop()) and achieved some code reduction as a consequence.

** Graphics extensions: You might like to take a look at this Code Project post by Arun Zaheeruddin which provides a fully overloaded set of rounded rectangle drawing functions that look enticing.

*** The VB3 manual set (included in the box) was probably the pinnacle of written language documentation. It was clearly intended to empower a generation of new Windows programmers – as indeed it probably did. We have come to accept that Google searches and Stack Overflow now provide detailed technical documentation for most things but this well written, detailed and accurate set in three (as I recall) volumes filled in the gap for a vast army until the World Wide Web was invented.

No comments: