Quantcast
Channel: cyotek.com Blog Summary Feed
Viewing all 559 articles
Browse latest View live

Zooming to fit a region in a ScrollableControl

$
0
0

I suspect the titles of these last pair of articles are a touch misleading as they talk about extended zoom operations without actually describing a zoom process (as this is already part of other ImageBox articles. Unfortunately I can't really think of better titles and the theory is generic enough to be applied to any type of zooming, not just the ImageBox.

My previous article touched on zooming in a ScrollableControl while keeping the content correctly aligned to a fixed point, usually the mouse position prior to the zoom. This article expands on that with another new feature in the upcoming ImageBox update, zooming to a given region. You've probably seen this behaviour in other paint programs, where you select a Zoom tool, draw a rectangle, and the document is automatically zoomed to fit.

Again, this is actually quite simple to achieve and this blog post is pretty much just a reference to me of how I did this so I don't forget next time I want something similar! We'll start off by quickly describing a method that I forgot to include in the previous post - CenterAt. This simple method centers the view port on the given document location. This use useful for when you want to use ScrollTo without providing a relative offset... or for centering the display on a selection rectangle.

public virtual void CenterAt(Point imageLocation)
{
  this.ScrollTo(imageLocation, new Point(this.ClientSize.Width / 2, this.ClientSize.Height / 2));
}

Very straightforward, it simply calls ScrollTo, using the center of the control as the offset. Now for the actual ZoomToRegion method:

public virtual void ZoomToRegion(RectangleF rectangle)
{
  double ratioX;
  double ratioY;
  int cx;
  int cy;

  ratioX = this.ClientSize.Width / rectangle.Width;
  ratioY = this.ClientSize.Height / rectangle.Height;
  cx = (int)(rectangle.X + (rectangle.Width / 2));
  cy = (int)(rectangle.Y + (rectangle.Height / 2));

  this.Zoom = (int)(Math.Min(ratioX, ratioY) * 100);
  this.CenterAt(new Point(cx, cy));
}

This accepts a RectangleF structure (you could use a Rectangle, but then if you attempt to draw selection regions on a zoomed out document, rounding from float to int would render your selections useless), and it then calculates a new zoom factor and offset.

  1. First, the ration of the width and height of the region against the width and height of the view port is calculated
  2. We use the smallest ratio (to ensure if that everything you selected appears when the zoom is applied) to calculate the new zom level
  3. After this, we define the center of the rectangle
  4. With all the calculations done, we set the zoom level of the control
  5. And finally, we call our new CenterAt method to center the view port on the center of the source region

In the actual ImageBox control, a new SelectionMode has been added - Zoom. As the name somewhat logically suggests, when this mode is active, after the user draws a selection rectangle, the control then zooms to match the rectangle they have drawn. This updated mode is called from the OnSelected method, as follows:

if (this.SelectionRegion != null && this.SelectionRegion.Width > ImageBox.SelectionDeadZone && this.SelectionRegion.Height > ImageBox.SelectionDeadZone)
{
  this.ZoomToRegion(this.SelectionRegion);
  this.SelectionRegion = RectangleF.Empty;
}

The mysterious ImageBox.SelectionDeadZone field is a constant currently set to 5, and basically ensures the user selects a valid rectangle first - when I was testing the first iteration of this code, having the mouse wobble as you clicked the control was enough to generate a 1x1 rectangle, definitely not a good user experience!

The only disadvantage is this functionality only lends itself to zooming it, and not out.


ImageBox update, version 1.1.0.0

$
0
0
A newer version of this code is now available.

The ImageBox control has had quite a big update, you can download the source from the link below, or from our GitHub page.

Listed below are the changes made during this update, we hope you enjoy them!

The updated ImageBox demonstration application.

Changes and new features

  • Zooming with the mouse is now smoother, and the control attempts to keep the area under the mouse before the zoom in the same area after the zoon.
  • Added a ZoomLevels property which allows you to configure the different zoom levels supported by the control. Now instead of the control trying to guess the next zoom level, it cycles appropriately through the defined levels. Currently ZoomLevels (apart from the default series) can only be set at runtime.
  • The ZoomIncrement property has been removed due to the introduction of the new zoom levels.
  • New CenterAt and ScrollTo methods allow you to scroll to a given location in the source image.
  • Split shortcut handling into two methods ProcessScrollingShortcuts for handling arrow keys and ProcessImageShortcuts for handling pretty much anything else.
  • Added EnableShortcuts property, allowing the built in keyboard support to be disabled. When this property is true, ProcessImageShortcuts is not called, allowing the control to still be scrolled via the keyboard, but not zoomed etc.
  • Zooming can now be performed by the -/+ keys (OemMinus and Oemplus).
  • When zooming (except via mouse action), if the AutoCenter property is set, the control will always center the image even when scrollbars are present.
  • Nestable BeginUpdate and EndUpdate methods allow you to disable and enable painting of the control, for example when changing multiple properties at once.
  • Added a new GetSelectedImage method which creates a new Bitmap based on the current selection.
  • Added new FitRectangle method which takes a given rectangle and ensure it fits within the image boundaries
  • The AllowClickZoom property now defaults to false.
  • The PointToImage function no longer adds +1 to the result of the function.
  • Added a new ZoomToRegion method. This will caculate and appropriate zoom level and scrollbar positions to fit a given rectangle.
  • Added new SelectionMode.Zoom. When this mode is selected, drawing a region will automatically zoom and position the control to fit the region, after which the region is automatically cleared.

Bug fixes

  • Panning no longer tries to activate if no scrollbars are visible
  • A new base class, VirtualScrollableControl is now used instead of ScrollableControl. This removes completely the flicker issues present in previous versions of the control.
  • The BorderStyle property has been moved to the ScrollControl class, so that borders now correctly surround the control (including scrollbars) rather than just the client area.
  • If the AllowZoomClick property is true, the control no longer magically zooms after panning or selecting a region. Code previously in the OnMouseClick override is now in OnMouseUp.
  • If both AutoPan and a valid SelectionMode are set, only selections are processed, instead of the control tying to do both. As a result of this fix, setting the SelectionMode property no longer resets AutoPan
  • With the introduction of the VirtualScrollableControl, the MouseWheel event is now raised as expected.

Known issues

  • The ScrollProperties class hasn't been fully integrated with the ScrollControl, setting properties on this class won't update the owner control.

Downloads

Extracting email addresses from Outlook

$
0
0

The cyotek.com receives an awful lot of spam and a lot of this is sent to email addresses that don't exist. However, as we currently have catch all's enabled, it means we receive it regardless. This is compounded by the fact that I tend to create a unique email address for each website or service I interact with. And it's impossible to remember them all!

As a first step to deleting the catch alls, I wanted to see how many unique @cyotek.com addresses were in use. The simplest way of picking up these would be scanning PST files - we have email going back to 2002 in these files, and there's the odd backup elsewhere going back even further. Last time I used OLE Automation with Outlook was back in the days of VB6 and I recall well getting plagued with permission dialogs each time I dreamed of trying to access the API. Still, I thought I'd take a look.

A console application merrily extracting my personal email addresses from my Outlook store.

Setting up

Note: I tested this project on an Outlook profile which has loaded a primary PST, an archive PST, and a Gmail account. I haven't tested this with any other type of account (for example Exchange) or with accounts using non-SMTP email addresses. Caveat emptor!

The first thing to do is add a reference to the Outlook COM objects. I have VS2010 and VS2012 installed on this machine, and one of them has installed a bunch of prepared Office Interop DLL's into the GAC. Handy, I won't have to create my own! Adding a reference to the Microsoft Outlook 14.0 Object Library added three references, Microsoft.Office.Interop.Outlook.dll, Office.dll and stdole to my project.

Note: Depending on your version of VS / .NET Framework, the references may have a property named Embed Interop Types which defaults to true. When left at this, you may have problems debugging as you won't be able to access the objects properly through the Immediate window, instead getting an error similar to

"Member 'To' on embedded interop type 'Microsoft.Office.Interop.Outlook.MailItem' cannot be evaluated while debugging since it is never referenced in the program. Consider casting the source object to type 'dynamic' first or building with the 'Embed Interop Types' property set to false when debugging"

Probably a good idea to set this to false before debugging your code!

Connecting to Outlook

All the code below assumes that you have a using Microsoft.Office.Interop.Outlook; statement at the top of your code file.

Connecting to Outlook is easy enough, just create a new instance of the Application interface. We'll use as a root for everything else.

Application application;

application = new Application();

Remember I mentioned permission dialogs? Older versions of Outlook used to prompt for permissions. Outlook 2010 just seems to quietly get on with things. The only thing I've noticed is that if you try and create a new Application when Outlook isn't currently running, it will be silently started and the system tray icon will have a slightly different icon and a tooltip informing that some other program is using Outlook. Much nicer than previous behaviours!

Getting Account Folders

The Session property of the Application interface returns a NameSpace that details your Outlook setup, and allows access to accounts, profile details etc. However, for this project, the only thing I care about is the Folders property which returns a collection of MAPIFolder objects. In my case, it was the three top level folders for my profile - I was somewhat surprised that the Gmail account was loaded actually.

Now that we have a folder, we can scan it by enumerating the Items property. As Outlook folders can contain items of various types, you need to check the item type - I'm looking for MailItem objects in order to extract those addresses.

Pulling out email addresses

Each MailItem has Sender, To and Recipients properties. To seems to be just a string version of Recipients and so shall be completely ignored - why bother parsing it manually when Recipients already does it for you. The Sender property returns an AddressEntry, and each item in the Recipients collection (a Recipient) offers an AddressEntry property. So we're all set!

The following code snippet is from the example project, and basically shows how I scan a source MAPIFolder looking for MailItem objects.

protected virtual void ScanFolder(MAPIFolder folder)
{
  this.CurrentFolderIndex++;
  this.OnFolderScanning(new MAPIFolderEventArgs(folder, this.FolderCount, this.CurrentFolderIndex));

  // items
  foreach (object item in folder.Items)
  {
    if (item is MailItem)
    {
      MailItem email;

      email = (MailItem)item;

      // add the sender of the email
      if (this.Options.HasFlag(Options.Sender))
        this.ProcessAddress(email.Sender);

      // add the recipies of the email
      if (this.Options.HasFlag(Options.Recipient))
      {
        foreach (Recipient recipient in email.Recipients)
          this.ProcessAddress(recipient.AddressEntry);
      }
    }
  }

  // sub folders
  if (this.Options.HasFlag(Options.SubFolders))
  {
    foreach (MAPIFolder childFolder in folder.Folders)
      this.ScanFolder(childFolder);
  }
}

When I find an AddressEntry to process, I call the following functions:

protected virtual void ProcessAddress(AddressEntry addressEntry)
{
  if (addressEntry != null && (addressEntry.AddressEntryUserType == OlAddressEntryUserType.olSmtpAddressEntry || addressEntry.AddressEntryUserType == OlAddressEntryUserType.olOutlookContactAddressEntry))
    this.ProcessAddress(addressEntry.Address);
  else if (addressEntry != null)
    Debug.Print("Unknown address type: {0} ({1})", addressEntry.AddressEntryUserType, addressEntry.Address);
}

protected virtual void ProcessAddress(string emailAddress)
{
  int domainStartPosition;

  domainStartPosition = emailAddress.IndexOf("@");

  if (!string.IsNullOrEmpty(emailAddress) && domainStartPosition != -1)
  {
    bool canAdd;

    if (this.Options.HasFlag(Options.FilterByDomain))
      canAdd = this.IncludedDomains.Contains(emailAddress.Substring(domainStartPosition + 1));
    else
      canAdd = true;

    if (canAdd)
      this.EmailAddresses.Add(emailAddress);
  }
}

Although I'm scanning my entire PST, I don't want every single email address in there - I ran it once and it brought back just over 5000 addresses. What I want, is addresses tied to the domains I own, so I added some filtering for this. With this filtering enabled it returned a more managable 497 unique addresses. Although I'm not creating 497 aliases on the email server!

Wrapping up

This is a lot easier than what I was expecting, and in fact this is probably the smoothest piece of COM interop I've done with .NET yet. No strange errors, no forced to compile in 32bit mode, It Just Works.

You can find the example project in the link below.

Downloads

Manually writing the byte order mark (BOM) for an encoding into a stream

$
0
0

I recently discovered a problem with our WebCopy and Cyotek Sitemap Creator products to do with "corruption" of plain text documents, where non-ANSI characters appeared incorrectly. It didn't take long to realize that these programs were saving text content as ANSI files. Which I found curious as Crawler library they use detects response encoding and uses this to save the files.

Or does it? Consider the code below:

string fileName;
byte[] data;
Encoding encoding;

fileName = Path.GetTempFileName();
data = new byte[0]; // assume you have a populated byte array!
encoding = Encoding.UTF8;

using (FileStream stream = new FileStream(fileName, FileMode.Create))
{
  using (BinaryWriter writer = new BinaryWriter(stream, encoding))
    writer.Write(data);
}

Looking at this, you might be tempted to assume (as I did) that this code would save the content in the given encoding. When I tried opening one of the files generated by similar code to the above in Notepad++, I found they were encoded as ANSI files. Switching the encoding to UTF-8 immediately displayed the files correctly without the "corruption". So it seems the byte order mark (BOM) isn't actually written by the BinaryWriter - I think it only uses the given encoding for converting strings to a byte array. All this time I assumed files were being saved as UTF-8 (or whatever the response encoding was) and properly supported Unicode, and all this time I was wrong.

So how do you manually write a BOM into a document? The oddly named GetPreamble function available from the Encoding class is what you need - this returns the bytes that comprise the BOM, and you can then write this directly to your stream:

string fileName;
byte[] data;
Encoding encoding;

fileName = Path.GetTempFileName();
data = new byte[0]; // assume you have a populated byte array!
encoding = Encoding.UTF8;

using (FileStream stream = new FileStream(fileName, FileMode.Create))
{
  using (BinaryWriter writer = new BinaryWriter(stream, encoding))
  {
    writer.Write(encoding.GetPreamble());
    writer.Write(data);
  }
}

Note that you only need to write a BOM if your document is actually supposed to be a text file - if it is "normal" binary data (such as an image or a gzip stream) then you definitely do not want to write a BOM, or you truly will have a corrupt file.

Now the files produced by WebCopy and Sitemap Creator are encoded correctly and I can be happily with yet another bug squashed, unhappy at yet another reminder of why I need to write a proper set of automated tests for the libraries I use, but happy again that I had another (albeit brief) tip to post on this blog.

ImageBox and TabList update's - virtual mode, pixel grid, bug fixes and more!

$
0
0
A newer version of this code is now available.

Our last post before the new year and some new material is an update to the ImageBox (now at version 1.1.2.0) and TabList (at version 1.0.0.2) controls. You can grab the updated source from the links at the end of the post, or from the GitHub page.

ImageBox

Virtual mode allows you to use the ImageBox control without a backing imageThe pixel grid allows you to show a grid when zoomed in

Changes and new features

  • Added IsPointInImage method. This function returns if a given point is within the image viewport, and is useful for combining with PointToImage.
  • Added ImageBorderColor property, allowing you to customize the color of the image border
  • Added a new ImageBoxBorderStyle, FixedSingleGlowShadow. This style allows for a more smoother outer glow shadow instead of the existing clunky drop shadow.
  • Added ShowPixelGrid and PixelGridColor properties. When set, a dotted grid is displayed around pixels when zooming in on an image.
  • Added new overload to PointToImage which allows you to specify if the function should map the given point to the nearest available edge(s) if the point is outside the image boundaries
  • Added AllowDoubleClick property. When set, the normal double click events and overrides work as expected.
  • Added VirtualMode and VirtualSize properties. These new properties allow you to use all functionality of the ImageBox control without having to set the Image property. You can also use the new VirtualDraw event to provide custom drawing without having to override existing drawing functionality.
  • Additional documentation added via XML comments

Bug Fixes

  • If the GridDisplayMode property is set to Image an explicit image border is no longer drawn, instead the ImageBorder property is correctly honoured.
  • Fixes a problem where half the pixels of the first row/column were lost when zooming. Thanks to Rotem for the fix.
  • The GetImageViewport method now correctly returns a width and height that accounts for control size, padding and zoom levels.
  • Fixed incorrect attributes on AutoSize property
  • Fixed the image viewport sometimes being the incorrect size when zoomed in. Thanks to WMJ for the fix.
  • Fixes "see also" documentation errors for events

TabList

Changes and new features

  • Added ShowTabList property. When set to False, the list of tabs is no longer displayed, and navigation can only occur via code.
  • Added AllowTabSelection property. When set to False, the control can no longer gain focus, mouse hover effects are not displayed, and navigation can only occur via code. This allows you to disable navigation whilst still having the tabs visible.

Bug fixes

  • Fixed the HoverIndex property always defaulting to zero.

Happy New Year all!

Downloads

Creating a custom ErrorProvider component for use with Windows Forms applications

$
0
0

In recent code, I've been trying to avoid displaying validation errors as message boxes, but display something in-line. The .NET Framework provides an ErrorProvider component which does just this. One of the disadvantages of this control is that it displays an icon indicating error state - which means you need a chunk of white space somewhere around your control, which may not always be very desirable.

This article describes how to create a custom error provider component that uses background colours and tool tips to indicate error state.

A simple application demonstrating the custom provider.

Note: I don't use data binding, so the provider implementation I demonstrate below currently has no support for this.

Getting Started

Create a new Component class and implement the IExtenderProvider interface. This interface is used to add custom properties to other controls - it has a single method CanExtend that must return true for a given source object if it can extend itself to said object.

In this example, we'll offer our properties to any control. However, you can always customize this to work only with certain control types such as TextBoxBase, ListBoxControl etc.

bool IExtenderProvider.CanExtend(object extendee)
{
  return extendee is Control;
}

Implementing Custom Properties

Unlike how properties are normally defined, you need to create get and set methods for each property you wish to expose. In our case, we'll be offering Error and ErrorBackColor properties. Using Error as an example, the methods would be GetError and SetError. Both methods need to have a parameter for the source object, and the set also needs a parameter for the property value.

Note: I named this property Error so I could drop in replace the new component for the .NET Framework one without changing any code bar the control declaration. If you don't plan on doing this, you may wish to name it ErrorText or something more descriptive!

In this example, we'll store all our properties in dictionaries, keyed on the source control. If you want to be more efficient, rather than using multiple dictionaries you could use one tied to a backing class/structure but we'll keep this example nice and simple.

Below is the implementation for getting the value.

[Category("Appearance"), DefaultValue("")]
public string GetError(Control control)
{
  string result;

  if(control == null)
    throw new ArgumentNullException("control");

  if(!_errorTexts.TryGetValue(control, out result))
    result = string.Empty;

  return result;
}

Getting the value is straightforward, we attempt to get a custom value from our backing dictionary, if one does not exist then we return a default value.

It's also a good idea to decorate your get methods with Category and DefaultValue attributes. The Category attribute allows you to place the property in the PropertyGrid (otherwise it will end up in the Misc group), while the DefaultValue attribute does two things. Firstly, in designers such as the PropertyGrid, default values appear in a normal type face whilst custom values appear in bold. Secondly, it avoids cluttering up auto generated code files with assignment statements. If the default value is an empty string, and the property is set to that value, no serialization code will be generated. (Which is also helpful if you decide to change default values, such as the default error colour later on)

Next, we have our set method code.

public void SetError(Control control, string value)
{
  if(control == null)
    throw new ArgumentNullException("control");

  if(value == null)
    value = string.Empty;

  if(!string.IsNullOrEmpty(value))
  {
    _errorTexts[control] = value;

    this.ShowError(control);
  }
  else
    this.ClearError(control);
}

As we want "unset" values to be the empty string, we have a quick null check in place to convert nulls to empty strings. If a non-empty string is passed in, we update the source control to be in it's "error" state. If it's blank, then we clear the error.

protected virtual void ShowError(Control control)
{
  if(control == null)
    throw new ArgumentNullException("control");

  if(!_originalColors.ContainsKey(control))
    _originalColors.Add(control, control.BackColor);

  control.BackColor = this.GetErrorBackColor(control);
  _toolTip.SetToolTip(control, this.GetError(control));

  if (!_erroredControls.Contains(control))
    _erroredControls.Add(control);
}

Above you can see the code to display an error. First we store the original background colour of the control if we haven't previously saved it, and then apply the error colour. And because users still need to know what the actual error is, we add a tool tip with the error text. Finally, we store the control in an internal list - we'll use that later on.

Clearing the error state is more or less the reverse. First we try and set the background colour back it what it's original value, and we remove the tool tip.

public void ClearError(Control control)
{
  Color originalColor;

  if (_originalColors.TryGetValue(control, out originalColor))
    control.BackColor = originalColor;

  _errorTexts.Remove(control);
  _toolTip.SetToolTip(control, null);
  _erroredControls.Remove(control);
}

Checking if errors are present

Personally speaking, I don't like the built in Validating event as it prevents focus from shifting until you resolve the error. That is a pretty horrible user experience in my view which is why my validation runs from change events. But then, how do you know if validation errors are present when submitting data? You could keep track of this separately, but we might as well get our component to do this.

When an error is shown, we store that control in a list, and then remove it from the list when the error is cleared. So we can add a very simple property to the control to check if errors are present:

public bool HasErrors
{
  get { return _erroredControls.Count != 0; }
}

At present the error list isn't exposed, but that would be easy enough to do if required.

Designer Support

If you now drop this component onto a form and try and use it, you'll find nothing happens. In order to get your new properties to appear on other controls, you need to add some attributes to the component.

For each new property you are exposing, you have to add a ProviderProperty declaration to the top of the class containing the name of the property, and the type of the objects that can get the new properties.

[ProvideProperty("ErrorBackColor", typeof(Control)), ProvideProperty("Error", typeof(Control))]
public partial class ErrorProvider : Component, IExtenderProvider
{
  ...

With these attributes in place (and assuming you have correctly created <PropertyName>Get and <PropertyName>Set methods, your new component should now start adding properties to other controls in the designer.

Example Usage

In this component validation is done from event handlers - you can either use the built in Control.Validating event, or use the most appropriate change event of your source control. For example, the demo project uses the following code to validate integer inputs:

private void integerTextBox_TextChanged(object sender, EventArgs e)
{
  Control control;
  string errorText;
  int value;

  control = (Control)sender;
  errorText = !int.TryParse(control.Text, out value) ? "Please enter a valid integer" : null;

  errorProvider.SetError(control, errorText);
}

private void okButton_Click(object sender, EventArgs e)
{
  if (!errorProvider.HasErrors)
  {
    // submit the new data

    this.DialogResult = DialogResult.OK;
    this.Close();
  }
  else
    this.DialogResult = DialogResult.None;
}

The only thing you need to remember is to clear errors as well as display them!

Limitations

As mentioned at the start of the article, the sample class doesn't support data binding.

Also, while you can happily set custom error background colours at design time, it probably won't work so well if you try and set the error text at design time. Not sure if the original ErrorProvider supports this either, but it hasn't been specifically coded for in this sample as my requirements are to use it via change events of the controls. For this reason, when clearing an error (or all errors), the text dictionary is always updated, but the background colour dictionaries are left alone.

Final words

As usual, this code should be tested before being used in a production application - while we are currently using this in almost-live code, it hasn't been thoroughly tested and may contain bugs or omissions.

The sample project below includes the full source for this example class, and a basic demonstration project.

Downloads

Dividing up a rectangle based on pairs of points using C#

$
0
0

Recently we released the first alpha of our latest product, Cyotek Slicr, a tool for slicing up an image. At the heart of this tool is a series of routines that take a given image and pairs of input points, from which the image is chopped up accordingly. This article describes how to break up a rectangle into smaller parts based on user defined co-ordinates.

An example of the programs output

Caveat Emptor

Before I get started, I just want to point out that the code I'm about to show you is proof of concept code. It doesn't use algorithms such as Bentley–Ottmann, it's not very efficient, and there's probably a hundred ways of doing it better. However, it works, which is pretty much all I care about at this moment!

Getting Started

These are the rules for splitting up a rectangle into component parts:

  1. Lines must be straight, so each pair of co-ordinates will align on one axis
  2. Co-ordinates must start from either an edge, or the intersection of another pair. The second co-ordinate must end in a similar fashion. Any "orphan" co-ordinates which don't start/end on another edge/join are illegal and should be ignored
  3. Co-ordinates can start and end at any point in the bounds of the canvas, as long as they follow the previous rules.

In order to achieve this, I ended up with creating a number of support classes

  • Segment - this class starts the starting point of a line, it's length, and it's orientation. Originally I just started by storing the two pairs of co-ordinates, but in the end it was easier with the length and orientation.
  • SegmentPoint - this class stores the details of a single point. An instance of this class is created for each unique point created by the start and end of a segment, and any intersections. It also stores the directions of neighbouring points.
internal class Segment
{
  public Point EndLocation
  {
    get
    {
      int x;
      int y;

      switch (this.Orientation)
      {
        case SegmentOrientation.Vertical:
          x = this.Location.X;
          y = this.Location.Y + this.Size;
          break;
        default:
          x = this.Location.X + this.Size;
          y = this.Location.Y;
          break;
      }

      return new Point(x, y);
    }
  }

  public Point Location { get; set; }

  public SegmentOrientation Orientation { get; set; }

  public int Size { get; set; }
}

internal class SegmentPoint
{
  public SegmentPointConnections Connections { get; set; }

  public Point Location { get; set; }

  public int X { get { return this.Location.X; } }

  public int Y { get { return this.Location.Y; } }
}

With these helper classes, I can now construct the code to process them and extract the rectangles.

Calculating Points

In this example, a rectangle has been created by segments crossing each other. We need to detect the intersections to find these types of rectangles.

The image above demonstrates the first problem. The four segments intersect with each other, causing a rectangle to be generated untouched by any user defined point. However, if we are going to find that rectangle, we need to find any new point generated by multiple segments intersecting.

private void CalculatePoints()
{
  List<Segment> segments;

  segments = new List<Segment>();
  this.Points = new Dictionary<Point, SegmentPoint>();

  // add segments representing the edges
  segments.Add(new Segment { Location = Point.Empty, Size = this.Size.Width, Orientation = SegmentOrientation.Horizontal });
  segments.Add(new Segment { Location = new Point(0, this.Size.Height), Size = this.Size.Width, Orientation = SegmentOrientation.Horizontal });
  segments.Add(new Segment { Location = Point.Empty, Size = this.Size.Height, Orientation = SegmentOrientation.Vertical });
  segments.Add(new Segment { Location = new Point(this.Size.Width, 0), Size = this.Size.Height, Orientation = SegmentOrientation.Vertical });

  // add the rest of the segments
  segments.AddRange(this.Segments);

  segments.Sort((a, b) =>
  {
    int result = a.Location.X.CompareTo(b.Location.X);
    if (result == 0)
      result = a.Location.Y.CompareTo(b.Location.Y);
    return result;
  });

  foreach (Segment segment in segments)
  {
    Segment currentSegment;

    // add the segment points
    this.UpdatePoint(segment.Location, segment.Orientation == SegmentOrientation.Horizontal ? SegmentPointConnections.Left : SegmentPointConnections.Top);
    this.UpdatePoint(segment.EndLocation, segment.Orientation == SegmentOrientation.Horizontal ? SegmentPointConnections.Right : SegmentPointConnections.Bottom);

    // calculate any intersecting points
    currentSegment = segment;
    foreach (Segment otherSegment in segments.Where(s => s != currentSegment))
    {
      Point intersection;

      intersection = Intersection.FindLineIntersection(segment.Location, segment.EndLocation, otherSegment.Location, otherSegment.EndLocation);
      if (!intersection.IsEmpty)
      {
         SegmentPointConnections flags;

        flags =  SegmentPointConnections.None;
        if (intersection != segment.Location && intersection != segment.EndLocation)
        {
          if (segment.Orientation == SegmentOrientation.Horizontal)
            flags |= ( SegmentPointConnections.Left | SegmentPointConnections.Right);
          else
            flags |= (SegmentPointConnections.Top | SegmentPointConnections.Bottom);
        }
        else if (intersection != otherSegment.Location && intersection != otherSegment.EndLocation)
        {
          if (otherSegment.Orientation == SegmentOrientation.Horizontal)
            flags |= (SegmentPointConnections.Left | SegmentPointConnections.Right);
          else
            flags |= (SegmentPointConnections.Top | SegmentPointConnections.Bottom);
        }

        if (flags != SegmentPointConnections.None)
          this.UpdatePoint(intersection, flags);
      }
    }
  }
}

Breaking the code down, we do the following:

  • Create an additional four segments representing the boundaries of the canvas
  • Sort the segments by their starting locations
  • Cycle each segment and
    • Create a point for the starting co-ordinate
    • Create a point for the ending co-ordinate
    • Cycle each other segment and see if it intersects with the current segment. If it does, create a new point at the intersection

In any case above where I state to create a point, the code will either create a point if one doesn't already exist, otherwise it will update the connections of the existing point.

private void UpdatePoint(Point location, SegmentPointConnections connections)
{
  SegmentPoint point;

  if (!this.Points.TryGetValue(location, out point))
  {
    point = new SegmentPoint { Location = location, Connections = connections };
    this.Points.Add(point.Location, point);
  }
  else if (!point.Connections.HasFlag(connections))
    point.Connections |= connections;
}

The CalculatePoints method above is very inefficient, but it does the job. Once this routine has run, we'll have an array of co-ordinates and the directions of linked points.

Calculating Rectangles

Now that we have all points, both user defined, and intersections, we can use this to generate the actual rectangles.

private void CalculateRectangles()
{
  SegmentPoint[] horizontalPoints;
  SegmentPoint[] verticalPoints;

  this.Rectangles = new HashSet<Rectangle>();
  horizontalPoints = this.Points.Values.OrderBy(p => p.X).ToArray();
  verticalPoints = this.Points.Values.OrderBy(p => p.Y).ToArray();

  foreach (SegmentPoint topLeft in this.Points.Values.Where(p => p.Connections.HasFlag(SegmentPointConnections.Left | SegmentPointConnections.Top)))
  {
    SegmentPoint topRight;
    SegmentPoint bottomLeft;

    topRight = horizontalPoints.FirstOrDefault(p => p.X > topLeft.X && p.Y == topLeft.Y && p.Connections.HasFlag(SegmentPointConnections.Right | SegmentPointConnections.Top));
    bottomLeft = verticalPoints.FirstOrDefault(p => p.X == topLeft.X && p.Y > topLeft.Y && p.Connections.HasFlag(SegmentPointConnections.Left | SegmentPointConnections.Bottom));

    if (topRight != null && bottomLeft != null)
    {
      SegmentPoint bottomRight;

      bottomRight = horizontalPoints.FirstOrDefault(p => p.X == topRight.X && p.Y == bottomLeft.Y && p.Connections.HasFlag(SegmentPointConnections.Right | SegmentPointConnections.Bottom));

      if (bottomRight != null)
      {
        Rectangle rectangle;

        rectangle = new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);

        this.Rectangles.Add(rectangle);
      }
    }
  }
}

In this method, we loop through all our defined points that have connections in the upper left corner.

For each matching point, we then try and find points with the following criteria

  • has the same Y co-ordinate and a right or top connection. This gives us the upper right corner.
  • has the same X co-ordinate and a left or bottom connection. This gives us the lower left corner.
  • if we have both the above corners, we then try and find a point that has the same X co-ordinate as the upper right corner, the same Y co-ordinate as the lower left corner, and right or bottom connections. This gives us the last corner, lower right
  • if we have all four corners, and the rectangle. The use of a HashSet ensures the same rectangle isn't added twice.

And that's all there is to it. With these two routines, I can now break up a rectangle into many smaller pieces just by defining pairs of points.

Another example of the programs output

Closing Remarks

There are a few things that I'm aware of that the code doesn't do

  • As mentioned (several times!) none of this code is particularly efficient. The more segments you add, the slower it will get. Gareth Rees posted a nice diagram of what I should be doing, and indeed his pointers help me get this code working originally.
  • It doesn't handle overlapping segments very well. It will rerun the point generation for these, adding to the overall time.
  • Ordering of the output rectangles isn't always what you expect - it jumps around a bit
  • The end coordinate must be equal to or greater than the start (using the sample, providing a negative segment size would trigger this bug).

As always the source code for this sample can be downloaded from the link below.

Downloads

ColorPicker Controls for Windows Forms

$
0
0
A newer version of this code is now available.

Back at the start of the new millennium, I had a publishing agreement with another company to publish our components under their branding. The first of these components was the ColorPicker ActiveX control. Roll on 13 years later and that publishing agreement is long expired, ActiveX is dead, and yet here I am again writing a color picker control. Except this time losing money rather than making it. There's probably a life lesson buried in there somewhere.

All of our current products ask for a color at least once (mostly buried in an options dialog), and some of the prototype products we are working on ask for more. Currently, we just wrap around the System.Drawing.Design.ColorEditor class, which overtime has identified a few problems:

  • Keyboard support is iffy (this is more to do with how it's implemented at our end I suspect)
  • You can't choose alpha channels, or enter custom values
  • The dependency on System.Design prevents us from targeting the Client Profile, and indeed will cause a crash if deployed to a system with only the Client Profile installed

The other option briefly considered was just to replace with the standard color common dialog. But this dialog looks and operates the same as it did back in Windows 3, and while that is pretty good from a consistent UI standpoint, it does mean it hasn't kept up with changing times - such as entering colors as hex values. I took a look at some other third party libraries but none of them were flexible enough for what I wanted.

In the end I went with writing my own set of C# based color picker controls which we're providing here as open source - we hope you find them useful. As there's quite a lot of code, I'm not going to describe them line by line as I've done in the past, but just provide an overview of each component.

ColorGrid Control

ColorGrid control demonstration

This control displays a grid of colors, and supports both a primary palette, and a custom color palete. Several properties are available for configuring the appearing of the control, and there are behaviour options too, such as built in editing of colors.

ColorWheel Control

ColorWheel control demonstration

This control displays a radial wheel of colors and allows selection from any point in the wheel. Not much in the way of customisation for this control!

ColorSlider Controls

ColorSlider controls demonstration

A bunch of controls (inheriting from a single base) that allow selection of values via a colourful bar. Similar to the TrackBar control you have a few options for specifying the drag handle's position and bar orientation.

ColorEditor Control

ColorEditor controls demonstration

This control allows you enter/select a color using RGB/HSL or hex formats.

ScreenColorPicker Control

ScreenColorPicker controls demonstration

This control allows the user to pick a color from any pixel displayed on the screen.

ColorPickerDialog Form

ColorPickerDialog demonstration

This form puts together the previous controls in a ready to use dialog.

ColorEditorManager

This is a non-GUI component that you can drop onto a form, and bind to other controls in this library. When the Color property of one control changes, it is reflected in the others without having to lift a finger. Useful if you're creating composite displays from multiple controls.

Color Palettes

The ColorGrid control has Colors and CustomColors properties which return a ColorCollection. These two properties make it easier as a developer to keep separate a primary palette whilst having the flexibility of custom colors, although it does complicate the control's internal logic a bit! The grid will automatically populate custom colors if you try and set the control's Color to a value not currently defined.

As well as manually populating ColorCollection instances, you can also load in external palette files. Paint.NET and the age old JASC 1.0 formats are currently supported.

Keyboard Support

All GUI components, with the exception of the ScreenColorPicker include full keyboard/focus support. Many controls support SmallChange and LargeChange properties which influence how navigation keys are processed. Although in the case of the ColorWheel it's not really a bonus... but that's what the ColorEditor control is best suited for!

Known Issues

  • XML documentation comments are incompleted
  • The ColorEditorManager control currently allows you to bind to the LightnessColorSlider control, but doesn't fully support it yet

Acknowledgements

Source Code

Download the source code from the link below, or from the GitHub page.

Downloads


ColorPicker Controls Update 1.0.2.0

$
0
0

I've been pretty busy recently pushing out updates to WebCopy, a pending update to Spriter and working on a game project so blog posts have suffered a bit. While I work to correct that, we've just pushed an update to the ColorPicker controls.

Important!

This update contains breaking changes due to a number of renamed classes and enum members.

So what's new?

  • All the somethingPaletteReader classes have been replaced with somethingPaletteSerializer. As the new names imply, palettes can now be written as well as read. This is a breaking change and may require some reworking of any code that used to use the old readers.
  • The ColorEditor now supports selecting of named colors as the hex editor is now a dropdown list. As well as being able to select named colors from the list, you can now also type names directly into the hex editor and they will be processed accordingly.
  • The ColorPickerDialog now can load and save palette files
  • Palette support has been reworked to allow the saving of palettes as well as loading. Unfortunately due to the initial names of these classes this is a breaking change, albeit a minor one.
  • Added a bit more documentation
  • Corrected some grammatical errors in existing documentation and headers
  • Added additional tests to ensure palettes are written correctly

New from 1.0.1.0:

  • GIMP Palette support
  • Some unit tests to make sure palettes are read correctly

Named color support

Support for loading and saving palettes

Downloads

Grab the update from the link below or from the GitHub page.

We hope you enjoy these improvements!

Downloads

Creating a custom TypeConverter part 1 - getting started

$
0
0

It is common practice to create a class that describes something, a person, a product - some entity or other. Your application may provide a sublime UI for editing these objects, or rely on something more basic such as a PropertyGrid. However, if you use this approach, you may find that some of your properties can't be edited. Yet examples abound of non-simple editing via the grid, such as colours, enumerations and image selection to name a few.

By making use of the TypeConverter and UITypeEditor classes, you can quite easily provide the ability to create richer editing support for your objects. This first article in this series will detail how to use TypeConverter allowing complex objects to be edited as though they were simple strings.

The Scenario

As with most of my articles, I'm starting with a real world example and a solid required. I need to store units of measurement, so for this I have a simple class that has a pair of properties describing a given measurement.

public class Length
{
  public override string ToString()
  {
    string value;
    string unit;

    value = this.Value.ToString(CultureInfo.InvariantCulture);
    unit = this.Unit.ToString();

    return string.Concat(value, unit);
  }

  public Unit Unit { get; set; }

  public float Value { get; set; }
}

A fairly standard class that simply has two properties along with a default (implicit) constructor. I'm also overriding ToString, as it's useful both for debugging purposes and for having something other than CustomTypeConverter1.Length displayed in the PropertyGrid.

And for the purposes of this demonstration, I have created a sample class which has three length properties.

internal class SampleClass
{
  public Length Length1 { get; set; }

  public Length Length2 { get; set; }

  public Length Length3 { get; set; }
}

Just for completeness sake, here's the Unit enum.

public enum Unit
{
  None,
  cm,
  mm,
  pt,
  px
}

Isn't that an ugly enum? For this example, it will suffice, but there is another article which describes an alternative approach.

First Steps

I've set up a sample project which binds an instance of our SampleClass to a PropertyGrid, with the Length1 property pre-set to 32px. When you run this project, you are left with a very unsatisfactory editing experience as you can't edit anything.

Default editing functionality... or lack thereof

So, what can we do about this?

The TypeConverterAttribute Class

The TypeConverterAttribute allows you to associate your class with a type that can handle conversion of instances of your type to and from other objects. You can only have one occurrence of this attribute per type. As with a lot of these types of attributes, you can provide the conversion type one of two ways:

[TypeConverter(typeof(LengthConverter))]

Here, we pass in a type object, meaning the type has to be directly referenced by your project and distributed as a dependency.

[TypeConverter("CustomTypeConverter1.LengthConverter, CustomTypeConverter1")]

Another alternative is to use a direct string, as shown above. This string is the fully qualified type name, meaning it could be located in a differently assembly, but one that isn't referenced directly or flagged as a dependency.

Which one you use depends on your needs, but bear in mind no compile time checking can be done of the string version, so if you get the name wrong, you won't find out until you are unable to edit the type!

The ExpandableObjectConverter

This class is built into the .NET Framework and will provide a minimum of functionality at minimum cost.

[TypeConverter(typeof(ExpandableObjectConverter))]
public class Length
{

If we change the declaration of our Length class to be the above and run our sample, we get this:

A little better, as long as you don't mind editing each property individually

The first property can now be expanded, and each property of the Length class can be individually set. However, there are two immediate problems with this approach:

  • Properties can only be edited one at a time, you can't combine values via the root property.
  • Properties with a null value (the second and third properties in the example screenshot) cannot be instantiated.

Again, depending on your requirements, this might be perfectly acceptable. In my case, it isn't, so on with the custom converter!

Writing a custom converter

In order to create a custom converter, you need to have a class which inherits from TypeConverter. At a minimum, you would override the CanConvertFrom and ConvertFrom methods.

Here's a sample converter for our simple Length class:

public class LengthConverter : TypeConverter
{
  public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
  {
    return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
  }

  public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
  {
    string stringValue;
    object result;

    result = null;
    stringValue = value as string;

    if (!string.IsNullOrEmpty(stringValue))
    {
      int nonDigitIndex;

      nonDigitIndex = stringValue.IndexOf(stringValue.FirstOrDefault(char.IsLetter));

      if (nonDigitIndex > 0)
      {
        result = new Length
        {
          Value = Convert.ToSingle(stringValue.Substring(0, nonDigitIndex)),
          Unit = (Unit)Enum.Parse(typeof(Unit), stringValue.Substring(nonDigitIndex), true)
        };
      }
    }

    return result ?? base.ConvertFrom(context, culture, value);
  }
}

So, what is this short class doing?

The first override, CanConvertFrom, is called when .NET wants to know if it can convert from a given type. Here, I'm saying "if you are a string, then yes I can convert" (or at least try!), otherwise it falls back and requests if the base converter can do the conversion. In most cases that'll probably be a "no", but it's probably a good idea to leave it in regardless;

Now for the interesting method. ConvertFrom does the type conversion. I'm going to ignore the context parameter for now as I haven't had a need for it. You can use the culture parameter as a guide if you need to do any conversions such as numbers or dates. The key parameter, is value as this contains the raw data to convert.

  • The first thing this method does is see if value is a non-null non-empty string. (If you're using .NET 4 or above you'd probably use the IsNullOrWhitespace method instead).
  • Next I try and find the index of the first letter character - the method assumes the input is in the form of <number><unit>.
  • If I find a letter, then I create a new Length object and use object initialization to set the Value property to be the first part of the string converted to a float, and Enum.Parse to set the Unit property using the latter part of the string. And that explains the horribly named enum. I'll still show you a better way though!

And that is all you need. Well almost, we need to change our class header:

[TypeConverter(typeof(LengthConverter))]
public class Length
{

Now when we run the sample project, we can directly type in a value into the different Length based properties and have them converted to the correct values, including creating new values.

With the custom type converter, we can now directly enter units of measurement

Note that this example doesn't cover clearing a value - for example if you enter an empty string. You could return a new Length object in this case and then change the ToString method to return an empty string. Simply returning null from ConvertFrom doesn't actually work, so at the moment I don't know the best method for accomplishing a value reset.

Error Handling

I haven't demonstrated error handling, firstly as this is a bare bones example, and also due to .NET providing it for you, at least in the case of the property grid. It will automatically handle the failure to convert a value. The disadvantage is the rather unhelpful error message. If you throw an exception yourself, the exception text you provide is displayed in the Details section of the dialog, allowing you to specifying a more succinct message.

Generic error messages aren't the best - do your users a favour and provide detailed exception text

Converting to a different data type

As well as converting a type into our class, we can also use a type converter to convert our class into another type by overriding the ConvertTo method.

In this example, the Length class overrides the ToString method. I would still recommend doing that in additional to this next tip, but as with everything, it depends on your purpose. In this case, we can use the ConvertTo method to convert our Length object into a string.

public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
  Length length;
  object result;

  result = null;
  length = value as Length;

  if (length != null && destinationType == typeof(string))
    result = length.ToString();

  return result ?? base.ConvertTo(context, culture, value, destinationType);
}

As with all the methods you override, if you can't explicitly handle the passed values, then ask the base class to attempt to handle it. The above method shows how I check to ensure the value is a Length object, and then if the destinationType is a string, I simply return value.ToString(). Whatever is returned via this method will appear in the PropertyGrid, so use caution if you decide to return formatted strings - you'll need to handle them in ConvertFrom.

There is another, more useful, purpose for this override, but I'll defer that for the next article.

Summing up

Adding a basic type converter is a very simple thing to do, and is something that can help enrich editing functionality, or even debugging (in lieu of an immediate window or scripting support, Cyotek products have a sample add-in which displays documents in a PropertyGrid for simple editing and querying). Even if you only go as far as adding the ExpandableObjectConverter attribute to a base class, it's more useful than nothing!

You can download the complete example from the link below.

Downloads

Creating a custom TypeConverter part 2 - Instance descriptors, expandable properties and standard values

$
0
0

In the first part of this article series, I described how to create a simple type converter for converting an object to and from a string. This follow up article expands upon that sample, to include more concise design time code generation, expandable property support and finally custom lists of values.

The examples in this article assume you are working from the original sample project from part 1.

Designer Code

When you place a Control or Component onto a design time surface such as a Form, the IDE will automatically generate any code required to initialize the object.

Modify the SampleClass class to inherit from Component then drop an instance onto the form and set the first property. Save the form, then open the designer file. You should see code something like this:

private void InitializeComponent()
{
  CustomTypeConverter2.Length length1 = new CustomTypeConverter2.Length();

  // ... SNIP ...

  // 
  // sample
  // 
  length1.Unit = CustomTypeConverter2.Unit.px;
  length1.Value = 32F;
  this.sample.Length1 = length1;
  this.sample.Length2 = null;
  this.sample.Length3 = null;

  // ... SNIP ...

}

The designer has generated the source code required to populate the object by specifying each property individually. However, what happens if you wanted to set both properties at once or perhaps perform some other initialization code? We can use our type converter to solve this one.

Although slightly outside the bounds of this article, it's probably worth mentioning nonetheless. In the snippet above, you can see the Length2 and Length3 properties are explicitly assigned null, even though that is already the default value of these properties. If you're creating public facing library components it's always a good idea to apply the DefaultValue attribute to properties. It makes for cleaner code (if the value is the default value, no code will be generated) and allows other components to perform custom processing if required. For example, the PropertyGrid shows default properties in normal style, and non-default ones in bold.

Updating the Length class

Before we can adjust our type converter to support code generation, we need to extend our Length class by adding a new constructor.

public Length()
{ }

public Length(float value, Unit unit)
  : this()
{
  this.Value = value;
  this.Unit = unit;
}

I've added one constructor which will set both Value and Unit properties of the class. Due to the addition of a constructor with parameters, I now need to explicitly define a parameterless constructor as an implicit one will no longer be generated and I still want to be able to do new Length().

With these modifications in place, we can now dive into the type converter modifications.

CanConvertTo

The first thing we need to do is update our type converter to state that it supports the InstanceDescriptor class which is the mechanism the IDE will use for the custom code generation. We can do this by overriding a new method, CanConvertTo.

Update the LengthConverter class from the previous article to include the following:

public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
  return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType);
}

This new overloads will inform the caller that we now support the InstanceDescriptor type, in addition to whatever the base TypeConverter can handle.

Extending ConvertTo

We briefly covered the ConvertTo override in the previous article in order to display our Length object as a string. Now that we have overridden CanConvertTo to state that we can handle additional types, we need to update this method as well.

The InstanceDescriptor class contains information needed to regenerate an object, and is comprised of two primary pieces of information.

  • A MemberInfo object which describes a method in the class. This can either be a constructor (which we'll use in our example), or something static that will return a new object - for example, Color.FromArgb.
  • An ICollection containing any of the arguments required to pass into the source member.

Lets update ConvertTo to include the extract support.

public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
  Length length;
  object result;

  result = null;
  length = value as Length;

  if (length != null)
  {
    if (destinationType == typeof(string))
      result = length.ToString();
    else if (destinationType == typeof(InstanceDescriptor))
    {
      ConstructorInfo constructorInfo;

      constructorInfo = typeof(Length).GetConstructor(new[] { typeof(float), typeof(Unit) });
      result = new InstanceDescriptor(constructorInfo, new object[] { length.Value, length.Unit });
    }
  }

  return result ?? base.ConvertTo(context, culture, value, destinationType);
}

We still do our null check to ensure we have a valid value to convert, but now we check to see if the type is either string or InstanceDescriptor and process accordingly.

For instance descriptors, we use Reflection in order to get the constructor which takes two parameters, and then we create an InstanceDescriptor object from that. Easy enough!

Now when we modify our SampleClass component in the designer, source code is generated similar to the below. (With the caveat of the warning in the next section)

Note that I'd also modified the properties on the SampleClass to include [DefaultValue(typeof(Length), "")] for default value support.

private void InitializeComponent()
{

  // ... SNIP ...

  // 
  // sample
  // 
  this.sample.Length1 = new CustomTypeConverter2.Length(16F, CustomTypeConverter2.Unit.px);

  // ... SNIP ...
  
}

Much cleaner!

A warning on Visual Studio

While writing this article, Visual Studio frequently took a huff and refused to generate the design time code. I assume it is due to Visual Studio caching the assembly containing the TypeConverter, or it is another manifestation of not being able to unload managed assemblies without destroying the application domain. Whatever the reason, I found it quickly to be a source of frustration requiring frequent restarts of the IDE in order to pick up changed code.

As an experiment, I did a test where the Length and LengthConverter classes were in another assembly referenced in binary form. In this mode, I didn't have a single problem.

Finally, whereas basic conversions are easy to debug, the InstanceDescriptor conversion is much less so.

Something to bear in mind.

Expandable properties

Returning to the ExpandableObjectConverter and property expansion, that is trivially easy to add to your custom converter by overriding the GetPropertiesSupported and GetProperties methods.

public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
  return true;
}

public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
  return TypeDescriptor.GetProperties(value, attributes);
}

