Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

A floating-point Margin structure, now with TypeConverter

4.75/5 (7 votes)
29 Mar 2007CPOL4 min read 1   500  
A Margin structure that can be used in a variety of situations, like layout, drawing and printing. Now comes with a custom TypeConverter and a usage sample.

Screenshot - Margin.png

Table of contents

Introduction

This is a structure similar to the <a href="http://msdn2.microsoft.com/en-us/library/system.windows.forms.padding.aspx">System.Windows.Forms.Padding</a> structure in .NET 2.0. It uses floating-point values however, instead of integers. I had to do some custom drawing, where floating-point numbers were required. Therefore I made this structure.

This is my first article on CP (or any programming article at all), and English is not my native language, so I keep my fingers crossed...

This works now, and I've made a sample too.

Background

Difference between margin and padding

In my opinion, margin is used 'outwards' and padding 'inwards'. This is on the same lines as in Windows Forms, where controls (like a button) have two properties. Margin 'specifies the space between this control and another control's margin'. In contrast, Padding 'specifies the interior spacing of a control'. HTML uses more or less the same idea. This structure can be used for both purposes though, just like the Padding structure.

Order of the sides

The order of the sides of a Margin is always left, top, right, bottom. This is also the same as the Padding structure, and a rectangle's FromLTRB method. It is different from CSS styles though, which uses a top, right, bottom, left order I believe. The LTRB order is used in the constructor and ToString.

Margin members

Constructors

There are several constructors available, most speak for themselves. One constructor however is less clear: new Margin(RectangleF outer, RectangleF inner). This creates a new Margin, whose sides are the difference between the outer and the inner rectangle. You get the margin of the inner rectangle relative to the outer one, or the padding of the outer rectangle relative to the inner one.

Image 2

Properties

  • float Left
  • float Top
  • float Right
  • float Bottom

    These represent each side of the Margin. They correspond to the private l, t, r, b fields.

  • float HorizontalAverage
  • float VerticalAverage
  • float Average

    Getting one of these properties returns the average of the sides. Setting a property sets the corresponding fields to the value

  • Margin Negated

    Returns a Margin whose sides are the opposite of this Margin (inverted)

    C#
    {1.4, -2, -19.5, 0}.Negated == {-1.4, 2, 19.5, 0} 

  • Margin Absolute

    Returns a Margin whose sides are all equal to or larger than zero

    C#
    {1.4, -2, -19.5, 0}.Absolute == {1.4, 2, 19.5, 0} 
  • Margin Rounded

    Returns a Margin whose sides are the rounded sides of this Margin. This performs rounding away form zero, not bankers rounding

    C#
    {1.4, -2, -19.5, 0}.Rounded == {1, -2, -20, 0} 

  • bool IsRound

    Returns true if all sides of this Margin have round values

  • bool IsEmpty

    Returns true if all sides of this Margin are equal to zero

  • bool IsNaN

    Returns true if one or more sides of this Margin are float.NaN, use this to check if this Margin is valid after some calculations

  • bool IsSymmetric

    Returns true if the Left side equals the Right side, and the Top side equals the Bottom side

  • bool AllSidesEqual

    Returns true if all four sides of this Margin have the same value

Methods for the Margin itself

I've made some common operator overloads and corresponding static methods:

  • - Negate

    This does the same as the Negated property.

    C#
    -{1.4, -2, -19.5, 0} == {-1.4, 2, 19.5, 0} 

  • + Add
  • - Subtract
  • * Multiply
  • / Divide

    These perform the operator on each side of the left hand Margin term. The right hand is overloaded for a Margin, a float or a SizeF.

    C#
    {1.4, -2, -19.5, 0} + 3.2F == {4.6, 1.2, -16.3, 3.2)
    {1.4, -2, -19.5, 0} * new SizeF(2F, 0.5F) == {2.8, -1, -39, 0}
    {1.4, -2, -19.5, 0} - {1.4, -2, -19.5, 0} == {0, 0, 0, 0}
    {1.4, -2, -19.5, 0} / 2F == {0.7, -1, 9.75, 0} 

    I've also made a Translate function (without an operator):

  • Translate This method 'moves' the Margin by a value.
    C#
    {0, 0, 0, 0}.Translate(1F, 3F) == {1, 3, -1, -3}
    {1.4, -2, -19.5, 0}.Translate(1F, 2F) == {2.4, -4, -20.5, -2}

Other methods

Now all these properties, methods and operators are nice, but some methods to use a Margin are nice to have as well. The amount of 'usability' methods is limited; it could be expanded, for points for example. But you have four kinds of points (one for each corner of a rectangle), and each of those four points has to be translated differently. This is on the to-do list, and suggestions are welcome of course!

For now I've made these methods:

  • InflateRectangle
  • DeflateRectangle
  • InflateSize
  • DeflateSize

    They modify rectangles and sizes with a Margin.

I've also overridden and overloaded the usual object methods:

  • Equals
  • GetHashCode
  • ToString

How to use

TypeConverter

After a lot of fiddling, I got the TypeConverter to work. No errors from the designer anymore, or sudden reset Margin properties.

The trick was to use the type Margin as little as possible in the MarginConverter class. I don't know why exactly, I think it has to do with the designer managing versions of the type between builds. Therefore, instead of doing this:

C#
// The current margin value
Margin margin = (Margin)value;

// The value of each side
float left = margin.Left;
float top = margin.Top;
float right = margin.Right;
float bottom = margin.Bottom;

You have to do this:

C#
// The current Margin type (from value)
Type type = value.GetType();

// The value of each side, retrieved with reflection
float left = (float)type.GetProperty("Left").GetValue(value, null);
float top = (float)type.GetProperty("Top").GetValue(value, null);
float right = (float)type.GetProperty("Right").GetValue(value, null);
float bottom = (float)type.GetProperty("Bottom").GetValue(value, null);

Anyway it works now, at least for me. If you find any bugs, please let me know.

Sample

Included is a sample project, it 'prints' a page. It fills the page with cells, you can adjust the number of columns and rows, as well as margin and padding.

Screenshot - MarginTest.png

Conclusion

So this was my little margin structure, and my first article. I hope you can find some use for it. And of course, comments are much appreciated!

History

  • 15-03-2007: Version 1.0 - Article submitted
  • 16-03-2007: No code changes. Changed the article title from 'Margin class' to 'Margin structure'. Duh...
  • 29-03-2007: Version 1.1 - Now the Margin has a (optional) MarginConverter for PropertyGrid and designers. Also added a sample.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)