Thursday, July 21, 2016

.NET Reflection bug

Had a fun bug with an app the other day. I had been leaving the app to run for a long time so I could monitor memory usage to check for any possible leaks. But the following bug was reported after a couple of hours:

“target invocation exception” in System.Reflection

 at the following line

this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));

but that line was in the form.designer.cs generated by Visual Studio and you would assume that this code was only executed when the form was first rendered.

I was potentially fighting an abstraction then – clearly one that had leaked

Then I remembered that the program updated the task bar icon to alert its user to potentially new and interesting occurrences. I had made sense at the time of writing to make use of the icon stored as a resource associated with the form. The image was retrieved using:

Bitmap b = this.Icon.ToBitmap();

then decorated and set as a revised taskbar icon – which is (of course) also the current form icon which just might give some insight into what was going wrong or at least a faint hint.

The icon file itself was properly formatted and contained 16x16 and 32x32 pixel bitmaps but it was clear that somewhere along the line and after several successful invocations in each instance something went wrong in mscorlib and the debug facility in Visual Studio pinned the problem back on the partial (designer) class for the form.

A couple of solutions came to mind but I went with embedding a 32x32 .png image as an additional assembly resource and retrieving that when it was required. So far, that has yet to fail but yes, there is a small memory leak somewhere so I am not finished yet. Testing and modifying programs designed to run continuously for extended periods within a Windows environment can be a challenge. 

Thursday, May 12, 2016

Drag and Drop

Drag and Drop is one of the fundamental building blocks of a modern GUI. It is so fundamental it is easy to overlook the potential for enhancement. There are lots of code fragments about if you look for enhancing the basics but most explanations seem a bit partial – hence this tutorial that I hope covers the wider ground.

Starting with the basic default process and a simple (but perhaps unlikely) example.

Consider a Label with some text that you might want to drag into a text box to edit. The TextBox has the AllowDrop attribute set to true. The drag process starts with a mouse down event on the label so we need some code to start the DragDrop process.

private void label1_MouseDown(object sender, MouseEventArgs e) {     DoDragDrop(label1.Text, DragDropEffects.Copy); }

The DoDragDrop method has been passed the label Text property as the data and the DragDropEffects value of Copy set to indicate the intention of the process. The main values are Copy, Link and Move with additional values of Scroll, None and All (multiple values can be combined with the Or operator) . The data can be supplied as an object and in this instance is a string.

Our target Textbox needs to handle two events, DragEnter and DragDrop.

private void textBox1_DragEnter(object sender, DragEventArgs e) {     if (e.Data.GetDataPresent(DataFormats.Text))     {         e.Effect = DragDropEffects.Copy;     } } private void textBox1_DragDrop(object sender, DragEventArgs e) {     textBox1.Text += e.Data.GetData(DataFormats.Text); }

That’s all that is required for this basic example. The drag drop process gets the default visual feedback effects and a drop onto the TextBox copies and adds the Label Text to the TextBox Text.

Now let us consider a more complex scenario. I might have multiple objects that could be dragged and dropped on the same page. It could also be true that things might be dragged with multiple potential purposes – I might be implicitly copying the content if I dropped it at one location but simply moving the content about within another screen area. The user needs feedback on the impact of a drop event at different locations – this makes a case for custom cursors. It is also important that the control receiving the “drop” knows the drag source as this might influence that drop action and certainly identifies the unique “content”.

As the DoDragDrop method accepts type “object” as the data source we could rewrite the code from above to use the Label (or any other) control itself:

private void label1_MouseDown(object sender, MouseEventArgs e) {     DoDragDrop(label1, DragDropEffects.Copy); } private void textBox1_DragEnter(object sender, DragEventArgs e) {     if (e.Data.GetDataPresent(label1.GetType()))     {         e.Effect = DragDropEffects.Copy;     } } private void textBox1_DragDrop(object sender, DragEventArgs e) {     textBox1.Text += ((dynamic)e.Data.GetData(label1.GetType())).Text; }

but this approach is somewhat limited as it would mean writing specific code for each individual data source.

The DataFormats class has a long list of predefined fields ranging from Bitmap through Text (as we have seen) and includes useful types like XAML. This list of DataFormats is the same list as is used by the Windows Copy/Paste functionality and it is perfectly straightforward to add additional types to that list.