First, by overriding GetPropertiesSupported we tell the caller that we support individual property editing. Then we can override GetProperties to return the actual properties to display.

In the above example, we return all available properties, which is probably normal behaviour. Let us assume the Length class has a property on it which we didn't want to see. We could return a different collection with that property filtered out:

public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
  //return TypeDescriptor.GetProperties(value, attributes);
  return new PropertyDescriptorCollection(TypeDescriptor.GetProperties(value, attributes).Cast<PropertyDescriptor>().Where(p => p.Name != "BadProperty").ToArray());
}

An awkward example, but it does demonstrate the feature.

The property grid honours the Browsable attribute - this is a much better way of controlling visibility of properties than the above!

Custom Values

The final example I want to demonstrate is custom values. Although you might assume that you'd have to create a custom UITypeEditor, if you just want a basic drop down list, you can do this directly from your type converter by overriding GetStandardValuesSupported and GetStandardValues.

public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
  return true;
}

public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
  List<Length> values;

  values = new List<Length>();
  values.Add(new Length(16, Unit.px));
  values.Add(new Length(32, Unit.px));
  values.Add(new Length(64, Unit.px));
  values.Add(new Length(128, Unit.px));

  return new StandardValuesCollection(values);
}

First you need to override GetStandardValuesSupported in order to specify that we do support such values. Then in GetStandardValues we simply return the objects we want to see. In this example, I've generate 4 lengths which I return. When you run the program, you can see and select these values. Of course, you need to make sure that the values you return can be handled by the ConvertFrom method!

