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.
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)
{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
{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
{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.
-{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
.
{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.
{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:
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:
Margin margin = (Margin)value;
float left = margin.Left;
float top = margin.Top;
float right = margin.Right;
float bottom = margin.Bottom;
You have to do this:
Type type = value.GetType();
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.
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.