The code below declares a new custom DataFormat and then uses that DataFormat identity to store an instance of a class in a DataObject and then passes that DataObject through the Drag and Drop process to the target control. The code can thus support multiple instances of said class generically.

private DataFormats.Format cardFormat = DataFormats.GetFormat("CCard"); private void label1_MouseDown(object sender, MouseEventArgs e) {     CCard mCard = new CCard(); // create a class instance     mCard.CardTitle = "New Card Title"; // set a property value     DataObject mDataObject = new DataObject(cardFormat.Name, mCard);     DoDragDrop(mDataObject, DragDropEffects.Copy); } private void textBox1_DragEnter(object sender, DragEventArgs e) {     if (e.Data.GetDataPresent(cardFormat.Name))     {         e.Effect = DragDropEffects.Copy;     } } private void textBox1_DragDrop(object sender, DragEventArgs e) {     CCard cCard = (CCard)e.Data.GetData(cardFormat.Name); // retrieve the class instance     textBox1.Text += cCard.CardTitle; // and use one or more proprty values }

In our more complex scenario, each data source type might be represented by a specific class and then each data source instance would simply supply an instance of the relevant class. The DragDrop targets can inspect the DataFormat to decide upon the relevant Effect for that DataFormat and the Drop event can process the given class instance where appropriate.

As that may not be the clearest explanation it would probably help to have some demo code. First though I want to look at custom Cursors.

My custom Cursor creation functions are encapsulated within a utility class

namespace Adit.Classes {     public struct IconInfo     {         public bool fIcon;         public int xHotspot;         public int yHotspot;         public IntPtr hbmMask;         public IntPtr hbmColor;     }     public static class CursorUtil     {         [DllImport("user32.dll")]         public static extern IntPtr CreateIconIndirect(ref IconInfo icon);         [DllImport("user32.dll")]         [return: MarshalAs(UnmanagedType.Bool)]         public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);         [DllImport("gdi32.dll")]         public static extern bool DeleteObject(IntPtr handle);         [DllImport("user32.dll", CharSet = CharSet.Auto)]         extern static bool DestroyIcon(IntPtr handle);         public static Cursor CreateCursor(Bitmap bm, int xHotspot, int yHotspot, bool resize = true)         {             IntPtr ptr = (resize) ? ((Bitmap)ResizeBitmap(bm, 32, 32)).GetHicon() : bm.GetHicon();             IconInfo inf = new IconInfo();             GetIconInfo(ptr, ref inf);             inf.xHotspot = xHotspot;             inf.yHotspot = yHotspot;             inf.fIcon = false;             IntPtr cursorPtr = CreateIconIndirect(ref inf);             if (inf.hbmColor != IntPtr.Zero) { DeleteObject(inf.hbmColor); }             if (inf.hbmMask != IntPtr.Zero) { DeleteObject(inf.hbmMask); }             if (ptr != IntPtr.Zero) { DestroyIcon(ptr); }             Cursor c = new Cursor(cursorPtr);             c.Tag = (resize) ? new Size(32, 32) : bm.Size;             return c;         }         public static Bitmap ResizeBitmap(Image image, int maxWidth, int maxHeight)         {             double ratio = System.Math.Min((double)maxHeight / image.Height, (double)maxWidth / image.Width);             var propWidth = (int)(image.Width * ratio);             var propHeight = (int)(image.Height * ratio);             var newImage = new Bitmap(propWidth, propHeight);             using (var g = Graphics.FromImage(newImage))             {                 g.DrawImage(image, 0, 0, propWidth, propHeight);             }             return newImage;         }         public static Bitmap GetControlBitmap(Control c, Color transparent)         {             var bm = new Bitmap(c.Width, c.Height);             c.DrawToBitmap(bm, new Rectangle(0, 0, c.Width, c.Height));             if (transparent != null)             {                 bm.MakeTransparent(transparent);             }             return bm;         }         public static Bitmap OverlayBitmap(Bitmap baseBitmap, Bitmap overlay, Point atPosition)         {             using (var g = Graphics.FromImage(baseBitmap))             {                 g.DrawImage(overlay, new Rectangle(atPosition, overlay.Size));             }             return baseBitmap;         }     } }