A simple example of adding some standard values

Summing up

Adding even an advanced type converter is still a easy task, and is something that can help enrich editing functionality.

You can download the complete example from the link below.

Downloads

Using alternate descriptions for enumeration members

$
0
0

The last two articles (here and here) described creating a custom type converter for converting units of measurement.

However, what happens when you want to display or convert to/from alternative representations? For example, consider the enum below.

internal enum Unit
{
  cm,
  mm,
  pt,
  px
}

Apart from the fact such an enum more than likely doesn't match any coding standards you use, what happens when you want to include percentages in the mix? Not many languages are going to let you use % as a symbol name!

So we rewrite the enum to make more sense, in which case you might have this:

internal enum Unit
{
  Centimetre,
  Millimetre,
  Point,
  Pixel,
  Percent
}

Using the actual enum member names doesn't seem like a good idea does it?

Great! Except... your users want to see cm, px, % etc. Now what?

The manual way

Well, you could create a function which takes a unit, and manually checks the values and returns an appropriate value, for example:

public string GetUnitSuffix(Unit unit)
{
  string result;

  switch (unit)
  {
    case Unit.Centimetre:
      result = "cm";
      break;
    ...
  }

  return result;
}

While this would certainly work, it means you have to duplicate this code for every enum you wish to have alternate descriptions for. Not to mention, should you add a new member to the enum, you have to remember to update this function. More than likely, you also want a sister version of this function which accepts the string version, and returns the enum value.

The automatic way

A better way would be to tag each enum member with an appropriate description, then you can use reflection to scan your enum members and perform automatic to and from conversions.

In this example, I'm going to use the DescriptionAttribute from the System.ComponentModel namespace, although depending on what you're trying to do, a custom attribute may be better - that's not exactly what this attribute was intended for!

First, decorate your enum with the attribute.

internal enum Unit
{
  [Description("cm")]
  Centimetre,

  [Description("mm")]
  Millimetre,

  [Description("pt")]
  Point,

  [Description("px")]
  Pixel,

  [Description("%")]
  Percent
}

Next add a couple of functions that will perform the conversion of your enum to and from a string. With this in place you can add new members, and, as long as you add your attribute to them, the functions will automatically handle the new values.

public static string GetDescription(this Unit value)
{
  FieldInfo field;
  DescriptionAttribute attribute;
  string result;

  field = value.GetType().GetField(value.ToString());
  attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute));
  result = attribute != null ? attribute.Description : string.Empty;

  return result;
}

public static Unit GetValue(string value)
{
  Unit result;

  result = Unit.None;

  foreach (Unit id in Enum.GetValues(typeof(Unit)))
  {
    FieldInfo field;
    DescriptionAttribute attribute;

    field = id.GetType().GetField(id.ToString());
    attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;

    if (attribute != null && attribute.Description == value)
    {
      result = id;
      break;
    }
  }

  return result;
}