I have used these functions to create cursors from 96x96 pixel .png files (some originally sourced from the Material Icons download page). I have also used the GetControlBitmap function to create a Cursor from a Bitmap displaying a copy of a control captured at run-time.

private Cursor[] cursors = new Cursor[4];         //later cursors[0] = CursorUtil.CreateCursor((Bitmap)imageList1.Images[2], 0, 0);         //and cursors[2] = CursorUtil.CreateCursor(CursorUtil.GetControlBitmap(label1, SystemColors.Control), 0, 0, false);

Important thing is, we can easily create custom Cursors from bitmaps ready to apply during our Drag and Drop.

Just one quick aside though, the cursor "hot-spot" does not have to be at position 0,0 as in the above examples. Think of (say) the Paint.NET flood-fill or Color-Picker cursors where the hot-spot is marked by a cross or is at the tip of the "eye dropper". You might want to use something like my OverlayBitmap() function to add a suitable symbol to a control image and set the hot-spot to the relevant position in the final image.

OK - one other aside. My cursor building function adds the image size to the Tag property. You might wonder why. If you create a custom cursor (say) from a control image you might be surprised to find that the resulting cursor Size attribute will always return 32,32 (or just possibly 64,64) and not the actual size of the cursor. You can access (say) the true Width of a cursor via the Tag property with code something like:

int cWidth = ((dynamic)myCursor.Tag).Width;

We can apply our custom cursors in a GiveFeedback event handler. so first we might create custom Cursors for Copy and None and then apply them in a function

private Cursor myCopyCursor, myNoneCursor;     myCopyCursor = CursorUtil.CreateCursor((Bitmap)imageList1.Images[2], 0, 0);     myNoneCursor = CursorUtil.CreateCursor((Bitmap)imageList1.Images[3], 0, 0); private void giveFeedback(object sender, GiveFeedbackEventArgs e) {     if(e.Effect == DragDropEffects.Copy)     {         e.UseDefaultCursors = false;         Cursor.Current = myCopyCursor;     }     else if(e.Effect == DragDropEffects.None)     {         e.UseDefaultCursors = false;         Cursor.Current = myNoneCursor;     } }
We add the giveFeedback() function to the GiveFeedback event of the Form - perhaps during the form Load()

this.GiveFeedback += giveFeedback;