I choose to make the version that accepts the enum member as an input parameter an extension method, that way I can call it like this:

string unitSuffix;

unitSuffix = this.Unit.GetDescription();

However, as the sister method accepts a string parameter, it doesn't make sense to make this an extension, unless you want it to appear on every single string variable you declare! So I just revert back to the usual static calling convention.

Unit unit;

unit = EnumExtensions.GetValue(stringValue.Substring(nonDigitIndex));

Much better - the user sees the short form version, but the code uses the full name

Using Generics

While there's nothing wrong with the above methods, they could still be improved upon. As it stands now, the methods are fixed to a specific enum, so we can change them to use generics instead, then they'll work for all enums.

public static string GetDescription<T>(this T value)
  where T : struct
{
  FieldInfo field;
  DescriptionAttribute attribute;
  string result;

  field = value.GetType().GetField(value.ToString());
  attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute));
  result = attribute != null ? attribute.Description : string.Empty;

  return result;
}

public static T GetValue<T>(string value, T defaultValue)
{
  T result;

  result = defaultValue;

  foreach (T id in Enum.GetValues(typeof(T)))
  {
    FieldInfo field;
    DescriptionAttribute attribute;

    field = id.GetType().GetField(id.ToString());
    attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;

    if (attribute != null && attribute.Description == value)
    {
      result = id;
      break;
    }
  }

  return result;
}

Final points

Using reflection does have an overhead. If you expect to be calling these methods a lot, you may wish to extend them yet further in order to support caching the results in a dictionary or other mechanism of your choice. That way, the first time a new member is requested you perform the reflection lookup, and thereafter just read the cache. I haven't done any benchmarking, but it's probably safe to say a dictionary lookup (remember to use TryGetValue!) is going to be a lot faster than a reflection scan.

An example showing how the custom type converter from the previous two articles updated to use the above technique is available from the link below.

Downloads

ImageBox 1.1.4.0 update

$
0
0
A newer version of this code is now available.

Today we've released a new updated version of the ImageBox control, with a nice collection of enhancements and a few bug fixes.

Full change log for this update:

Changes and new features

  • Added NuGet package
  • Added a license file to hopefully cut down on questions about usage. The ImageBox control is licensed under the MIT license, allowing you free reign to use it in your projects, commercial or otherwise. See imagebox-license.txt for the full text.
  • Added a new SizeMode property. This allows you to switch between Normal, Fit and Stretch modes. Stretch is a new mode for the ImageBox, and acts similar to existing Fit functionality except the aspect ratio is not preserved.
  • The SizeToFit property has been marked as deprecated and should no longer be used. The SizeMode property has a Fit value that should be used instead. Setting the SizeToFit property will now manipulate SizeMode instead.
  • Added a new CenterPoint property. This property returns the pixel at the center of the current image viewport.
  • Added a bunch of missing XML comments documentation.
  • Added new overloads for most methods that accepted a source Rectangle, Point or Size to also accept float and int arguments.
  • Added a new Zoomed event that uses new ImageBoxZoomEventArgs arguments. This new event allows you to tell if the zoom was in or out, how it was raised, and current and previous zoom values. Not hugely thrilled with how aspects of this change has been internally implemented, so implementation methods are private rather than virtual so I can change them without affecting the signature.
  • Added new CenterToImage method which resets the viewport to be centered of the image, in the same way as zooming via the keyboard used to work.
  • Added support for animated GIF's, thanks to a contribution from Eggy. Note animations only play at runtime, not design time.
  • The Text and Font properties are now available and, if set, will be displayed in the control. You can use the ForeColor, TextBackColor, TextAlign, TextDisplayMode and ScaleText properties to determine how the text will be rendered.
  • A new DrawLabel method that performs text drawing is available for use by custom implementations or virtual modes.

Demonstration Changes

  • Added a new Scaled Adornments demonstration, showing how easy it is to add custom drawing that is scaled and positioned appropriately.
  • Added a new Switch Image During Zoom demonstration, a demo with an unwieldy name that shows how to switch out a low resolution image with a higher detailed one as you zoom into an ImageBox.
  • Added new Text and Size Mode demonstrations.

Bug Fixes

  • Zooming in and out with the keyboard now keeps the view centered to the same pixel that was centered prior to the zoom
  • Zooming in and out with the keyboard is now correctly disabled if the AllowZoom property is False, or the SizeMode property is a value other than Normal. This means keyboard behaviour now matches mouse behaviour.
  • If the mouse wheel was rapidly spun (thus having a multiple of the base delta), the Zoom property was only adjusted once
  • Setting the GridScale property to None rendered the default Small grid. Using a scale of None now correctly just fills the grid area with a solid brush from the GridColor property.
  • The MouseWheel event is now available
  • Layout changes no longer occur if the AllowPainting property is false through use of the BeginUpdate method.
  • Fixed various documentation errors

Downloads

As usual, either grab the source from GitHub, from the link below, or make use of the NuGet package!

Downloads

Creating long running Windows Forms applications without a start-up form

$
0
0

Sometimes you may wish to create an application that sits running in the background but doesn't actually display an initial user interface. However, the user can interact with the application and so therefore its not appropriate to be a service. Often such applications are accessible from a system tray icon. Another viable requirement might be for multiple top level windows, for example recent versions of Microsoft Word, where each document has its own application window.

By default however, a normal Windows Form application displays a single start-up form which definitely isn't desirable when you want to have a hidden UI, especially as hiding this form isn't as straightforward as you might expect. Fortunately however, the framework provides us with the ApplicationContext class that we can use to create a different approach to managing the application.

Getting Started

If you look in Program.Main, you'll see code similar to the following:

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());

The Application.Run statement is the critical aspect, and it operates by creating a new instance of your start-up form which, when closed, causes the application to end. The Run method also allows you to pass in a custom ApplicationContext class instead which can be used for more flexibility. In order to exit the application when using this class, you can either call the static Application.ExitThread or the ExitThread method of the ApplicationContext class. In additional, Application.Exit seems to work just as well.

To start with, we're going to create a basic class that inherits from ApplicationContent and provides system tray icon and context menu support. To that end, we'll create a class named TrayIconApplicationContext.

The first thing we need to do is hook into the ApplicationExit event. We'll use this for clean-up purposes no matter if the application is shut down via our new class, or other code calling ExitThread.

public abstract class TrayIconApplicationContext : ApplicationContext
{
  protected TrayIconApplicationContext()
  {
    Application.ApplicationExit += this.ApplicationExitHandler;
  }

  protected virtual void OnApplicationExit(EventArgs e)
  {

  }

  private void ApplicationExitHandler(object sender, EventArgs e)
  {
    this.OnApplicationExit(e);
  }
}

When the event handler is triggered, it calls the OnApplicationExit virtual method. This makes it easier for inheritors of this class to provide their own clean up behaviour without hooking into events. It seems a shame there isn't an existing method to override in the first place without the the initial hooking of events, but it's a minor thing.

Adding the tray icon

Now that we have the basic infrastructure in place, we can add our tray icon. To do this we'll create an instance of the NotifyIcon component, accessible via an protected property. We'll also automatically hook into the Click and DoubleClick events of the icon and provide virtual methods for inheritors.

private readonly NotifyIcon _notifyIcon;

protected TrayIconApplicationContext()
{
  Application.ApplicationExit += this.ApplicationExitHandler;

  _notifyIcon = new NotifyIcon
  {
    Text = Application.ProductName,
    Visible = true
  };
  this.TrayIcon.MouseDoubleClick += this.TrayIconDoubleClickHandler;
  this.TrayIcon.MouseClick += this.TrayIconClickHandler;
}

protected NotifyIcon TrayIcon
{
  get { return _notifyIcon; }
}
  
protected virtual void OnTrayIconClick(MouseEventArgs e)
{ }

protected virtual void OnTrayIconDoubleClick(MouseEventArgs e)
{ }

private void TrayIconClickHandler(object sender, MouseEventArgs e)
{
  this.OnTrayIconClick(e);
}

private void TrayIconDoubleClickHandler(object sender, MouseEventArgs e)
{
  this.OnTrayIconDoubleClick(e);
}

We'll also update OnApplicationExit to clear up the icon:

if (_notifyIcon != null)
{
  _notifyIcon.Visible = false;
  _notifyIcon.Dispose();
}

Even though we are setting the icon's Visible property to true, nothing will happen as you need to assign the Icon property first.

Adding a context menu

Having a tray icon is very nice, but if the only interaction possible with our application is double clicking the icon, it's a bit of a limited application! We'll solve this by adding a ContextMenuStrip to the class, which will be bound to the icon. Inheritors can then populate the menu according to their requirements.

private readonly ContextMenuStrip _contextMenu;

protected TrayIconApplicationContext()
{
  _contextMenu = new ContextMenuStrip();

  Application.ApplicationExit += this.ApplicationExitHandler;

  _notifyIcon = new NotifyIcon
  {
    ContextMenuStrip = _contextMenu,
    Text = Application.ProductName,
    Visible = true
  };
  this.TrayIcon.DoubleClick += this.TrayIconDoubleClickHandler;
  this.TrayIcon.Click += this.TrayIconClickHandler;
}

protected ContextMenuStrip ContextMenu
{
  get { return _contextMenu; }
}

Again, we'll update the exit handler to dispose of the menu:

if (_contextMenu != null)
  _contextMenu.Dispose();

Creating the application

With our reusable application context class ready, we can now create a custom application specific version. Of course, you don't have to do this, you could just make the changes directly to the original class, but it's better to promote resuse where you can.

For example, a basic application which had a settings dialog and an about dialog could look something like this:

class CustomApplicationContext : TrayIconApplicationContext
{
  public ApplicationContext()
  {
    this.TrayIcon.Icon = Resources.SmallIcon;

    this.ContextMenu.Items.Add("&Settings...", null, this.SettingsContextMenuClickHandler).Font = new Font(this.ContextMenu.Font, FontStyle.Bold);
    this.ContextMenu.Items.Add("-");
    this.ContextMenu.Items.Add("&About...", null, this.AboutContextMenuClickHandler);
    this.ContextMenu.Items.Add("-");
    this.ContextMenu.Items.Add("E&xit", null, this.ExitContextMenuClickHandler);
  }

  protected override void OnTrayIconDoubleClick(MouseEventArgs e)
  {
    this.ShowSettings();

    base.OnTrayIconDoubleClick(e);
  }

  private void AboutContextMenuClickHandler(object sender, EventArgs eventArgs)
  {
    using (Form dialog = new AboutDialog())
      dialog.ShowDialog();
  }

  private void ExitContextMenuClickHandler(object sender, EventArgs eventArgs)
  {
    this.ExitThread();
  }

  private void SettingsContextMenuClickHandler(object sender, EventArgs eventArgs)
  {
    this.ShowSettings();
  }

  private void ShowSettings()
  {
    using (Form dialog = new SettingsDialog())
      dialog.ShowDialog();
  }
}

This sample creates a context menu with 3 items; two dialogs and a way to exit the program. Double clicking the icon also displays a dialog. Convention usually suggests that for the context menu, you display the primary item in bold - so in this example the bold item opens the settings dialog, matching the double click action.

Finally, we need to modify the entry point of our application to use the new class.

[STAThread]
private static void Main()
{
  Application.EnableVisualStyles();
  Application.SetCompatibleTextRenderingDefault(false);
  Application.Run(new CustomApplicationContext());
}

And that's how simple it is to set up an application with no start up form!

Notes

While writing a real utility program that made use of this technique for an application accessed via the system I had the following observations:

  • Dialogs opened from this class are not modal. You can see this by double clicking the tray icon several times - if you call ShowDialog, they aren't modal and you can can therefore open multiple dialogs by accessing the menu again etc. It's probably better to have instance variables for such forms, and then create them on first use, and activate the existing instance on subsequent calls. The full source code download available below shows examples of this.
  • Mixing the MouseClick and MouseDoubleClick events to show windows doesn't really work as shown in the example project. Perhaps this can be worked around by using the MouseUp event instead and theSystemInformation.DoubleClickSize / SystemInformation.DoubleClickTime properties but that's beyond the scope of this article.
  • As there is no top level main window to appear in the taskbar, you should probably ensure any window that can be opened directly from the tray icon has its Icon, ShowIcon and ShowInTaskbar properties set.
  • Opened dialogs were frequently displayed behind existing windows of other applications. I didn't observe this while debugging the project, but only when running the program outside the IDE. The simplest way I found to work around this issue was to call this.Activate() from the Shown event
protected override void OnShown(EventArgs e)
{
  base.OnShown(e);

  this.Activate();
}

As usual an example project is available from the link below containing a demonstration of this technique.

Downloads

Downloading new and changed Azure storage blobs at scheduled intervals

$
0
0

Background