and then slightly counter-intuitively set the AllowDrop property to true for the form and all controls over which you anticipate the user might drag. This does not allow a drop to occur at these sites (that requires code to handle DragEnter and DragDrop but it does allow any relevant custom Cursor to be displayed during any drag over the form and control surfaces.

A point worth noting is that DragDrop event arguments include X/Y coordinates and these are screen co-ordinates as drags are not restricted to the originating window. Drag/Drop is a key element of OLE (Object Linking and Embedding). This can make using those conveniently presented values slightly tricky when managing things like local scrolling under program control. Which brings me nicely to my demo application (zipped as a Visual Studio 2015 project ready to download) that manages just that.

First off, this is demo and not production code and thus likely to include bugs, ignore edge cases, certainly takes shortcuts, is unduly verbose in places and might totally fail to explain some key point you are interested in. However it might provide a platform that can be used to try out alternate scenarios and approaches without having to build the whole thing from scratch.


Here you can see a screen image from the demo program. Addresses can be entered in the mini form on the left of the window and dragged onto the FlowLayoutPanel on the right that is acting as an "address store". To save energy, the "Fill" button will populate the form with up to 50 addresses taken randomly from an embedded list.

Addresses in the right hand panel can be dragged back to the address entry form for editing. Addresses can also be dragged up and down the list to reorder them.

We thus have two sorts of object that can be dragged for (is it) three different purposes. Some simple custom icons are created and used during the drag events.

The demo features loading data from an embedded resource (.csv file) using the Visual Basic TextFieldParser class (don't panic this is C# demo code). There are two custom DataFormats declared and used in the Drag operations. The addresses in the notional "address store" are encapsulated within multiple UserControls and these controls use Delegates to communicate with the Form. Dragging an address within the FlowLayoutPanel will trigger an automatic scroll when the scroll bar is active and the drag nears the top or bottom of the panel..

Exercises for further study might include using an image of an AddressControl as a cursor and/or dragging new addresses to a specific location in the FlowLayoutPanel instead of just adding them at the bottom irrespective of the drop location.

An analysis of the code will note that the demo "cheats" when determining the target location of a move within the FlowLayoutPanel and that it is possible to dodge around one or more address and expose the bug.

Inspect or download the demo code at https://github.com/MikeGWem/DragDropDemo

If anyone knows or finds out what DragDropEffects.Scroll actually does then please let me know as so far the answer to that has eluded me.

Addendum:

The "bug fix" (to decide where a drop happens relative to the existing list of addresses in the "store" could be based upon a calculation performed at the same time as the test to see if the panel should be scrolled.

private void maybeScrollpanel(int dragY) {     if ((dragY - panelTop) <= flowLayoutPanel1.VerticalScroll.Value && flowLayoutPanel1.VerticalScroll.Value > 0)     {         flowLayoutPanel1.VerticalScroll.Value -= (flowLayoutPanel1.VerticalScroll.Value > scrollAt) ? scrollAt : flowLayoutPanel1.VerticalScroll.Value;     }     else if (dragY - panelTop >= flowLayoutPanel1.Height - scrollAt && flowLayoutPanel1.VerticalScroll.Maximum > 0)     {         flowLayoutPanel1.VerticalScroll.Value += (flowLayoutPanel1.VerticalScroll.Maximum - flowLayoutPanel1.VerticalScroll.Value > scrollAt) ? scrollAt : flowLayoutPanel1.VerticalScroll.Maximum - flowLayoutPanel1.VerticalScroll.Value;     }     calculatedPosition = calculatePosition(dragY - panelTop); } private int calculatePosition(int dragY) {     if (flowLayoutPanel1.Controls.Count == 1 || dragY < flowLayoutPanel1.Controls[1].Height) // || allows lazy evaluation     {          return 1;     }     Point pt = new Point(flowLayoutPanel1.Width / 2, dragY); // asumes single column of controls in panel     Control cAt = flowLayoutPanel1.GetChildAtPoint(pt);     while (cAt == null && pt.Y < (flowLayoutPanel1.Height + flowLayoutPanel1.VerticalScroll.Maximum))     {         pt.Y += 5; // arbitrary - adjust at will         cAt = flowLayoutPanel1.GetChildAtPoint(pt);     }     return (cAt != null) ? ((AddressControl)cAt).Sequence : (flowLayoutPanel1.Controls.OfType<AddressControl>().Max(a => a.Sequence) + 1); }
Then applied to the drop events like

private void flowLayoutPanel1_DragDrop(object sender, DragEventArgs e) {     // the two paths could share more of the code but...     if (e.Data.GetDataPresent(dragAddress.Name))     {         //handles drop of new address onto the panel         AddressControl newControl = new AddressControl((DragAddress)e.Data.GetData(dragAddress.Name), DoControlDrag, DraggedPast, flowLayoutPanel1.Controls.Count);         foreach(AddressControl ac in flowLayoutPanel1.Controls.OfType<AddressControl>().Where(a => a.Sequence >= calculatedPosition))         {             ac.Sequence++;         }         newControl.Sequence = calculatedPosition;         flowLayoutPanel1.Controls.Add(newControl);         reorderAddresses();         resetPnlEdit();     } else if (e.Data.GetDataPresent(addressControl.Name))     {         // handles drop of an existing AddressControl into (presumably) a new position         int newSeq = 1;         foreach (AddressControl ac in flowLayoutPanel1.Controls.OfType<AddressControl>().OrderBy(a => a.Sequence))         {             if(ac.Sequence == dragItemSequence)             {                 ac.Sequence = calculatedPosition;             } else if(ac.Sequence == calculatedPosition)             {                 ac.Sequence++;                 newSeq = ac.Sequence;             } else             {                 ac.Sequence = newSeq;             }             newSeq++;         }         newSeq = 1;         // is redoing the sequence to avoid breaks a bit too anal?         foreach (AddressControl ac in flowLayoutPanel1.Controls.OfType<AddressControl>().OrderBy(a => a.Sequence))         {             ac.Sequence = newSeq;             newSeq++;         }         reorderAddresses();     } }
For the rest of the code check out GitHub at https://github.com/MikeGWem/DragDropDemo

Friday, April 22, 2016

A textbox that auto-wraps to fit contents

The Windows Forms Textbox control is a thin wrapper for the underlying Win32 control and there is no Paint event you can hang extra code on in the normal way. If you write something like mycontrol.Paint += myFunction; and mycontrol is a Textbox them myFunction() will never get called.

You can go the whole hog and

SetStyle(ControlStyles.UserPaint, true);

and provide a Paint method but you have to do everything as it is no good calling the base.OnPaint(). Give it a try, and you will end up with an astonishingly unresponsive textbox.

I wanted to create a version of a Textbox that would resize itself vertically to accommodate extended word wrapped text. That did not result in any issues – all it needed was a FitToContents() method that retained the set width and allowed the height to vary to fit the control content.

protected virtual void FitToContents() {     Size size = this.GetPreferredSize(new Size(this.Width, 0));     if (multiLine) { this.Height = size.Height; } }

However I also wanted to be able to switch between a label displaying a given string and a textbox capable of editing that string. Just to make that interesting I wanted a “fade in” and “fade out” animation to switch between the two. While I was at it, I also wanted to support a textbox “Placeholder” facility to keep the UI nice and clean. It was that final element that reminded me that it was possible to slide a limited paint facility into position to be used in place of the controls own paint when the control was relatively inactive. My own paint facility only needed to manage the fades and the placeholder text display – all the rest could be left to the underlying control.

Fading text in and out was just a matter of tweaking the Alpha component of the control ForeColor in steps over a defined time period. It was a good opportunity to use the recently introduced async/await functionality although a timer would have worked just fine. The control border is unreachable to all intents and purposes although if the control was set borderless one could be drawn around it and arrangements made to fade that rectangle in and out.

OK – I know - WPF and all that, but I also have this funny feeling that as soon as I invest any real time in WPF code Microsoft are going to announce a newer superer duperer common base for Windows apps and it will all be to no avail. Don’t take advice from me though – anything could happen.

The full code for this class can be found at the bottom of this post – usual caveats.

In case anyone fancies just using the placeholder functionality I have generously added an attribute (AutoMultiLine) that can be set false to stop the control re-sizing in response to text longer than the control width. Just set the PlaceHolderText attribute at design or run time. You can always ignore or remove the code associated with the fade.

The control based upon Label with a matching fade facility is very similar but simpler and again the code can be found below. This label also supports automatic height resizing to match the Text.

Please note that these controls are using a language feature from .NET v4.5 so you would need to switch to using a timer to make use of the fade functionality with earlier versions.

Combining these two custom controls into a UserControl made sense as they could then be addressed together as a single entity. This did have its challenges – partly because the TextBox BackColor property can’t be set Transparent and that makes the fade through a bit clunky in one direction – perhaps I should have gone for a “wipe” effect instead…

namespace Adit.Classes.Controls {     [ToolboxBitmap(typeof(TextBox))]     class MultiLineTextBox : TextBox     {         #region Private Declarations         private Color foreColour;         private Color placeholderColour = Color.Gray;         private int opacity = 256;         private int fadeSteps = 10;         private int fadeTime = 750;         private string placeholderText = "Type here";         private bool showPlaceholderText = false, multiLine = true;         #endregion         public MultiLineTextBox()         {             this.Multiline = multiLine;             this.WordWrap = multiLine;         }         #region Design attributes         [Description("Fade time in milliseconds"), Category("Behavior")]         public int FadeTime         {             get { return fadeTime; }             set { fadeTime = value; }         }         [Description("Number of steps from transparent 0 to opaque 256"), Category("Behavior")]         public int FadeSteps         {             get { return fadeSteps; }             set { fadeSteps = value; }         }         [Description("Placeholder Text"), Category("Appearance")]         public string PlaceHolderText         {             get { return placeholderText; }             set { placeholderText = value; Invalidate(); }         }         [Description("Placeholder Colour"), Category("Appearance")]         public Color PlaceHolderColour         {             get { return placeholderColour; }             set { placeholderColour = value; }         }         [Description("Automatic switch to multiline"), Category("Appearance")]         public bool AutoMultiLine         {             get { return multiLine; }             set {                 multiLine = value;                 Multiline = value;                 WordWrap = value;             }         }         #endregion         #region Public methods         public async void FadeInOut()         {             bool saveUserPaint = GetStyle(ControlStyles.UserPaint);             foreColour = ForeColor;             int opStep = 256 / fadeSteps;             if (Visible)             {                 opacity = 256;                 opStep *= -1;             } else             {                 Visible = true;                 opacity = 0;             }             SetStyle(ControlStyles.UserPaint, true); // set after any Visibility change             opacity = opacity + opStep;             while(opacity > 0 && opacity < 256)             {                 foreColour = fadeColour(opacity, foreColour);                 placeholderColour = fadeColour(opacity, placeholderColour);                 Invalidate();                 await Task.Delay(fadeTime / fadeSteps);                 opacity = opacity + opStep;             }             Visible = (opacity >= 255);             SetStyle(ControlStyles.UserPaint, saveUserPaint);             if (Visible)             {                 Invalidate();                 Focus();             }         }         #endregion         #region Override control methods         protected override void OnResize(EventArgs e)         {             base.OnResize(e);             this.FitToContents();         }         protected override void OnKeyUp(KeyEventArgs e)         {             base.OnKeyUp(e);             FitToContents();         }         protected override void OnTextChanged(EventArgs e)         {             base.OnTextChanged(e);             placeholderToggle();             FitToContents();         }         protected override void OnCreateControl()         {             base.OnCreateControl();             placeholderToggle();         }         protected override void OnPaint(PaintEventArgs e)         {             using (var drawBrush = new SolidBrush((showPlaceholderText)? placeholderColour: foreColour))             {                 e.Graphics.DrawString((showPlaceholderText) ? placeholderText : Text, Font, drawBrush, this.ClientRectangle);                 // The underlying control is probably using TextRenderer.DrawText (gdi not gdi+)             }         }         protected virtual void FitToContents()         {             Size size = this.GetPreferredSize(new Size(this.Width, 0));             if (multiLine) { this.Height = size.Height; }         }         #endregion         #region Private methods         private Color fadeColour(int opacity, Color argbColour)         {             return Color.FromArgb(opacity, argbColour);         }         private void placeholderToggle()         {             showPlaceholderText = (Text.Length > 0) ? false : true;             SetStyle(ControlStyles.UserPaint, showPlaceholderText);         }         #endregion     } }

and

namespace CheckBuilder.Classes.Controls {     public partial class WrapLabel : Label     {         #region Private Declarations         private Color foreColour;         private int opacity = 256;         private int fadeSteps = 10;         private int fadeTime = 750;         private bool fading = false;         #endregion         public WrapLabel()         {             base.AutoSize = false;         }         #region Public methods         public async void FadeInOut()         {             fading = true;             foreColour = ForeColor;             int opStep = 256 / fadeSteps;             if (Visible)             {                 opacity = 256;                 opStep *= -1;             }             else             {                 opacity = 0;                 Visible = true;             }             opacity = opacity + opStep;             while (opacity > 0 && opacity < 256)             {                 Invalidate();                 await Task.Delay(fadeTime / fadeSteps);                 opacity = opacity + opStep;             }             Visible = (opacity >= 255);             fading = false;         }         #endregion         #region Design attributes         [Description("Fade time in milliseconds"), Category("Behavior")]         public int FadeTime         {             get { return fadeTime; }             set { fadeTime = value; }         }         [Description("Number of steps from transparent 0 to opaque 256"), Category("Behavior")]         public int FadeSteps         {             get { return fadeSteps; }             set { fadeSteps = value; }         }         #endregion         #region Override control events         protected override void OnPaint(PaintEventArgs pe)         {             if(fading)             {                 using (var drawBrush = new SolidBrush(fadeColour(opacity, foreColour)))                 {                     pe.Graphics.DrawString(Text, Font, drawBrush, ClientRectangle);                 }             } else             {                 base.OnPaint(pe);             }         }         protected override void OnResize(EventArgs e)         {             base.OnResize(e);             this.FitToContents();         }         protected override void OnTextChanged(EventArgs e)         {             base.OnTextChanged(e);             this.FitToContents();         }         protected virtual void FitToContents()         {             Size size = this.GetPreferredSize(new Size(this.Width, 0));             this.Height = size.Height;         }         protected override void OnCreateControl()         {             base.OnCreateControl();             this.AutoSize = false;         }         #endregion         #region Stomp on AutoSize         [DefaultValue(false), Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]         public override bool AutoSize         {             get { return base.AutoSize; }             set { base.AutoSize = value; }         }         #endregion         #region Private methods         private Color fadeColour(int opacity, Color argbColour)         {             return Color.FromArgb(opacity, argbColour);         }         #endregion     } }

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.

Monday, April 11, 2016

Dynamic C#

Just used the dynamic key word for the first time in C# - to get the compiler to relax a bit and act more like VB which allows late binding.

I was using a control Tag to store a couple of data items and as Tag accepts an object decided to use an Anonymous Object rather than define another class or whatnot. Something like:

aControl.Tag = new { Sequence = ItemSequence, id = ItemID};

But how to consume those object attributes in another function?

The IDE and compiler are not going to take kindly to anything like aControl.Tag.Sequence as that can only make sense at runtime.

The dynamic keyword comes to the rescue.

Here I am looping through a control collection filtered by my specific control type and in the order of the Sequence attribute in the anonymous object stored in the control Tag:

foreach(MyCheckbox mb in this.Controls.OfType().OrderBy(p => ((dynamic)p.Tag).Sequence))
            { … }

Also (after prompting by Visual Studio) used the following syntax to check if a reference to a delegate is null and execute it if it is valid:

notifyChange?.Invoke(ids);

You can add a conditional call to a delegate to a standard control event. For example:

myControl.SizeChanged += (object s, EventArgs e) => { checkSize?.Invoke(); };

The “is” keyword also came in handy with the ability to say something like:

if (myobj is CardSubsection){} 

and do something if myobj can be safely cast to the relevant class. “is” makes it way simpler (and produces clearer code) when passing one of a range of classes into a function via an object reference.

On the subject of clarity, I came across a little bit of code like this in a blog:

if (pa.TicksToGo-- > 0) {}

which is shorter than

pa.TicksToGo--; // or maybe pa.TicksToGo -=1;
if (pa.TicksToGo > 0) {}

but less easy to maintain at some future date. I know that JSLint would like to persuade you that -– and ++ are evil in and of themselves but I am not convinced – safe to use with due caution I think.

Looks like C# is going to “keep on giving” with the news that the dev teams for C# and VB are going to let the two languages drift further apart. Seems that developers using VB favour stability (presumably finding multiple language versions bothersome within their organisations) and the C# types are gluttons for novelty. Should be interesting.

Read more here: http://www.infoworld.com/article/3051066/application-development/microsoft-c-visual-basic-are-now-set-to-diverge.html

Sunday, March 20, 2016

Material Design Lite

A short and incomplete review

Material Design Lite (MDL) is a Google project intended to deliver a “Material design” look and feel to websites. This HTML, CSS and JavaScript project is a work in progress so shortcomings experienced might well be short lived so please bear in mind that this limited review was based upon version 1.1.2.

The project FAQ makes the following point. “We optimise for websites heavy on content, such as marketing pages, articles, blogs and general web content that isn’t particularly app-y.” However the supplied layout components respond well to variations in screen size and thus provide a solid basis for web pages targeting smartphones alongside desktop browsers. Where things are a bit clunky at the moment is in the area of dynamic page changes after the initial load and it may be that this will prove to be an enduring issue as (after all) that could be considered somewhat app-y.

The layout components provide a good range of page formats that can include navigation, a drawer, tabs, a straightforward and responsive grid and a footer. There is inbuilt JavaScript support for most of the layout options – you just set out your mark-up as directed and you are good to go. Colour choice is deliberately restrained but you can select from a range of themes to build a ‘custom’ CSS file.

Other components include a good variety of buttons, excellent cards, lists, menus, sliders, toggles and text fields. There is a dialog component but this is limited by only partial browser support although there is a polyfill available. The only obviously lacking component is a <select> but I found a CSS only solution that filled the gap nicely.

There is full support for the Google Material Design Icons although for MDL I only used the icon font having previously used the SVG and CSS sprite versions on other projects. As expected the font more than met my requirements.

I have a fondness for the consistency and flexibility of jQuery when it comes to DOM manipulation (however untrendy) and can confirm that jQuery works happily with MDL as long as you remember that jQuery objects are a collection of elements and that when calling an MDL function you may need to pass the first (or selected) element of the relevant collection:

Which brings me nicely to the app-y bits.

I first drafted out a reasonably complex static page to get a feel for the components and how they are laid out. The results were pleasing with just enough animation to give the design some life. Next I set up a skeleton MDL layout and attempted to build the same page content from JSON using JavaScript in the browser. Once the DOM has been updated with the new elements then you must call the componentHandler.upgradeElement() utility to apply the MDL layout magic to the new DOM elements. This worked very well for the set of cards I inserted together with their <div>s, text and checkboxes.

I then started to build the layout for a “visual” form constructor and started to run into some limitations. I envisaged my form constructor being based upon multiple tabs with the user being able to add a new tab to start a new ‘form’. I was able to inject the same structures into the DOM as would be required to initialise an MDL tabbed page but calling the upgradeElement() utility only gave me a partial result lacking in any MDL script support.

Currently most of the MDL documentation consists of asking questions on Stack Overflow.

There I learned about the downgradeElements() utility (although it does get a mention on the MDL web pages) that should be applied to a container that has already been sprinkled with the MDL fairy dust prior to calling the upgradeElement() function to rebuild things with any new inclusions. Indeed user HR_117 has kindly supplied a CodePen demo of this being applied to a set of MDL tabs. Trouble is, I found this failed when the tab container was itself wrapped within an MDL header (reported as an improbable bug so the true cause was masked in some way). Could very well be that I missed a trick here but how many hours can you spend on just one issue? My workaround (horror) was to create a bunch of tabs up front and to hide them on page load until they were required (a remarkably low overhead in reality as I was able to recycle previously used and subsequently closed tabs).

I hit similar problems with the clever “Text with Floating Label” component when injected into a pre-existing layout. After a short losing battle (quite possibly my misunderstanding or whatever) I switched to using this http://clubdesign.github.io/floatlabels.js/ alternative jQuery plugin that was a very nice substitute well within the style and spirit of Material Design.

In the end I got a functional page although many interactive elements of the layout were untidy. I knew I was in for a long round of CSS tweaking to get things polished but then again I have hit similar walls when working with Bootstrap. It was one of the reasons I had my first dalliance with Polymer where, by default, you can avoid the intrusion of inherited CSS – and the overhead of tracking down and fixing localised layout glitches. So maybe I should have a bash at mixing Polymer components into an MDL ‘frame’ – now that sounds like a winner and indeed should be very doable. A definite if I get to re-visit this specific task.

I am convinced enough by MDL as it stands to be sure of using it for aspects a commercial project currently in development. If you can avoid bumping into the limitations (and they will fast retreat I am sure – or maybe future documentation will show me, at least, the error of my ways) then it is very effective and can support a responsive web page that is bang up to date in design terms.

The experiment with MDL was a nice opportunity to exercise my JavaScript muscles after a bit of a lengthy break where C#, Java and even a touch of VB.NET had been the required tools. JavaScript is always a delight to get back to, as I find it a very satisfying language to work with. You can take liberties that other languages could not support but in the end I always feel a certain obligation to refactor code until it is fit for public display – even though I then feed it through Closure * to optimise browser download and execution. I found the (reasonably) recent addition of the Array.forEach() functionality (Mozilla polyfill available) very effective in reducing code complexity and enhancing readability – looks like ECMA 5 is creeping into my code. Array.find() is also another great – er – find.


* I do hope that Closure is extended to be a compiler for WebAssembly when that gets support across the browser range (looking good so far).

Addendum:

In many ways it makes more sense to implement the (rather restricted) forms designer app-y page that highlighted the MDL issues above as an app. Mobile development platforms are obviously going to be happy with the design concepts but how about good old Windows?

There is a Material Design skin project on GitHub addressing Windows forms projects although it has been a while since there were any updates. There is also a WPF focused project that looks a lot more complete and is certainly active - maybe attention has drifted from the former to the latter.

A little experiment showed that some Material Design styling can be added to a pretty basic Windows Forms app by overriding the Paint() events on some of the key components. Turning a panel into a card, for instance, does not require a lot of effort.

So, for Windows, a bit of mix and match should do the trick...