I recently finished moving our Lumitix server from Amazon's EC2 into a hybrid of an Azure Web Role and Azure Blob Storage, and a local machine. It's been running for a couple of weeks now without a spot of bother - I really like Azure.

However, one thing I did need was a way of pulling down the files in blob storage so the local loader service could process them. I ended up writing a rough and ready scheduling app to download any new files (and as an afterthought, modified files or optionally uploading of local files).

tl;dr

Grab the sample from the link at the end of the post!

About the application

Running normally

The sample project attached to this post is a small application which sits in the system tray without a default user interface. At regular intervals the application connects to the configured Azure storage account sand pulls down any new or modified files, and also optionally uploads files as well.

I wrote this app in a hurry and so I neglected to add incidental things such as logging. If a job fails, it'll display a balloon tip with the error message... but you'll have to catch it fast!

Error tooltip

Note: This sample makes use of Nuget packages. If you have version 2.7 or later installed, or enable Package Restore, this should automatically download the missing packages as I'm not including several MB of binary files in a source code download!

I'm not going to describe the source as using the Azure storage API is pretty much as easy as it gets. If you find this example project useful, then I'm glad it helped!

Configuration

Double clicking the system tray icon allows you to configure the storage accounts to look it. I've tested it downloading from two accounts, and downloading + uploading to a further account, and so far it's been running smoothly, but it's rough code, so while it works for me, it might not for you.

Application settings

Download task properties

Why use a custom scheduler instead of using Windows?

The Windows Task Scheduler is a powerful beast (certainly in latter versions of Windows) and includes many bells and whistles. You could certainly use this to run a download task every few minutes if required.

The main reason I went with using a custom scheduler is because I wanted the system tray icon to let me know what's happening. And if the program exits as soon as it's finished, then I'll not be able to see any errors without having to manually check logs. Which sort of defeats the purpose of using automation.

If you are happy with the stability of the program (or perhaps add the logging that I neglected or some other form of notifications) then you could just change the program to remove the scheduling aspects, and then run it every so often via Windows and save yourself some resources.

A note on the task scheduler

I haven't included the source for the task scheduler, as it's not entirely my own work - the task scheduler I normally use is too tightly integrated with one of our products for me to easily pull it out, and so I chopped together one based partly on this, and partly one some random source I found on the internet. I'll update this download with the full scheduler source once I've properly separated our custom one and remove the plagiarized one.

Limitations

As I mentioned, I built this in a hurry, so it doesn't have a great deal of polish. Amongst the more notable omissions are

  • No logging support. It won't tell you what it has downloaded or uploaded
  • Sub folder support. It doesn't do sub folders, either local or remote.
  • Uploaded files don't keep the local file's modification time stamp. That's actually a real annoyance for me, but unfortunately those properties are read-only for the remote blobs.

Assuming I extend this application to address these (and any other) limitations, I'll update the source download below.

License

The source code associated with this article is licensed under the MIT License. See license.txt in the download for more details.

Downloads


Getting the hWnd of the edit component within a ComboBox control

$
0
0

I'm currently in the process of dusting off the bugs from a tool written back in 2011. One of the bugs involves an editable ComboBox control paired with a separate Button control. When the button is clicked a popup menu is shown, and when an item in this menu is clicked, the text of the ComboBox is updated.

The problem with this scenario is by default, as soon as the ComboBox loses focus, the SelectionStart, SelectionLength and SelectedText properties are reset, thus preventing my little menu from replacing the selected text. While this article doesn't solve this problem (I'll save that for the next article!), it does describe how you can get the hWnd, or window handle (in .NET terms the Handle) of the edit component, thus opening the door for a little Win32 API fun.

There are various approaches to doing this - you could use FindWindow and search for a child window with a class of edit, or use SendMessage with the CB_GETCOMBOBOXINFO message. Or, easiest of all, we can use the GetComboBoxInfo function.

A simple demonstration application

Interop Declarations

Using this API is very simple, as there's one method and two simple structs.

[DllImport("user32.dll")]
public static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);

[StructLayout(LayoutKind.Sequential)]
public struct COMBOBOXINFO
{
  public int cbSize;
  public RECT rcItem;
  public RECT rcButton;
  public int stateButton;
  public IntPtr hwndCombo;
  public IntPtr hwndEdit;
  public IntPtr hwndList;
}

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
  public int left;
  public int top;
  public int right;
  public int bottom;
}

Calling the API

There is one (well, many, but lets start simple!) caveat which you may fall foul of if you are new to Win32 API programming - often the contents of structs need to be initialized with their size first. But how to you know how big a structure is? Lucky for you, you don't need to calculate it manually - the Marshal.SizeOf function will handle this for you.

If you forget to set the size, then the structure simply won't be populated.

NativeMethods.COMBOBOXINFO info;

info = new NativeMethods.COMBOBOXINFO();
info.cbSize = Marshal.SizeOf(info);

if (NativeMethods.GetComboBoxInfo(control.Handle, ref info))
{
  // the info structure is now populated, go wild!
}

Getting the edit control handle

With the above in place, then getting the handle of the edit component is very straightforward.

private IntPtr GetEditHandle(ComboBox control)
{
  NativeMethods.COMBOBOXINFO info;

  info = new NativeMethods.COMBOBOXINFO();
  info.cbSize = Marshal.SizeOf(info);

  return NativeMethods.GetComboBoxInfo(control.Handle, ref info) ? info.hwndEdit : IntPtr.Zero;
}

Bear in mind that the edit control is a Win32 control - not a managed .NET control. In order to do anything with this therefore, you need to use additional Win32 API methods, or perhaps bind it to a NativeWindow class for easy subclassing. I'll briefly cover some of this in a future article.

Other goodies

The COMBOBOXINFO structure has other information, such as the handle of the list control, which you see if you set the DropDownStyle property of the ComboBox to Simple and the state of the dropdown button. You can view the MSDN Documentation to learn more about the structure.

Downloads

Visual Studio Extension for adding multiple projects to a solution

$
0
0

Background

My solutions have lots of references to other projects, either common libraries or unit testing libraries. Neither of these scenarios lend well to manual binary references or NuGet packages, so I have lots of source code projects loaded for each solution.

When creating a new solution (or retro fitting an existing solution to use new libraries), I end up using File | Add | Existing projecta lot. As I was curious about how extensions in Visual Studio worked, I decided to write a very simple one to ease the grunt work of adding multiple common projects.

The last time I wrote an extension (or addin as they were called back then :)) for an IDE was vbCodeShield for Visual Basic 6 and that was years ago. I was incredibly surprised to find that writing an extension for Visual Studio is pretty much the same as it was (if not worse) - a horrible mass of unintuitive COM interfaces and clunky code. Whatever happened to the managed dream?

On the plus side, they are much easier to debug than I remember VB6 addins being. Or at least, they would be if Resharper didn't raise continuous exceptions while running in Visual Studio's Experimental Instance.

Almost Ranting

Still, I created the extension without too much pain, although I have to say it's some of the code I'm least proud of, and I'm certainly not going to walk through the code on this blog.

I gather you're supposed to use WPF to write extensions, but well... I wasn't going to learn WPF and the DTE at the same time. I actually tried (the aborted attempt is still in the source tree) to use a WPF dialog as recommended by the MSDN docs, but after finding simple things like checked list boxes (or even just list views with checkboxes) seemed to have a learning curve equal to the Moon landing, I went back to Windows Forms and had it knocked up in no time.

The code is messy, isn't using WPF, doesn't have a great deal of exception handling, and is full of artefacts from the wizard generation. But, it does seem to work.

Using the extension

Accessing the extension

To use the extension, open the Tools menu and choose Add Projects. This will open a lovely unthemed Windows Forms dialog containing an empty list of projects.

Adding a single project to the MRU

To add a single project to the list, click the Add File button then select the project you want to include.

Adding multiple projects to the MRU

To add multiple projects to the list, click the Add Folder button, then select a folder. After you've selected a folder, all projects in this folder and its subfolders will be added to the list.

Removing projects from the MRU

You can remove projects from the list, just select them and press the Delete key or the Remove button.

Adding projects to your solution

Using the extension

Just place tick each project you want to add to your solution, then click the OK button. It will then try and add each selected project to your solution, skipping any that are already present.

Configuration Settings

The settings for the extension are saved into an XML file located at %AppData%\Cyotek\VisualStudioExtensions\AddProjects\config.xml.

<?xml version="1.0"?><ExtensionSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Filter>C# Projects (*.csproj)|*.csproj|All Files (*.*)|*.*</Filter><Projects><string>C:\Checkout\cyotek\source\Libraries\Cyotek.ApplicationServices\Cyotek.ApplicationServices.csproj</string><string>C:\Checkout\cyotek\source\Libraries\Cyotek.ApplicationServices.Commands\Cyotek.ApplicationServices.Commands.csproj</string><!-- SNIP --><string>C:\Checkout\cyotek\source\Libraries\Cyotek.Windows.Runtime.Support\Cyotek.Windows.Runtime.Support.csproj</string><string>C:\Checkout\cyotek\source\Libraries\Cyotek.Windows.Runtime.Testing\Cyotek.Windows.Runtime.Testing.csproj</string></Projects></ExtensionSettings>

The Filter element lets you specify the filter for the Add File dialog, and is also used by Add Folder to search for appropriate files - if you write in Visual Basic rather than C# you'll probably want to change the filter to be vbproj rather than csproj!

The Projects element stores the MRU list of projects.

Closing

That's pretty much it - it's a very simple extension, but potentially a starting point for something more interesting.

You can download the code or the compiled VSIX installer from the links below.

Downloads

Cyotek Add Projects Extension updated for Visual Studio 2013 RTM

$
0
0

In my last post I introduced Cyotek Add Projects, a simple extension for Visual Studio that allowed you to add multiple projects to a solution.

However, I'd left the VSIX manifest version at 11.0, meaning it would only install on Visual Studio 2012. I've updated this so that it should install on Visual Studio 2012 or higher - certainly it's now installed on my fresh install of Visual Studio 2013.

I've made no other code changes so I haven't updated the source archive (I'll save that for the WPF overhaul!), but just updated the VSIX file, which you can download below.

Downloads

Specifying custom text when using the LabelEdit functionality of a TreeView

$
0
0

Recently I was updating a support tool that displays documents in raw form and allows editing of them. This tool is centred around a TreeView, and the Text property of each TreeNode is a concatenation of a name and one or more values.

The problem with this approach is if you wish to allow the nodes to be edited using the built in functionality you're in trouble as by default you can't actually influence the text that appears in the in-line editor. In other applications of a similar nature I used owner-drawn trees as I was using different styles for the name and the value. In this case, I just wanted the standard look.

An example project showing the different techniques described in this article.

How you would expect it to work

Ideally, you'd expect that by hooking into the BeforeLabelEdit event (or overriding OnBeforeLabelEdit) then you could manipulate the NodeLabelEditEventArgs.Label property. Except this property is read only.

Scratch that then. What about setting the TreeNode.Text property to something else in this event, then resetting it afterwards? Nope, doesn't work either.

Therefore, using just the managed code of the TreeView it's not possible to do what we want. Lets get slightly outside the black box with a little Win32 API. We'll get the handle of the edit control the TreeView is using and directly set it's text.

Getting the handle to the EDIT control

In order to manipulate the edit control, we first need to get a handle to it. We can do this succinctly by overriding OnBeforeLabelEdit (or hooking the BeforeLabelEdit event although the former is preferable) and using the TVM_GETEDITCONTROL message.

[DllImport("USER32", EntryPoint = "SendMessage", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

public const int TVM_GETEDITCONTROL = 0x110F;

protected override void OnBeforeLabelEdit(NodeLabelEditEventArgs e)
{
  IntPtr editHandle;

  editHandle = SendMessage(this.Handle, TVM_GETEDITCONTROL, IntPtr.Zero, IntPtr.Zero);
  if (editHandle != IntPtr.Zero)
  {
    // we have a handle, lets use it!
  }

  base.OnBeforeLabelEdit(e);
}

Setting the text of the EDIT control

Now that we have a handle, we can painlessly use WM_SETTEXT to change the text of the edit control

[DllImport("USER32", EntryPoint = "SendMessage", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, string lParam);

public const int WM_SETTEXT = 0xC;
    
protected override void OnBeforeLabelEdit(NodeLabelEditEventArgs e)
{
  // -- snip --

  if (editHandle != IntPtr.Zero)
    SendMessage(editHandle, WM_SETTEXT, IntPtr.Zero, "Magic String");

  // -- snip --
}

OK, but what about specifying the real text?

If you were hooking into the BeforeLabelEdit event, then you can just have your own logic in that event to determine the text to apply. If however you're overriding OnBeforeEdit in order to make a nice reusable component, you need another way of allowing implementers to specify the value. For this, I added a new event to the control.

[Category("Behavior")]
public event EventHandler<NodeRequestTextEventArgs> RequestEditText;

protected virtual void OnRequestEditText(NodeRequestTextEventArgs e)
{
  EventHandler<NodeRequestTextEventArgs> handler;

  handler = this.RequestEditText;

  if (handler != null)
    handler(this, e);
}

The NodeRequestTextEventArgs class is essentially a clone of NodeLabelEditEventArgs except with a writeable Label property. I also decided to allow you to cancel the node edit from this event, so that implementers don't have to hook both events unless necessary.

public class NodeRequestTextEventArgs : CancelEventArgs
{
  public NodeRequestTextEventArgs(TreeNode node, string label)
    : this()
  {
    this.Node = node;
    this.Label = label;
  }

  protected NodeRequestTextEventArgs()
  { }

  public TreeNode Node { get; protected set; }

  public string Label { get; set; }
}

Our final version now looks like this:

protected override void OnBeforeLabelEdit(NodeLabelEditEventArgs e)
{
  NodeRequestEditTextEventArgs editTextArgs;

  // get the text to apply to the label
  editTextArgs = new NodeRequestTextEventArgs(e.Node, e.Node.Text);
  this.OnRequestEditText(editTextArgs);

  // cancel the edit if required
  if (editTextArgs.Cancel)
    e.CancelEdit = true;

  // apply the text to the EDIT control
  if (!e.CancelEdit)
  {
    IntPtr editHandle;
    
    editHandle = SendMessage(this.Handle, TVM_GETEDITCONTROL, IntPtr.Zero, IntPtr.Zero); // Get the handle of the EDIT control
    if (editHandle != IntPtr.Zero)
      SendMessage(editHandle, WM_SETTEXT, IntPtr.Zero, editTextArgs.Label); // And apply the text. Simples.
  }

  base.OnBeforeLabelEdit(e);
}

And an sample usage scenario from the demo application:

private void subclassedTreeView_RequestEditText(object sender, NodeRequestEditTextEventArgs e)
{
  e.Label = e.Node.Name;
}

In this example, we are setting the edit text to be the value of the TreeNode's Name property, regardless of whatever its Text is.

Updating the text post-edit

After the conclusion of the label editing, the node's text will be set to the new label, and therefore we need to tinker that logic to allow the implementer to specify the new value text.

You could just hook the AfterLabelEdit event and have your custom logic in there (remembering to cancel the default edit), as shown here:

private void notifyTreeView_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
{
  if (e.Label != null)
  {
    e.CancelEdit = true;

    e.Node.Name = e.Label;
    e.Node.Text = string.Format("{0}: {1}", e.Label, e.Node.Tag);
  }
}

However, I didn't want to be having to do this type of code each time I implemented this sort of behaviour in an application. Rather than get fancy with subclassed TreeNode classes, I choose to add a sister event for RequestEditText, named RequestDisplayText and then handle this automatically. This is the only aspect of this article that feels "smelly" to me - ideally it would be nice if the control could handle this for you without having to ask for more information. But, this should do for the time being.

protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
{
  if (e.Label != null) // if the user cancelled the edit this event is still raised, just with a null label
  {
    NodeRequestTextEventArgs displayTextArgs;

    displayTextArgs = new NodeRequestTextEventArgs(e.Node, e.Label);
    this.OnRequestDisplayText(displayTextArgs);

    e.CancelEdit = true; // cancel the built in operation so we can substitute our own

    if (!displayTextArgs.Cancel)
      e.Node.Text = displayTextArgs.Label;
  }

  base.OnAfterLabelEdit(e);
}

And an example of use:

private void subclassedTreeView_RequestDisplayText(object sender, NodeRequestTextEventArgs e)
{
  e.Label = string.Format("{0}: {1}", e.Label, e.Node.Tag);
}

The demonstration shows both of these approaches - the TreeViewEx control favours the RequestDisplayText event, and the TreeViewExNotify control leaves it entirely to the implementer to deal with.

Closing notes

And that's it. I've seen some implementations of this sort of functionality in various places across the internet, and some of them are pretty awful, having to override all sorts of methods, store and restore various states. The above solution is pretty simple and works regardless of if you are calling TreeNode.BeginEdit or using the "click and hover" approach on a node.

In addition, it's trivially easy to expand this to support validation as well, I'll cover that in the next article.

Bonus Chatter

I originally tried two different approaches to modifying the value, both of these involved capturing the TVN_BEGINLABELEDIT notification. The first approach used a NativeWindow bound to the TreeView control's parent watching for the WM_NOTIFY message. The second approach did the same thing, but used MFC's Message Reflection via WM_REFLECT to intercept the notification message on the tree view itself. Both of these solutions worked, and yet were still overkill as overriding OnBeforeLabelEdit is sufficient.

Although I'm not going to describe that approach here as it'll just clutter the article, I did include an implementation of the WM_REFLECT solution in the demonstration project as I think it is a neat technique and potentially useful for other notifications.

Downloads

Extending the LabelEdit functionality of a TreeView to include validation

$
0
0

In my last post I described how to extend the default label edit funcionality of a TreeView control to be somewhat more flexible, by allowing you to specify custom text other than blindly using the text of the TreeNode being edited.

This post will extend the original code to include custom validation. For example, you may wish to restrict the available characters, or check to see if the value entered doesn't match an existing value.

An example project showing the use of validation in node editing, and preserving the entered text between errors.

Getting started

The code in this article assumes you have a base class that includes the enhancements from the previous post, or you can download the complete example from the link at the end of the article.

Firstly we need to add a new event that will present the proposed change and allow the implementer to validate it. As this is event won't allow the label to be modified, we can use the original NodeLabelEditEventArgs class rather than the custom class we created in the previous post.

[Category("Behavior")]
public event EventHandler<NodeLabelEditEventArgs> ValidateLabelEdit;

protected virtual void OnValidateLabelEdit(NodeLabelEditEventArgs e)
{
  EventHandler<NodeLabelEditEventArgs> handler;

  handler = this.ValidateLabelEdit;

  if (handler != null)
    handler(this, e);
}

We also need a backing variable to store the current text string in the event of a validation error in order to correctly re-initialize the edit field.

private string _postEditText;

Raising the validation event

In our extended TreeView component, we had overridden OnAfterLabelEdit in order to obtain the new display text after a successful edit. We're going to modify this override slightly in order to handle validation.

protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
{
  if (e.Label != null) // if the user cancelled the edit this event is still raised, just with a null label
  {
    NodeLabelEditEventArgs validateEventArgs;

    e.CancelEdit = true; // cancel the built in operation so we can substitute our own

    validateEventArgs = new NodeLabelEditEventArgs(e.Node, e.Label);

    this.OnValidateLabelEdit(validateEventArgs); // validate the users input

    if (validateEventArgs.CancelEdit)
    {
      // if the users input was invalid, enter edit mode again using the previously entered text to give them the chance to correct it
      _postEditText = e.Label;
      e.Node.BeginEdit();
    }
    else
    {
      // -- snip --
    }
  }

  base.OnAfterLabelEdit(e);
}

Here, we automatically cancel the default handling of the label edit, as regardless of whether validation passes or not, we'll be updating node text manually.

First we raise our ValidateLabelEdit event, passing in the TreeNode to be edited, and the proposed label text. If the CancelEdit property of the passed NodeLabelEditEventArgs is set to true, then validation has failed.

If validation does fail, we update the _postEditText variable we defined earlier with the current label text, then automatically switch the control back into label editing mode.

Changing how label edits are initialized

There's just one thing left to change. As with OnAfterLabelEdit, we had also overridden OnBeforeLabelEdit in order to modify the text displayed in the edit field. We'll need to modify this to provide the current label value if a validation error occurs, otherwise the text will reset to whatever the original value was before editing started. Of course, in the event of a validation error you want he user to be able to retry with the modified value to allow correction of the error. To do this, we'll modify the block of code that obtained the text to display to use the new _postEditText variable and to skip raising the RequestEditText event if its set. We'll also reset the _postEditText to null so that the next time an edit is started, it reverts to the original behaviour. Unless it's another validation error for the current edit operation of course!

protected override void OnBeforeLabelEdit(NodeLabelEditEventArgs e)
{
  NodeRequestTextEventArgs editTextArgs;

  // get the text to apply to the label
  editTextArgs = new NodeRequestTextEventArgs(e.Node, _postEditText ?? e.Node.Text);
  if (_postEditText == null)
    this.OnRequestEditText(editTextArgs);
  _postEditText = null;

  // -- snip --

  base.OnBeforeLabelEdit(e);
}

And that is it. Extremely simple, but very useful if you need to validate this sort of input!

Sample application

The sample project available with this article demonstrates validation, as shown in the following snippet.

private void subclassedTreeView_ValidateLabelEdit(object sender, NodeLabelEditEventArgs e)
{
  TreeNode[] matchingNodes;

  // Check to make sure the value the user enters isn't used by any other node than the current.
  // This code assumes that all names in the tree are unique, regardless of level
  matchingNodes = subclassedTreeView.Nodes.Find(e.Label, true);
  if (matchingNodes.Length != 0 && matchingNodes[0] != e.Node)
  {
    MessageBox.Show("You must enter a unique value.", "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
    e.CancelEdit = true;
  }
}

Further Improvements

As can be seen from the simple animation at the start of the article, the edit field is hidden and the original node text displayed, validation occurs, then editing restarts in the event of an error. This means, if you display a message box for example, the original tree state is displayed. It also means that the cursor and selection state of the edit field is lost. Ideally, it would be preferable to do validation without causing the edit field to vanish first, but that would require some more pinvoke, and probably isn't necessary for most cases - this method keeps the users entered text which is the important bit.

Downloads

Viewing all 559 articles
Browse latest View live